blob: 71f43b487214ca322e0e7098757d752c177fd98e [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 Jang61f4fbd2014-08-06 19:21:12 +090024import android.hardware.hdmi.HdmiDeviceInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +090025import android.hardware.hdmi.HdmiHotplugEvent;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090026import android.hardware.hdmi.IHdmiControlService;
Wonsik Kim187423c2014-06-25 14:12:48 +090027import android.hardware.hdmi.IHdmiDeviceEventListener;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090028import android.hardware.hdmi.IHdmiHotplugEventListener;
29import android.hardware.hdmi.IHdmiInputChangeListener;
Wonsik Kimd7c29182014-05-27 10:38:21 +090030import android.media.AudioDevicePort;
Wonsik Kimca17a902014-07-31 10:09:46 +090031import android.media.AudioFormat;
Wonsik Kim8e45a332014-08-04 10:17:18 +090032import android.media.AudioGain;
33import android.media.AudioGainConfig;
Wonsik Kimd7c29182014-05-27 10:38:21 +090034import android.media.AudioManager;
35import android.media.AudioPatch;
36import android.media.AudioPort;
37import android.media.AudioPortConfig;
Jae Seod5cc4a22014-05-30 16:57:43 -070038import android.media.tv.ITvInputHardware;
39import android.media.tv.ITvInputHardwareCallback;
Jinsuk Kimb2b31512014-07-21 17:14:44 +090040import android.media.tv.TvContract;
Jae Seo546c6352014-08-07 11:57:01 -070041import android.media.tv.TvInputHardwareInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +090042import android.media.tv.TvInputInfo;
Jae Seod5cc4a22014-05-30 16:57:43 -070043import android.media.tv.TvStreamConfig;
Wonsik Kim969167d2014-06-24 16:33:17 +090044import android.os.Handler;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000045import android.os.IBinder;
Wonsik Kim969167d2014-06-24 16:33:17 +090046import android.os.Message;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000047import android.os.RemoteException;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090048import android.os.ServiceManager;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090049import android.util.ArrayMap;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000050import android.util.Slog;
51import android.util.SparseArray;
Wonsik Kim969167d2014-06-24 16:33:17 +090052import android.util.SparseBooleanArray;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000053import android.view.KeyEvent;
54import android.view.Surface;
55
Wonsik Kim969167d2014-06-24 16:33:17 +090056import com.android.server.SystemService;
57
Wonsik Kimc22dbb62014-05-26 02:26:04 +000058import java.util.ArrayList;
Wonsik Kim8e45a332014-08-04 10:17:18 +090059import java.util.Arrays;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090060import java.util.Collections;
Wonsik Kimd922a542014-07-24 18:25:29 +090061import java.util.Iterator;
62import java.util.LinkedList;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000063import java.util.List;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090064import java.util.Map;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000065
66/**
67 * A helper class for TvInputManagerService to handle TV input hardware.
68 *
69 * This class does a basic connection management and forwarding calls to TvInputHal which eventually
70 * calls to tv_input HAL module.
71 *
72 * @hide
73 */
Jinsuk Kim7474fac2014-07-18 17:22:26 +090074class TvInputHardwareManager implements TvInputHal.Callback {
Wonsik Kimc22dbb62014-05-26 02:26:04 +000075 private static final String TAG = TvInputHardwareManager.class.getSimpleName();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090076
77 private final Context mContext;
78 private final Listener mListener;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000079 private final TvInputHal mHal = new TvInputHal(this);
Wonsik Kimd922a542014-07-24 18:25:29 +090080 private final SparseArray<Connection> mConnections = new SparseArray<>();
81 private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
Jae Seo546c6352014-08-07 11:57:01 -070082 private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090083 /* A map from a device ID to the matching TV input ID. */
Wonsik Kimd922a542014-07-24 18:25:29 +090084 private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090085 /* A map from a HDMI logical address to the matching TV input ID. */
Jae Seo546c6352014-08-07 11:57:01 -070086 private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>();
Wonsik Kimd922a542014-07-24 18:25:29 +090087 private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090088
Wonsik Kimd7c29182014-05-27 10:38:21 +090089 private final AudioManager mAudioManager;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090090 private IHdmiControlService mHdmiControlService;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090091 private final IHdmiHotplugEventListener mHdmiHotplugEventListener =
92 new HdmiHotplugEventListener();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090093 private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener();
Jinsuk Kim7474fac2014-07-18 17:22:26 +090094 private final IHdmiInputChangeListener mHdmiInputChangeListener = new HdmiInputChangeListener();
Wonsik Kimd922a542014-07-24 18:25:29 +090095 // TODO: Should handle STANDBY case.
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090096 private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
Wonsik Kimd922a542014-07-24 18:25:29 +090097 private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
Wonsik Kim969167d2014-06-24 16:33:17 +090098
Wonsik Kim187423c2014-06-25 14:12:48 +090099 // Calls to mListener should happen here.
100 private final Handler mHandler = new ListenerHandler();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000101
102 private final Object mLock = new Object();
103
Wonsik Kim187423c2014-06-25 14:12:48 +0900104 public TvInputHardwareManager(Context context, Listener listener) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000105 mContext = context;
Wonsik Kim187423c2014-06-25 14:12:48 +0900106 mListener = listener;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900107 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000108 mHal.init();
Wonsik Kim969167d2014-06-24 16:33:17 +0900109 }
110
111 public void onBootPhase(int phase) {
112 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900113 mHdmiControlService = IHdmiControlService.Stub.asInterface(ServiceManager.getService(
114 Context.HDMI_CONTROL_SERVICE));
115 if (mHdmiControlService != null) {
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900116 try {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900117 mHdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
118 mHdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
Jae Seo546c6352014-08-07 11:57:01 -0700119 mHdmiDeviceList.addAll(mHdmiControlService.getInputDevices());
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900120 mHdmiControlService.setInputChangeListener(mHdmiInputChangeListener);
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900121 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900122 Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900123 }
124 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900125 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000126 }
127
128 @Override
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900129 public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000130 synchronized (mLock) {
131 Connection connection = new Connection(info);
132 connection.updateConfigsLocked(configs);
133 mConnections.put(info.getDeviceId(), connection);
Wonsik Kimd922a542014-07-24 18:25:29 +0900134 buildHardwareListLocked();
Wonsik Kim187423c2014-06-25 14:12:48 +0900135 mHandler.obtainMessage(
136 ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget();
Wonsik Kimd922a542014-07-24 18:25:29 +0900137 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
138 processPendingHdmiDeviceEventsLocked();
139 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000140 }
141 }
142
Wonsik Kimd922a542014-07-24 18:25:29 +0900143 private void buildHardwareListLocked() {
144 mHardwareList.clear();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000145 for (int i = 0; i < mConnections.size(); ++i) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900146 mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000147 }
148 }
149
150 @Override
151 public void onDeviceUnavailable(int deviceId) {
152 synchronized (mLock) {
153 Connection connection = mConnections.get(deviceId);
154 if (connection == null) {
155 Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
156 return;
157 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900158 connection.resetLocked(null, null, null, null, null);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000159 mConnections.remove(deviceId);
Wonsik Kimd922a542014-07-24 18:25:29 +0900160 buildHardwareListLocked();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900161 TvInputHardwareInfo info = connection.getHardwareInfoLocked();
Wonsik Kimd922a542014-07-24 18:25:29 +0900162 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
Jae Seo546c6352014-08-07 11:57:01 -0700163 // Remove HDMI devices linked with this hardware.
164 for (Iterator<HdmiDeviceInfo> it = mHdmiDeviceList.iterator(); it.hasNext();) {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900165 HdmiDeviceInfo deviceInfo = it.next();
Wonsik Kimd922a542014-07-24 18:25:29 +0900166 if (deviceInfo.getPortId() == info.getHdmiPortId()) {
Jae Seo546c6352014-08-07 11:57:01 -0700167 mHandler.obtainMessage(ListenerHandler.HDMI_DEVICE_REMOVED, 0, 0,
Wonsik Kimd922a542014-07-24 18:25:29 +0900168 deviceInfo).sendToTarget();
169 it.remove();
170 }
171 }
172 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900173 mHandler.obtainMessage(
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900174 ListenerHandler.HARDWARE_DEVICE_REMOVED, 0, 0, info).sendToTarget();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000175 }
176 }
177
178 @Override
179 public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) {
180 synchronized (mLock) {
181 Connection connection = mConnections.get(deviceId);
182 if (connection == null) {
183 Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with "
184 + deviceId);
185 return;
186 }
187 connection.updateConfigsLocked(configs);
188 try {
189 connection.getCallbackLocked().onStreamConfigChanged(configs);
190 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900191 Slog.e(TAG, "error in onStreamConfigurationChanged", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000192 }
193 }
194 }
195
Terry Heoc086a3d2014-06-18 14:26:44 +0900196 @Override
197 public void onFirstFrameCaptured(int deviceId, int streamId) {
198 synchronized (mLock) {
199 Connection connection = mConnections.get(deviceId);
200 if (connection == null) {
201 Slog.e(TAG, "FirstFrameCaptured: Cannot find a connection with "
202 + deviceId);
203 return;
204 }
205 Runnable runnable = connection.getOnFirstFrameCapturedLocked();
206 if (runnable != null) {
207 runnable.run();
208 connection.setOnFirstFrameCapturedLocked(null);
209 }
210 }
211 }
212
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000213 public List<TvInputHardwareInfo> getHardwareList() {
214 synchronized (mLock) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900215 return Collections.unmodifiableList(mHardwareList);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000216 }
217 }
218
Jae Seo546c6352014-08-07 11:57:01 -0700219 public List<HdmiDeviceInfo> getHdmiDeviceList() {
Wonsik Kimd922a542014-07-24 18:25:29 +0900220 synchronized (mLock) {
Jae Seo546c6352014-08-07 11:57:01 -0700221 return Collections.unmodifiableList(mHdmiDeviceList);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900222 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900223 }
224
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900225 private boolean checkUidChangedLocked(
226 Connection connection, int callingUid, int resolvedUserId) {
227 Integer connectionCallingUid = connection.getCallingUidLocked();
228 Integer connectionResolvedUserId = connection.getResolvedUserIdLocked();
229 if (connectionCallingUid == null || connectionResolvedUserId == null) {
230 return true;
231 }
232 if (connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId) {
233 return true;
234 }
235 return false;
236 }
237
Wonsik Kim969167d2014-06-24 16:33:17 +0900238 private int convertConnectedToState(boolean connected) {
239 if (connected) {
240 return INPUT_STATE_CONNECTED;
241 } else {
242 return INPUT_STATE_DISCONNECTED;
243 }
244 }
245
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900246 public void addHardwareTvInput(int deviceId, TvInputInfo info) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900247 synchronized (mLock) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900248 String oldInputId = mHardwareInputIdMap.get(deviceId);
249 if (oldInputId != null) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900250 Slog.w(TAG, "Trying to override previous registration: old = "
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900251 + mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
Wonsik Kim969167d2014-06-24 16:33:17 +0900252 + info + ":" + deviceId);
253 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900254 mHardwareInputIdMap.put(deviceId, info.getId());
255 mInputMap.put(info.getId(), info);
Wonsik Kim969167d2014-06-24 16:33:17 +0900256
257 for (int i = 0; i < mHdmiStateMap.size(); ++i) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900258 TvInputHardwareInfo hardwareInfo =
259 findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i));
260 if (hardwareInfo == null) {
261 continue;
262 }
263 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Wonsik Kim969167d2014-06-24 16:33:17 +0900264 if (inputId != null && inputId.equals(info.getId())) {
Wonsik Kim187423c2014-06-25 14:12:48 +0900265 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
Wonsik Kim969167d2014-06-24 16:33:17 +0900266 convertConnectedToState(mHdmiStateMap.valueAt(i)), 0,
267 inputId).sendToTarget();
268 }
269 }
270 }
271 }
272
Wonsik Kim38feae92014-07-21 21:35:50 +0900273 private static <T> int indexOfEqualValue(SparseArray<T> map, T value) {
274 for (int i = 0; i < map.size(); ++i) {
275 if (map.valueAt(i).equals(value)) {
276 return i;
277 }
278 }
279 return -1;
280 }
281
Jae Seo546c6352014-08-07 11:57:01 -0700282 public void addHdmiTvInput(int logicalAddress, TvInputInfo info) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900283 if (info.getType() != TvInputInfo.TYPE_HDMI) {
284 throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
285 }
286 synchronized (mLock) {
287 String parentId = info.getParentId();
Wonsik Kim38feae92014-07-21 21:35:50 +0900288 int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId);
289 if (parentIndex < 0) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900290 throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
291 }
Jae Seo546c6352014-08-07 11:57:01 -0700292 String oldInputId = mHdmiInputIdMap.get(logicalAddress);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900293 if (oldInputId != null) {
294 Slog.w(TAG, "Trying to override previous registration: old = "
295 + mInputMap.get(oldInputId) + ":" + logicalAddress + ", new = "
296 + info + ":" + logicalAddress);
297 }
Jae Seo546c6352014-08-07 11:57:01 -0700298 mHdmiInputIdMap.put(logicalAddress, info.getId());
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900299 mInputMap.put(info.getId(), info);
300 }
301 }
302
303 public void removeTvInput(String inputId) {
304 synchronized (mLock) {
305 mInputMap.remove(inputId);
Wonsik Kim38feae92014-07-21 21:35:50 +0900306 int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900307 if (hardwareIndex >= 0) {
308 mHardwareInputIdMap.removeAt(hardwareIndex);
309 }
Jae Seo546c6352014-08-07 11:57:01 -0700310 int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId);
311 if (deviceIndex >= 0) {
312 mHdmiInputIdMap.removeAt(deviceIndex);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900313 }
314 }
315 }
316
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000317 /**
318 * Create a TvInputHardware object with a specific deviceId. One service at a time can access
319 * the object, and if more than one process attempts to create hardware with the same deviceId,
320 * the latest service will get the object and all the other hardware are released. The
321 * release is notified via ITvInputHardwareCallback.onReleased().
322 */
323 public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
Wonsik Kim969167d2014-06-24 16:33:17 +0900324 TvInputInfo info, int callingUid, int resolvedUserId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000325 if (callback == null) {
326 throw new NullPointerException();
327 }
328 synchronized (mLock) {
329 Connection connection = mConnections.get(deviceId);
330 if (connection == null) {
331 Slog.e(TAG, "Invalid deviceId : " + deviceId);
332 return null;
333 }
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900334 if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900335 TvInputHardwareImpl hardware =
336 new TvInputHardwareImpl(connection.getHardwareInfoLocked());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000337 try {
338 callback.asBinder().linkToDeath(connection, 0);
339 } catch (RemoteException e) {
340 hardware.release();
341 return null;
342 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900343 connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000344 }
345 return connection.getHardwareLocked();
346 }
347 }
348
349 /**
350 * Release the specified hardware.
351 */
352 public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid,
353 int resolvedUserId) {
354 synchronized (mLock) {
355 Connection connection = mConnections.get(deviceId);
356 if (connection == null) {
357 Slog.e(TAG, "Invalid deviceId : " + deviceId);
358 return;
359 }
360 if (connection.getHardwareLocked() != hardware
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900361 || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000362 return;
363 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900364 connection.resetLocked(null, null, null, null, null);
365 }
366 }
367
Wonsik Kimd922a542014-07-24 18:25:29 +0900368 private TvInputHardwareInfo findHardwareInfoForHdmiPortLocked(int port) {
369 for (TvInputHardwareInfo hardwareInfo : mHardwareList) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900370 if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
371 && hardwareInfo.getHdmiPortId() == port) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900372 return hardwareInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +0900373 }
374 }
375 return null;
376 }
377
Terry Heoc086a3d2014-06-18 14:26:44 +0900378 private int findDeviceIdForInputIdLocked(String inputId) {
379 for (int i = 0; i < mConnections.size(); ++i) {
380 Connection connection = mConnections.get(i);
381 if (connection.getInfoLocked().getId().equals(inputId)) {
382 return i;
383 }
384 }
385 return -1;
386 }
387
388 /**
389 * Get the list of TvStreamConfig which is buffered mode.
390 */
391 public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid,
392 int resolvedUserId) {
393 List<TvStreamConfig> configsList = new ArrayList<TvStreamConfig>();
394 synchronized (mLock) {
395 int deviceId = findDeviceIdForInputIdLocked(inputId);
396 if (deviceId < 0) {
397 Slog.e(TAG, "Invalid inputId : " + inputId);
398 return configsList;
399 }
400 Connection connection = mConnections.get(deviceId);
401 for (TvStreamConfig config : connection.getConfigsLocked()) {
402 if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
403 configsList.add(config);
404 }
405 }
406 }
407 return configsList;
408 }
409
410 /**
411 * Take a snapshot of the given TV input into the provided Surface.
412 */
413 public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config,
414 int callingUid, int resolvedUserId) {
415 synchronized (mLock) {
416 int deviceId = findDeviceIdForInputIdLocked(inputId);
417 if (deviceId < 0) {
418 Slog.e(TAG, "Invalid inputId : " + inputId);
419 return false;
420 }
421 Connection connection = mConnections.get(deviceId);
422 final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
423 if (hardwareImpl != null) {
424 // Stop previous capture.
425 Runnable runnable = connection.getOnFirstFrameCapturedLocked();
426 if (runnable != null) {
427 runnable.run();
428 connection.setOnFirstFrameCapturedLocked(null);
429 }
430
431 boolean result = hardwareImpl.startCapture(surface, config);
432 if (result) {
433 connection.setOnFirstFrameCapturedLocked(new Runnable() {
434 @Override
435 public void run() {
436 hardwareImpl.stopCapture(config);
437 }
438 });
439 }
440 return result;
441 }
442 }
443 return false;
444 }
445
Wonsik Kimd922a542014-07-24 18:25:29 +0900446 private void processPendingHdmiDeviceEventsLocked() {
447 for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) {
448 Message msg = it.next();
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900449 HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
Wonsik Kimd922a542014-07-24 18:25:29 +0900450 TvInputHardwareInfo hardwareInfo =
451 findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId());
452 if (hardwareInfo != null) {
453 msg.sendToTarget();
454 it.remove();
455 }
456 }
457 }
458
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000459 private class Connection implements IBinder.DeathRecipient {
Wonsik Kim969167d2014-06-24 16:33:17 +0900460 private final TvInputHardwareInfo mHardwareInfo;
461 private TvInputInfo mInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000462 private TvInputHardwareImpl mHardware = null;
463 private ITvInputHardwareCallback mCallback;
464 private TvStreamConfig[] mConfigs = null;
465 private Integer mCallingUid = null;
466 private Integer mResolvedUserId = null;
Terry Heoc086a3d2014-06-18 14:26:44 +0900467 private Runnable mOnFirstFrameCaptured;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000468
Wonsik Kim969167d2014-06-24 16:33:17 +0900469 public Connection(TvInputHardwareInfo hardwareInfo) {
470 mHardwareInfo = hardwareInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000471 }
472
473 // *Locked methods assume TvInputHardwareManager.mLock is held.
474
Wonsik Kim969167d2014-06-24 16:33:17 +0900475 public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
476 TvInputInfo info, Integer callingUid, Integer resolvedUserId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000477 if (mHardware != null) {
478 try {
479 mCallback.onReleased();
480 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900481 Slog.e(TAG, "error in Connection::resetLocked", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000482 }
483 mHardware.release();
484 }
485 mHardware = hardware;
486 mCallback = callback;
Wonsik Kim969167d2014-06-24 16:33:17 +0900487 mInfo = info;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000488 mCallingUid = callingUid;
489 mResolvedUserId = resolvedUserId;
Terry Heoc086a3d2014-06-18 14:26:44 +0900490 mOnFirstFrameCaptured = null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000491
492 if (mHardware != null && mCallback != null) {
493 try {
494 mCallback.onStreamConfigChanged(getConfigsLocked());
495 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900496 Slog.e(TAG, "error in Connection::resetLocked", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000497 }
498 }
499 }
500
501 public void updateConfigsLocked(TvStreamConfig[] configs) {
502 mConfigs = configs;
503 }
504
Wonsik Kim969167d2014-06-24 16:33:17 +0900505 public TvInputHardwareInfo getHardwareInfoLocked() {
506 return mHardwareInfo;
507 }
508
509 public TvInputInfo getInfoLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000510 return mInfo;
511 }
512
513 public ITvInputHardware getHardwareLocked() {
514 return mHardware;
515 }
516
Terry Heoc086a3d2014-06-18 14:26:44 +0900517 public TvInputHardwareImpl getHardwareImplLocked() {
518 return mHardware;
519 }
520
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000521 public ITvInputHardwareCallback getCallbackLocked() {
522 return mCallback;
523 }
524
525 public TvStreamConfig[] getConfigsLocked() {
526 return mConfigs;
527 }
528
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900529 public Integer getCallingUidLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000530 return mCallingUid;
531 }
532
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900533 public Integer getResolvedUserIdLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000534 return mResolvedUserId;
535 }
536
Terry Heoc086a3d2014-06-18 14:26:44 +0900537 public void setOnFirstFrameCapturedLocked(Runnable runnable) {
538 mOnFirstFrameCaptured = runnable;
539 }
540
541 public Runnable getOnFirstFrameCapturedLocked() {
542 return mOnFirstFrameCaptured;
543 }
544
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000545 @Override
546 public void binderDied() {
547 synchronized (mLock) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900548 resetLocked(null, null, null, null, null);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000549 }
550 }
551 }
552
553 private class TvInputHardwareImpl extends ITvInputHardware.Stub {
554 private final TvInputHardwareInfo mInfo;
555 private boolean mReleased = false;
556 private final Object mImplLock = new Object();
557
Wonsik Kim06c41a32014-08-04 18:57:47 +0900558 private final AudioManager.OnAudioPortUpdateListener mAudioListener =
559 new AudioManager.OnAudioPortUpdateListener() {
560 @Override
561 public void onAudioPortListUpdate(AudioPort[] portList) {
562 synchronized (mImplLock) {
563 updateAudioSinkLocked();
564 if (mInfo.getAudioType() != AudioManager.DEVICE_NONE && mAudioSource == null) {
565 mAudioSource = findAudioDevicePort(mInfo.getAudioType(),
566 mInfo.getAudioAddress());
567 if (mActiveConfig != null) {
568 updateAudioPatchLocked();
569 }
570 }
571 }
572 }
573
574 @Override
575 public void onAudioPatchListUpdate(AudioPatch[] patchList) {
576 // No-op
577 }
578
579 @Override
580 public void onServiceDied() {
581 synchronized (mImplLock) {
582 mAudioSource = null;
583 mAudioSink = null;
584 mAudioPatch = null;
585 }
586 }
587 };
588 private int mOverrideAudioType = AudioManager.DEVICE_NONE;
589 private String mOverrideAudioAddress = "";
590 private AudioDevicePort mAudioSource;
Wonsik Kimca17a902014-07-31 10:09:46 +0900591 private AudioDevicePort mAudioSink;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900592 private AudioPatch mAudioPatch = null;
Wonsik Kim8e45a332014-08-04 10:17:18 +0900593 private float mCommittedVolume = 0.0f;
594 private float mVolume = 0.0f;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900595
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900596 private TvStreamConfig mActiveConfig = null;
597
Wonsik Kimca17a902014-07-31 10:09:46 +0900598 private int mDesiredSamplingRate = 0;
599 private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
600 private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT;
601
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000602 public TvInputHardwareImpl(TvInputHardwareInfo info) {
603 mInfo = info;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900604 mAudioManager.registerAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900605 if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900606 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
Wonsik Kimca17a902014-07-31 10:09:46 +0900607 mAudioSink = findAudioSinkFromAudioPolicy();
608 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900609 }
610
611 private AudioDevicePort findAudioSinkFromAudioPolicy() {
612 ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
613 if (mAudioManager.listAudioDevicePorts(devicePorts) == AudioManager.SUCCESS) {
614 int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
615 for (AudioPort port : devicePorts) {
616 AudioDevicePort devicePort = (AudioDevicePort) port;
617 if (devicePort.type() == sinkDevice) {
618 return devicePort;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900619 }
620 }
621 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900622 return null;
623 }
624
625 private AudioDevicePort findAudioDevicePort(int type, String address) {
626 if (type == AudioManager.DEVICE_NONE) {
627 return null;
628 }
629 ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
630 if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
631 return null;
632 }
633 for (AudioPort port : devicePorts) {
634 AudioDevicePort devicePort = (AudioDevicePort) port;
635 if (devicePort.type() == type && devicePort.address().equals(address)) {
636 return devicePort;
637 }
638 }
639 return null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000640 }
641
642 public void release() {
643 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900644 mAudioManager.unregisterAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900645 if (mAudioPatch != null) {
646 mAudioManager.releaseAudioPatch(mAudioPatch);
647 mAudioPatch = null;
648 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000649 mReleased = true;
650 }
651 }
652
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900653 // A TvInputHardwareImpl object holds only one active session. Therefore, if a client
654 // attempts to call setSurface with different TvStreamConfig objects, the last call will
655 // prevail.
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000656 @Override
657 public boolean setSurface(Surface surface, TvStreamConfig config)
658 throws RemoteException {
659 synchronized (mImplLock) {
660 if (mReleased) {
661 throw new IllegalStateException("Device already released.");
662 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900663 if (surface != null && config == null) {
664 return false;
665 }
666 if (surface == null && mActiveConfig == null) {
667 return false;
668 }
Wonsik Kimd7c29182014-05-27 10:38:21 +0900669 if (mAudioSource != null && mAudioSink != null) {
670 if (surface != null) {
Wonsik Kimca17a902014-07-31 10:09:46 +0900671 updateAudioPatchLocked();
Wonsik Kimd7c29182014-05-27 10:38:21 +0900672 } else {
673 mAudioManager.releaseAudioPatch(mAudioPatch);
674 mAudioPatch = null;
675 }
676 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900677 int result = TvInputHal.ERROR_UNKNOWN;
678 if (surface == null) {
679 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
680 mActiveConfig = null;
681 } else {
682 if (config != mActiveConfig && mActiveConfig != null) {
683 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
684 if (result != TvInputHal.SUCCESS) {
685 mActiveConfig = null;
686 return false;
687 }
688 }
689 result = mHal.addStream(mInfo.getDeviceId(), surface, config);
690 if (result == TvInputHal.SUCCESS) {
691 mActiveConfig = config;
692 }
693 }
694 return result == TvInputHal.SUCCESS;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000695 }
696 }
697
Wonsik Kimca17a902014-07-31 10:09:46 +0900698 private void updateAudioPatchLocked() {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900699 if (mAudioSource == null || mAudioSink == null) {
700 if (mAudioPatch != null) {
701 throw new IllegalStateException("Audio patch should be null if audio source "
702 + "or sink is null.");
703 }
704 return;
705 }
706
Wonsik Kim8e45a332014-08-04 10:17:18 +0900707 AudioGainConfig sourceGainConfig = null;
708 if (mAudioSource.gains().length > 0 && mVolume != mCommittedVolume) {
709 AudioGain sourceGain = null;
710 for (AudioGain gain : mAudioSource.gains()) {
711 if ((gain.mode() & AudioGain.MODE_JOINT) != 0) {
712 sourceGain = gain;
713 break;
714 }
715 }
716 // NOTE: we only change the source gain in MODE_JOINT here.
717 if (sourceGain != null) {
718 int steps = (sourceGain.maxValue() - sourceGain.minValue())
719 / sourceGain.stepValue();
720 int gainValue = sourceGain.minValue();
721 if (mVolume < 1.0f) {
722 gainValue += sourceGain.stepValue() * (int) (mVolume * steps + 0.5);
723 } else {
724 gainValue = sourceGain.maxValue();
725 }
726 int numChannels = 0;
727 for (int mask = sourceGain.channelMask(); mask > 0; mask >>= 1) {
728 numChannels += (mask & 1);
729 }
730 int[] gainValues = new int[numChannels];
731 Arrays.fill(gainValues, gainValue);
732 sourceGainConfig = sourceGain.buildConfig(AudioGain.MODE_JOINT,
733 sourceGain.channelMask(), gainValues, 0);
734 } else {
735 Slog.w(TAG, "No audio source gain with MODE_JOINT support exists.");
736 }
737 }
738
Wonsik Kimca17a902014-07-31 10:09:46 +0900739 AudioPortConfig sourceConfig = mAudioSource.activeConfig();
740 AudioPortConfig sinkConfig = mAudioSink.activeConfig();
741 AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
Wonsik Kim8e45a332014-08-04 10:17:18 +0900742 boolean shouldRecreateAudioPatch = false;
Wonsik Kimca17a902014-07-31 10:09:46 +0900743 if (sinkConfig == null
744 || (mDesiredSamplingRate != 0
745 && sinkConfig.samplingRate() != mDesiredSamplingRate)
746 || (mDesiredChannelMask != AudioFormat.CHANNEL_OUT_DEFAULT
747 && sinkConfig.channelMask() != mDesiredChannelMask)
748 || (mDesiredFormat != AudioFormat.ENCODING_DEFAULT
749 && sinkConfig.format() != mDesiredFormat)) {
750 sinkConfig = mAudioSource.buildConfig(mDesiredSamplingRate, mDesiredChannelMask,
751 mDesiredFormat, null);
Wonsik Kim8e45a332014-08-04 10:17:18 +0900752 shouldRecreateAudioPatch = true;
Wonsik Kimca17a902014-07-31 10:09:46 +0900753 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900754 if (sourceConfig == null || sourceGainConfig != null) {
Wonsik Kimca17a902014-07-31 10:09:46 +0900755 sourceConfig = mAudioSource.buildConfig(sinkConfig.samplingRate(),
Wonsik Kim8e45a332014-08-04 10:17:18 +0900756 sinkConfig.channelMask(), sinkConfig.format(), sourceGainConfig);
757 shouldRecreateAudioPatch = true;
Wonsik Kimca17a902014-07-31 10:09:46 +0900758 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900759 if (shouldRecreateAudioPatch) {
760 mCommittedVolume = mVolume;
761 mAudioManager.createAudioPatch(
762 audioPatchArray,
763 new AudioPortConfig[] { sourceConfig },
764 new AudioPortConfig[] { sinkConfig });
765 mAudioPatch = audioPatchArray[0];
766 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900767 }
768
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000769 @Override
Wonsik Kim8e45a332014-08-04 10:17:18 +0900770 public void setStreamVolume(float volume) throws RemoteException {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000771 synchronized (mImplLock) {
772 if (mReleased) {
773 throw new IllegalStateException("Device already released.");
774 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900775 mVolume = volume;
776 updateAudioPatchLocked();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000777 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000778 }
779
780 @Override
781 public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
782 synchronized (mImplLock) {
783 if (mReleased) {
784 throw new IllegalStateException("Device already released.");
785 }
786 }
Wonsik Kim610ccd92014-07-19 18:49:49 +0900787 if (mInfo.getType() != TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000788 return false;
789 }
790 // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
791 return false;
792 }
Terry Heoc086a3d2014-06-18 14:26:44 +0900793
794 private boolean startCapture(Surface surface, TvStreamConfig config) {
795 synchronized (mImplLock) {
796 if (mReleased) {
797 return false;
798 }
799 if (surface == null || config == null) {
800 return false;
801 }
802 if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
803 return false;
804 }
805
806 int result = mHal.addStream(mInfo.getDeviceId(), surface, config);
807 return result == TvInputHal.SUCCESS;
808 }
809 }
810
811 private boolean stopCapture(TvStreamConfig config) {
812 synchronized (mImplLock) {
813 if (mReleased) {
814 return false;
815 }
816 if (config == null) {
817 return false;
818 }
819
820 int result = mHal.removeStream(mInfo.getDeviceId(), config);
821 return result == TvInputHal.SUCCESS;
822 }
823 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900824
Wonsik Kim06c41a32014-08-04 18:57:47 +0900825 private void updateAudioSinkLocked() {
826 if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
827 return;
828 }
829 if (mOverrideAudioType == AudioManager.DEVICE_NONE) {
830 mAudioSink = findAudioSinkFromAudioPolicy();
831 } else {
832 AudioDevicePort audioSink =
833 findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress);
834 if (audioSink != null) {
835 mAudioSink = audioSink;
836 }
837 }
838 }
839
Wonsik Kimca17a902014-07-31 10:09:46 +0900840 @Override
841 public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
842 int channelMask, int format) {
843 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900844 mOverrideAudioType = audioType;
845 mOverrideAudioAddress = audioAddress;
846 updateAudioSinkLocked();
847
Wonsik Kimca17a902014-07-31 10:09:46 +0900848 mDesiredSamplingRate = samplingRate;
849 mDesiredChannelMask = channelMask;
850 mDesiredFormat = format;
851
852 if (mAudioPatch != null) {
853 updateAudioPatchLocked();
854 }
855 }
856 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000857 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900858
Wonsik Kim187423c2014-06-25 14:12:48 +0900859 interface Listener {
860 public void onStateChanged(String inputId, int state);
861 public void onHardwareDeviceAdded(TvInputHardwareInfo info);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900862 public void onHardwareDeviceRemoved(TvInputHardwareInfo info);
Jae Seo546c6352014-08-07 11:57:01 -0700863 public void onHdmiDeviceAdded(HdmiDeviceInfo device);
864 public void onHdmiDeviceRemoved(HdmiDeviceInfo device);
Wonsik Kim187423c2014-06-25 14:12:48 +0900865 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900866
Wonsik Kim187423c2014-06-25 14:12:48 +0900867 private class ListenerHandler extends Handler {
868 private static final int STATE_CHANGED = 1;
869 private static final int HARDWARE_DEVICE_ADDED = 2;
870 private static final int HARDWARE_DEVICE_REMOVED = 3;
Jae Seo546c6352014-08-07 11:57:01 -0700871 private static final int HDMI_DEVICE_ADDED = 4;
872 private static final int HDMI_DEVICE_REMOVED = 5;
Wonsik Kim969167d2014-06-24 16:33:17 +0900873
874 @Override
875 public final void handleMessage(Message msg) {
876 switch (msg.what) {
Wonsik Kim187423c2014-06-25 14:12:48 +0900877 case STATE_CHANGED: {
Wonsik Kim969167d2014-06-24 16:33:17 +0900878 String inputId = (String) msg.obj;
879 int state = msg.arg1;
Wonsik Kim187423c2014-06-25 14:12:48 +0900880 mListener.onStateChanged(inputId, state);
881 break;
882 }
883 case HARDWARE_DEVICE_ADDED: {
884 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
885 mListener.onHardwareDeviceAdded(info);
886 break;
887 }
888 case HARDWARE_DEVICE_REMOVED: {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900889 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
890 mListener.onHardwareDeviceRemoved(info);
Wonsik Kim187423c2014-06-25 14:12:48 +0900891 break;
892 }
Jae Seo546c6352014-08-07 11:57:01 -0700893 case HDMI_DEVICE_ADDED: {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900894 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo546c6352014-08-07 11:57:01 -0700895 mListener.onHdmiDeviceAdded(info);
Wonsik Kim187423c2014-06-25 14:12:48 +0900896 break;
897 }
Jae Seo546c6352014-08-07 11:57:01 -0700898 case HDMI_DEVICE_REMOVED: {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900899 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo546c6352014-08-07 11:57:01 -0700900 mListener.onHdmiDeviceRemoved(info);
Wonsik Kim969167d2014-06-24 16:33:17 +0900901 break;
902 }
903 default: {
904 Slog.w(TAG, "Unhandled message: " + msg);
905 break;
906 }
907 }
908 }
909 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900910
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900911 // Listener implementations for HdmiControlService
912
913 private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub {
914 @Override
915 public void onReceived(HdmiHotplugEvent event) {
916 synchronized (mLock) {
917 mHdmiStateMap.put(event.getPort(), event.isConnected());
Wonsik Kimd922a542014-07-24 18:25:29 +0900918 TvInputHardwareInfo hardwareInfo =
919 findHardwareInfoForHdmiPortLocked(event.getPort());
920 if (hardwareInfo == null) {
921 return;
922 }
923 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900924 if (inputId == null) {
925 return;
926 }
927 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
928 convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
929 }
930 }
931 }
932
Wonsik Kim187423c2014-06-25 14:12:48 +0900933 private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
934 @Override
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900935 public void onStatusChanged(HdmiDeviceInfo deviceInfo, boolean activated) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900936 synchronized (mLock) {
937 if (activated) {
Jae Seo546c6352014-08-07 11:57:01 -0700938 if (!mHdmiDeviceList.contains(deviceInfo)) {
939 mHdmiDeviceList.add(deviceInfo);
Wonsik Kimd922a542014-07-24 18:25:29 +0900940 } else {
941 Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
942 return;
943 }
944 } else {
Jae Seo546c6352014-08-07 11:57:01 -0700945 if (!mHdmiDeviceList.remove(deviceInfo)) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900946 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
947 return;
948 }
949 }
950 Message msg = mHandler.obtainMessage(
Jae Seo546c6352014-08-07 11:57:01 -0700951 activated ? ListenerHandler.HDMI_DEVICE_ADDED
952 : ListenerHandler.HDMI_DEVICE_REMOVED,
Wonsik Kimd922a542014-07-24 18:25:29 +0900953 0, 0, deviceInfo);
954 if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
955 msg.sendToTarget();
956 } else {
957 mPendingHdmiDeviceEvents.add(msg);
958 }
959 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900960 }
961 }
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900962
963 private final class HdmiInputChangeListener extends IHdmiInputChangeListener.Stub {
964 @Override
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900965 public void onChanged(HdmiDeviceInfo device) throws RemoteException {
Jinsuk Kimb2b31512014-07-21 17:14:44 +0900966 String inputId;
967 synchronized (mLock) {
968 if (device.isCecDevice()) {
Jae Seo546c6352014-08-07 11:57:01 -0700969 inputId = mHdmiInputIdMap.get(device.getLogicalAddress());
Jinsuk Kimb2b31512014-07-21 17:14:44 +0900970 } else {
Wonsik Kimd922a542014-07-24 18:25:29 +0900971 TvInputHardwareInfo hardwareInfo =
972 findHardwareInfoForHdmiPortLocked(device.getPortId());
973 inputId = (hardwareInfo == null) ? null
974 : mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Jinsuk Kimb2b31512014-07-21 17:14:44 +0900975 }
976 }
977 if (inputId != null) {
978 Intent intent = new Intent(Intent.ACTION_VIEW);
979 intent.setData(TvContract.buildChannelUriForPassthroughTvInput(inputId));
Jinsuk Kim58500f42014-08-05 12:48:35 +0900980 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Jinsuk Kimb2b31512014-07-21 17:14:44 +0900981 mContext.startActivity(intent);
982 } else {
983 Slog.w(TAG, "onChanged: InputId cannot be found for :" + device);
984 }
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900985 }
986 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000987}