TvInputHardwareManager.java revision 8960d1b1552729e3dfd33deee951ac75933ad8e5
1c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim/*
2c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * Copyright (C) 2014 The Android Open Source Project
3c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
4c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * Licensed under the Apache License, Version 2.0 (the "License");
5c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * you may not use this file except in compliance with the License.
6c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * You may obtain a copy of the License at
7c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
8c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *      http://www.apache.org/licenses/LICENSE-2.0
9c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
10c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * Unless required by applicable law or agreed to in writing, software
11c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * distributed under the License is distributed on an "AS IS" BASIS,
12c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * See the License for the specific language governing permissions and
14c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * limitations under the License.
15c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim */
16c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
17c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimpackage com.android.server.tv;
18c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
19969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
20969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
21969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
22c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.content.Context;
23b2b3151a42bd563669b222b82efb64e294dc9049Jinsuk Kimimport android.content.Intent;
2461daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jangimport android.hardware.hdmi.HdmiControlManager;
2561f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jangimport android.hardware.hdmi.HdmiDeviceInfo;
26969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.hardware.hdmi.HdmiHotplugEvent;
277474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.hardware.hdmi.IHdmiControlService;
28187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kimimport android.hardware.hdmi.IHdmiDeviceEventListener;
297474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.hardware.hdmi.IHdmiHotplugEventListener;
307474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.hardware.hdmi.IHdmiInputChangeListener;
311f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jangimport android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
32d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioDevicePort;
33ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kimimport android.media.AudioFormat;
348e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kimimport android.media.AudioGain;
358e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kimimport android.media.AudioGainConfig;
36d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioManager;
37d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioPatch;
38d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioPort;
39d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioPortConfig;
40d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardware;
41d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardwareCallback;
42b2b3151a42bd563669b222b82efb64e294dc9049Jinsuk Kimimport android.media.tv.TvContract;
43546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seoimport android.media.tv.TvInputHardwareInfo;
44969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.media.tv.TvInputInfo;
45d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvStreamConfig;
46969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.os.Handler;
47c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.os.IBinder;
48969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.os.Message;
49c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.os.RemoteException;
507474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.os.ServiceManager;
514f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Leeimport android.util.ArrayMap;
52c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.util.Slog;
53c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.util.SparseArray;
54969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.util.SparseBooleanArray;
55c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.view.KeyEvent;
56c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.view.Surface;
57c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
58969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport com.android.server.SystemService;
59969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
60c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport java.util.ArrayList;
618e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kimimport java.util.Arrays;
624f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Leeimport java.util.Collections;
63d922a546b94119217fb790113d0001cad0432060Wonsik Kimimport java.util.Iterator;
64d922a546b94119217fb790113d0001cad0432060Wonsik Kimimport java.util.LinkedList;
65c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport java.util.List;
664f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Leeimport java.util.Map;
67c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
68c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim/**
69c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * A helper class for TvInputManagerService to handle TV input hardware.
70c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
71c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * This class does a basic connection management and forwarding calls to TvInputHal which eventually
72c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * calls to tv_input HAL module.
73c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
74c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * @hide
75c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim */
767474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimclass TvInputHardwareManager implements TvInputHal.Callback {
77c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private static final String TAG = TvInputHardwareManager.class.getSimpleName();
784f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
794f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final Context mContext;
804f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final Listener mListener;
81c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private final TvInputHal mHal = new TvInputHal(this);
82d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final SparseArray<Connection> mConnections = new SparseArray<>();
83d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
84546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo    private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>();
854f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    /* A map from a device ID to the matching TV input ID. */
86d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
874f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    /* A map from a HDMI logical address to the matching TV input ID. */
88546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo    private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>();
89d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>();
904f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
91d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim    private final AudioManager mAudioManager;
924f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private IHdmiControlService mHdmiControlService;
937474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    private final IHdmiHotplugEventListener mHdmiHotplugEventListener =
947474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            new HdmiHotplugEventListener();
954f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener();
961f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang    private final IHdmiSystemAudioModeChangeListener mHdmiSystemAudioModeChangeListener =
971f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            new HdmiSystemAudioModeChangeListener();
98d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim
99d922a546b94119217fb790113d0001cad0432060Wonsik Kim    // TODO: Should handle STANDBY case.
1004f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
101d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
102969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
103187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    // Calls to mListener should happen here.
104187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    private final Handler mHandler = new ListenerHandler();
105c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
106c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private final Object mLock = new Object();
107c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
108187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    public TvInputHardwareManager(Context context, Listener listener) {
109c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        mContext = context;
110187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        mListener = listener;
111d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
112c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        mHal.init();
113969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
114969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
115969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    public void onBootPhase(int phase) {
116969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1174f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mHdmiControlService = IHdmiControlService.Stub.asInterface(ServiceManager.getService(
1184f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Context.HDMI_CONTROL_SERVICE));
1194f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (mHdmiControlService != null) {
1207474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                try {
1214f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    mHdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
1224f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    mHdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
1231f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                    mHdmiControlService.addSystemAudioModeChangeListener(
1241f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                            mHdmiSystemAudioModeChangeListener);
125546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    mHdmiDeviceList.addAll(mHdmiControlService.getInputDevices());
1267474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                } catch (RemoteException e) {
1274f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
1287474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                }
1297474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            }
130969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
131c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
132c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
133c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    @Override
1344f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) {
135c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
136c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = new Connection(info);
137c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            connection.updateConfigsLocked(configs);
138c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mConnections.put(info.getDeviceId(), connection);
139d922a546b94119217fb790113d0001cad0432060Wonsik Kim            buildHardwareListLocked();
140187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            mHandler.obtainMessage(
141187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget();
142d922a546b94119217fb790113d0001cad0432060Wonsik Kim            if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
143d922a546b94119217fb790113d0001cad0432060Wonsik Kim                processPendingHdmiDeviceEventsLocked();
144d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
145c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
146c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
147c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
148d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private void buildHardwareListLocked() {
149d922a546b94119217fb790113d0001cad0432060Wonsik Kim        mHardwareList.clear();
150c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        for (int i = 0; i < mConnections.size(); ++i) {
151d922a546b94119217fb790113d0001cad0432060Wonsik Kim            mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked());
152c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
153c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
154c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
155c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    @Override
156c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public void onDeviceUnavailable(int deviceId) {
157c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
158c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
159c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
160c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
161c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
162c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
163969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            connection.resetLocked(null, null, null, null, null);
164c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mConnections.remove(deviceId);
165d922a546b94119217fb790113d0001cad0432060Wonsik Kim            buildHardwareListLocked();
1664f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            TvInputHardwareInfo info = connection.getHardwareInfoLocked();
167d922a546b94119217fb790113d0001cad0432060Wonsik Kim            if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
168546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                // Remove HDMI devices linked with this hardware.
169546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                for (Iterator<HdmiDeviceInfo> it = mHdmiDeviceList.iterator(); it.hasNext();) {
17061f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                    HdmiDeviceInfo deviceInfo = it.next();
171d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    if (deviceInfo.getPortId() == info.getHdmiPortId()) {
172546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                        mHandler.obtainMessage(ListenerHandler.HDMI_DEVICE_REMOVED, 0, 0,
173d922a546b94119217fb790113d0001cad0432060Wonsik Kim                                deviceInfo).sendToTarget();
174d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        it.remove();
175d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    }
176d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
177d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
178187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            mHandler.obtainMessage(
1794f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    ListenerHandler.HARDWARE_DEVICE_REMOVED, 0, 0, info).sendToTarget();
180c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
181c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
182c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
183c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    @Override
184c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) {
185c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
186c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
187c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
188c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with "
189c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                        + deviceId);
190c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
191c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
192c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            connection.updateConfigsLocked(configs);
193c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
194c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                connection.getCallbackLocked().onStreamConfigChanged(configs);
195c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } catch (RemoteException e) {
1964f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                Slog.e(TAG, "error in onStreamConfigurationChanged", e);
197c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
198c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
199c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
200c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
201c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    @Override
202c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    public void onFirstFrameCaptured(int deviceId, int streamId) {
203c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        synchronized (mLock) {
204c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(deviceId);
205c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (connection == null) {
206c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Slog.e(TAG, "FirstFrameCaptured: Cannot find a connection with "
207c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        + deviceId);
208c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return;
209c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
210c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Runnable runnable = connection.getOnFirstFrameCapturedLocked();
211c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (runnable != null) {
212c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                runnable.run();
213c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                connection.setOnFirstFrameCapturedLocked(null);
214c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
215c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
216c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
217c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
218c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public List<TvInputHardwareInfo> getHardwareList() {
219c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
220d922a546b94119217fb790113d0001cad0432060Wonsik Kim            return Collections.unmodifiableList(mHardwareList);
221c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
222c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
223c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
224546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo    public List<HdmiDeviceInfo> getHdmiDeviceList() {
225d922a546b94119217fb790113d0001cad0432060Wonsik Kim        synchronized (mLock) {
226546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo            return Collections.unmodifiableList(mHdmiDeviceList);
2274f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
2284f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
2294f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
230e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim    private boolean checkUidChangedLocked(
231e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            Connection connection, int callingUid, int resolvedUserId) {
232e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        Integer connectionCallingUid = connection.getCallingUidLocked();
233e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        Integer connectionResolvedUserId = connection.getResolvedUserIdLocked();
234e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        if (connectionCallingUid == null || connectionResolvedUserId == null) {
235e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            return true;
236e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        }
237e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        if (connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId) {
238e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            return true;
239e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        }
240e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        return false;
241e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim    }
242e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim
243969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    private int convertConnectedToState(boolean connected) {
244969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (connected) {
245969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return INPUT_STATE_CONNECTED;
246969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        } else {
247969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return INPUT_STATE_DISCONNECTED;
248969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
249969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
250969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
2514f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    public void addHardwareTvInput(int deviceId, TvInputInfo info) {
252969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        synchronized (mLock) {
2534f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            String oldInputId = mHardwareInputIdMap.get(deviceId);
2544f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (oldInputId != null) {
255969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                Slog.w(TAG, "Trying to override previous registration: old = "
2564f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                        + mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
257969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        + info + ":" + deviceId);
258969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
2594f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mHardwareInputIdMap.put(deviceId, info.getId());
2604f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mInputMap.put(info.getId(), info);
261969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
262969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            for (int i = 0; i < mHdmiStateMap.size(); ++i) {
263d922a546b94119217fb790113d0001cad0432060Wonsik Kim                TvInputHardwareInfo hardwareInfo =
264d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i));
265d922a546b94119217fb790113d0001cad0432060Wonsik Kim                if (hardwareInfo == null) {
266d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    continue;
267d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
268d922a546b94119217fb790113d0001cad0432060Wonsik Kim                String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
269969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                if (inputId != null && inputId.equals(info.getId())) {
270187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
271969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                            convertConnectedToState(mHdmiStateMap.valueAt(i)), 0,
272969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                            inputId).sendToTarget();
273969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
274969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
275969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
276969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
277969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
27838feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim    private static <T> int indexOfEqualValue(SparseArray<T> map, T value) {
27938feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim        for (int i = 0; i < map.size(); ++i) {
28038feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            if (map.valueAt(i).equals(value)) {
28138feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim                return i;
28238feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            }
28338feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim        }
28438feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim        return -1;
28538feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim    }
28638feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim
2878960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim    public void addHdmiTvInput(int id, TvInputInfo info) {
2884f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        if (info.getType() != TvInputInfo.TYPE_HDMI) {
2894f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
2904f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
2914f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        synchronized (mLock) {
2924f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            String parentId = info.getParentId();
29338feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId);
29438feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            if (parentIndex < 0) {
2954f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
2964f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
2978960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim            String oldInputId = mHdmiInputIdMap.get(id);
2984f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (oldInputId != null) {
2994f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                Slog.w(TAG, "Trying to override previous registration: old = "
3008960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim                        + mInputMap.get(oldInputId) + ":" + id + ", new = "
3018960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim                        + info + ":" + id);
3024f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
3038960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim            mHdmiInputIdMap.put(id, info.getId());
3044f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mInputMap.put(info.getId(), info);
3054f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
3064f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
3074f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
3084f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    public void removeTvInput(String inputId) {
3094f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        synchronized (mLock) {
3104f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mInputMap.remove(inputId);
31138feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
3124f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (hardwareIndex >= 0) {
3134f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                mHardwareInputIdMap.removeAt(hardwareIndex);
3144f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
315546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo            int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId);
316546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo            if (deviceIndex >= 0) {
317546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                mHdmiInputIdMap.removeAt(deviceIndex);
3184f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
3194f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
3204f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
3214f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
322c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    /**
323c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * Create a TvInputHardware object with a specific deviceId. One service at a time can access
324c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * the object, and if more than one process attempts to create hardware with the same deviceId,
325c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * the latest service will get the object and all the other hardware are released. The
326c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * release is notified via ITvInputHardwareCallback.onReleased().
327c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     */
328c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
329969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            TvInputInfo info, int callingUid, int resolvedUserId) {
330c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        if (callback == null) {
331c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            throw new NullPointerException();
332c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
333c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
334c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
335c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
336c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "Invalid deviceId : " + deviceId);
337c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return null;
338c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
339e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
340969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                TvInputHardwareImpl hardware =
341969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        new TvInputHardwareImpl(connection.getHardwareInfoLocked());
342c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                try {
343c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    callback.asBinder().linkToDeath(connection, 0);
344c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                } catch (RemoteException e) {
345c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    hardware.release();
346c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    return null;
347c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
348969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId);
349c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
350c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return connection.getHardwareLocked();
351c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
352c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
353c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
354c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    /**
355c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * Release the specified hardware.
356c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     */
357c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid,
358c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            int resolvedUserId) {
359c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
360c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
361c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
362c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "Invalid deviceId : " + deviceId);
363c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
364c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
365c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection.getHardwareLocked() != hardware
366e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim                    || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
367c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
368c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
369969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            connection.resetLocked(null, null, null, null, null);
370969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
371969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
372969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
373d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private TvInputHardwareInfo findHardwareInfoForHdmiPortLocked(int port) {
374d922a546b94119217fb790113d0001cad0432060Wonsik Kim        for (TvInputHardwareInfo hardwareInfo : mHardwareList) {
375969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
376969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    && hardwareInfo.getHdmiPortId() == port) {
377d922a546b94119217fb790113d0001cad0432060Wonsik Kim                return hardwareInfo;
378969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
379969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
380969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        return null;
381969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
382969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
383c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    private int findDeviceIdForInputIdLocked(String inputId) {
384c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        for (int i = 0; i < mConnections.size(); ++i) {
385c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(i);
386c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (connection.getInfoLocked().getId().equals(inputId)) {
387c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return i;
388c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
389c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
390c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        return -1;
391c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
392c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
393c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    /**
394c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     * Get the list of TvStreamConfig which is buffered mode.
395c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     */
396c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid,
397c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int resolvedUserId) {
398c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        List<TvStreamConfig> configsList = new ArrayList<TvStreamConfig>();
399c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        synchronized (mLock) {
400c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int deviceId = findDeviceIdForInputIdLocked(inputId);
401c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (deviceId < 0) {
402c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Slog.e(TAG, "Invalid inputId : " + inputId);
403c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return configsList;
404c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
405c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(deviceId);
406c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            for (TvStreamConfig config : connection.getConfigsLocked()) {
407c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
408c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    configsList.add(config);
409c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
410c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
411c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
412c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        return configsList;
413c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
414c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
415c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    /**
416c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     * Take a snapshot of the given TV input into the provided Surface.
417c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     */
418c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config,
419c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int callingUid, int resolvedUserId) {
420c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        synchronized (mLock) {
421c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int deviceId = findDeviceIdForInputIdLocked(inputId);
422c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (deviceId < 0) {
423c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Slog.e(TAG, "Invalid inputId : " + inputId);
424c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return false;
425c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
426c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(deviceId);
427c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
428c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (hardwareImpl != null) {
429c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                // Stop previous capture.
430c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Runnable runnable = connection.getOnFirstFrameCapturedLocked();
431c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (runnable != null) {
432c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    runnable.run();
433c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    connection.setOnFirstFrameCapturedLocked(null);
434c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
435c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
436c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                boolean result = hardwareImpl.startCapture(surface, config);
437c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (result) {
438c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    connection.setOnFirstFrameCapturedLocked(new Runnable() {
439c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        @Override
440c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        public void run() {
441c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                            hardwareImpl.stopCapture(config);
442c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        }
443c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    });
444c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
445c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return result;
446c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
447c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
448c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        return false;
449c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
450c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
451d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private void processPendingHdmiDeviceEventsLocked() {
452d922a546b94119217fb790113d0001cad0432060Wonsik Kim        for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) {
453d922a546b94119217fb790113d0001cad0432060Wonsik Kim            Message msg = it.next();
45461f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang            HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
455d922a546b94119217fb790113d0001cad0432060Wonsik Kim            TvInputHardwareInfo hardwareInfo =
456d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId());
457d922a546b94119217fb790113d0001cad0432060Wonsik Kim            if (hardwareInfo != null) {
458d922a546b94119217fb790113d0001cad0432060Wonsik Kim                msg.sendToTarget();
459d922a546b94119217fb790113d0001cad0432060Wonsik Kim                it.remove();
460d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
461d922a546b94119217fb790113d0001cad0432060Wonsik Kim        }
462d922a546b94119217fb790113d0001cad0432060Wonsik Kim    }
463d922a546b94119217fb790113d0001cad0432060Wonsik Kim
464c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private class Connection implements IBinder.DeathRecipient {
465969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        private final TvInputHardwareInfo mHardwareInfo;
466969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        private TvInputInfo mInfo;
467c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private TvInputHardwareImpl mHardware = null;
468c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private ITvInputHardwareCallback mCallback;
469c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private TvStreamConfig[] mConfigs = null;
470c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private Integer mCallingUid = null;
471c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private Integer mResolvedUserId = null;
472c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        private Runnable mOnFirstFrameCaptured;
473c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
474969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public Connection(TvInputHardwareInfo hardwareInfo) {
475969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            mHardwareInfo = hardwareInfo;
476c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
477c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
478c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        // *Locked methods assume TvInputHardwareManager.mLock is held.
479c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
480969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
481969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                TvInputInfo info, Integer callingUid, Integer resolvedUserId) {
482c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mHardware != null) {
483c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                try {
484c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    mCallback.onReleased();
485c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                } catch (RemoteException e) {
4864f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Slog.e(TAG, "error in Connection::resetLocked", e);
487c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
488c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                mHardware.release();
489c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
490c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mHardware = hardware;
491c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mCallback = callback;
492969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            mInfo = info;
493c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mCallingUid = callingUid;
494c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mResolvedUserId = resolvedUserId;
495c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            mOnFirstFrameCaptured = null;
496c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
497c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mHardware != null && mCallback != null) {
498c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                try {
499c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    mCallback.onStreamConfigChanged(getConfigsLocked());
500c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                } catch (RemoteException e) {
5014f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Slog.e(TAG, "error in Connection::resetLocked", e);
502c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
503c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
504c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
505c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
506c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void updateConfigsLocked(TvStreamConfig[] configs) {
507c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mConfigs = configs;
508c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
509c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
510969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public TvInputHardwareInfo getHardwareInfoLocked() {
511969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return mHardwareInfo;
512969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
513969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
514969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public TvInputInfo getInfoLocked() {
515c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mInfo;
516c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
517c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
518c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public ITvInputHardware getHardwareLocked() {
519c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mHardware;
520c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
521c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
522c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public TvInputHardwareImpl getHardwareImplLocked() {
523c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            return mHardware;
524c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
525c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
526c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public ITvInputHardwareCallback getCallbackLocked() {
527c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mCallback;
528c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
529c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
530c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public TvStreamConfig[] getConfigsLocked() {
531c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mConfigs;
532c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
533c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
534e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        public Integer getCallingUidLocked() {
535c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mCallingUid;
536c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
537c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
538e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        public Integer getResolvedUserIdLocked() {
539c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mResolvedUserId;
540c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
541c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
542c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public void setOnFirstFrameCapturedLocked(Runnable runnable) {
543c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            mOnFirstFrameCaptured = runnable;
544c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
545c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
546c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public Runnable getOnFirstFrameCapturedLocked() {
547c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            return mOnFirstFrameCaptured;
548c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
549c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
550c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
551c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void binderDied() {
552c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mLock) {
553969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                resetLocked(null, null, null, null, null);
554c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
555c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
556c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
557c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
558c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private class TvInputHardwareImpl extends ITvInputHardware.Stub {
559c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private final TvInputHardwareInfo mInfo;
560c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private boolean mReleased = false;
561c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private final Object mImplLock = new Object();
562c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
56306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private final AudioManager.OnAudioPortUpdateListener mAudioListener =
56406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                new AudioManager.OnAudioPortUpdateListener() {
56506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            @Override
56606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            public void onAudioPortListUpdate(AudioPort[] portList) {
56706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                synchronized (mImplLock) {
5687587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    updateAudioConfigLocked();
56906c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
57006c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
57106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
57206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            @Override
57306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            public void onAudioPatchListUpdate(AudioPatch[] patchList) {
57406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                // No-op
57506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
57606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
57706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            @Override
57806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            public void onServiceDied() {
57906c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                synchronized (mImplLock) {
58006c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                    mAudioSource = null;
58106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                    mAudioSink = null;
58206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                    mAudioPatch = null;
58306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
58406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
58506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        };
58606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private int mOverrideAudioType = AudioManager.DEVICE_NONE;
58706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private String mOverrideAudioAddress = "";
58806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private AudioDevicePort mAudioSource;
589ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private AudioDevicePort mAudioSink;
590d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim        private AudioPatch mAudioPatch = null;
5918e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim        private float mCommittedVolume = 0.0f;
5928e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim        private float mVolume = 0.0f;
593d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim
594839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        private TvStreamConfig mActiveConfig = null;
595839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim
596ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private int mDesiredSamplingRate = 0;
597ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
598ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT;
599ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
600c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public TvInputHardwareImpl(TvInputHardwareInfo info) {
601c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mInfo = info;
60206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            mAudioManager.registerAudioPortUpdateListener(mAudioListener);
603d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim            if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
60406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
605ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mAudioSink = findAudioSinkFromAudioPolicy();
606ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
607ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
608ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
609ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private AudioDevicePort findAudioSinkFromAudioPolicy() {
610ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
611ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (mAudioManager.listAudioDevicePorts(devicePorts) == AudioManager.SUCCESS) {
612ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
613ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                for (AudioPort port : devicePorts) {
614ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    AudioDevicePort devicePort = (AudioDevicePort) port;
6151f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                    if ((devicePort.type() & sinkDevice) != 0) {
616ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                        return devicePort;
617d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                    }
618d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                }
619d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim            }
620ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            return null;
621ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
622ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
623ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private AudioDevicePort findAudioDevicePort(int type, String address) {
624ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (type == AudioManager.DEVICE_NONE) {
625ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                return null;
626ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
627ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
628ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
629ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                return null;
630ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
631ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            for (AudioPort port : devicePorts) {
632ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                AudioDevicePort devicePort = (AudioDevicePort) port;
633ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                if (devicePort.type() == type && devicePort.address().equals(address)) {
634ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    return devicePort;
635ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                }
636ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
637ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            return null;
638c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
639c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
640c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void release() {
641c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
64206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mAudioManager.unregisterAudioPortUpdateListener(mAudioListener);
643d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                if (mAudioPatch != null) {
644d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                    mAudioManager.releaseAudioPatch(mAudioPatch);
645d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                    mAudioPatch = null;
646d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                }
647c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                mReleased = true;
648c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
649c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
650c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
651839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        // A TvInputHardwareImpl object holds only one active session. Therefore, if a client
652839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        // attempts to call setSurface with different TvStreamConfig objects, the last call will
653839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        // prevail.
654c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
655c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public boolean setSurface(Surface surface, TvStreamConfig config)
656c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                throws RemoteException {
657c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
658c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                if (mReleased) {
659c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    throw new IllegalStateException("Device already released.");
660c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
661839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                if (surface != null && config == null) {
662839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    return false;
663839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                }
664839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                if (surface == null && mActiveConfig == null) {
665839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    return false;
666839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                }
6677587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim
668839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                int result = TvInputHal.ERROR_UNKNOWN;
669839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                if (surface == null) {
670839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
671839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    mActiveConfig = null;
672839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                } else {
673839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    if (config != mActiveConfig && mActiveConfig != null) {
674839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
675839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        if (result != TvInputHal.SUCCESS) {
676839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                            mActiveConfig = null;
677839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                            return false;
678839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        }
679839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    }
680839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    result = mHal.addStream(mInfo.getDeviceId(), surface, config);
681839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    if (result == TvInputHal.SUCCESS) {
682839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        mActiveConfig = config;
683839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    }
684839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                }
6857587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
686839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                return result == TvInputHal.SUCCESS;
687c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
688c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
689c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
6907587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        /**
6917587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim         * Update audio configuration (source, sink, patch) all up to current state.
6927587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim         */
6937587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        private void updateAudioConfigLocked() {
6947587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            boolean sinkUpdated = updateAudioSinkLocked();
6957587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            boolean sourceUpdated = updateAudioSourceLocked();
6967587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            // We can't do updated = updateAudioSinkLocked() || updateAudioSourceLocked() here
6977587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            // because Java won't evaluate the latter if the former is true.
6987587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim
6997587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            if (mAudioSource == null || mAudioSink == null || mActiveConfig == null) {
70006c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                if (mAudioPatch != null) {
7017587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    mAudioManager.releaseAudioPatch(mAudioPatch);
7027587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    mAudioPatch = null;
70306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
70406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                return;
70506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
70606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
7078e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            AudioGainConfig sourceGainConfig = null;
7088e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            if (mAudioSource.gains().length > 0 && mVolume != mCommittedVolume) {
7098e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                AudioGain sourceGain = null;
7108e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                for (AudioGain gain : mAudioSource.gains()) {
7118e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    if ((gain.mode() & AudioGain.MODE_JOINT) != 0) {
7128e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        sourceGain = gain;
7138e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        break;
7148e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    }
7158e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                }
7168e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                // NOTE: we only change the source gain in MODE_JOINT here.
7178e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                if (sourceGain != null) {
7188e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    int steps = (sourceGain.maxValue() - sourceGain.minValue())
7198e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                            / sourceGain.stepValue();
7208e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    int gainValue = sourceGain.minValue();
7218e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    if (mVolume < 1.0f) {
7228e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        gainValue += sourceGain.stepValue() * (int) (mVolume * steps + 0.5);
7238e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    } else {
7248e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        gainValue = sourceGain.maxValue();
7258e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    }
7268e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    int numChannels = 0;
7278e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    for (int mask = sourceGain.channelMask(); mask > 0; mask >>= 1) {
7288e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        numChannels += (mask & 1);
7298e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    }
7308e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    int[] gainValues = new int[numChannels];
7318e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    Arrays.fill(gainValues, gainValue);
7328e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    sourceGainConfig = sourceGain.buildConfig(AudioGain.MODE_JOINT,
7338e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                            sourceGain.channelMask(), gainValues, 0);
7348e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                } else {
7358e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    Slog.w(TAG, "No audio source gain with MODE_JOINT support exists.");
7368e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                }
7378e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            }
7388e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim
739ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            AudioPortConfig sourceConfig = mAudioSource.activeConfig();
740ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            AudioPortConfig sinkConfig = mAudioSink.activeConfig();
741ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
7427587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
743ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (sinkConfig == null
744ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    || (mDesiredSamplingRate != 0
745ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                            && sinkConfig.samplingRate() != mDesiredSamplingRate)
746ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    || (mDesiredChannelMask != AudioFormat.CHANNEL_OUT_DEFAULT
747ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                            && sinkConfig.channelMask() != mDesiredChannelMask)
748ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    || (mDesiredFormat != AudioFormat.ENCODING_DEFAULT
749ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                            && sinkConfig.format() != mDesiredFormat)) {
750ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                sinkConfig = mAudioSource.buildConfig(mDesiredSamplingRate, mDesiredChannelMask,
751ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                        mDesiredFormat, null);
7528e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                shouldRecreateAudioPatch = true;
753ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
7548e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            if (sourceConfig == null || sourceGainConfig != null) {
755ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                sourceConfig = mAudioSource.buildConfig(sinkConfig.samplingRate(),
7568e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        sinkConfig.channelMask(), sinkConfig.format(), sourceGainConfig);
7578e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                shouldRecreateAudioPatch = true;
7588e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            }
7598e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            if (shouldRecreateAudioPatch) {
7608e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                mCommittedVolume = mVolume;
7618e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                mAudioManager.createAudioPatch(
7628e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        audioPatchArray,
7638e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        new AudioPortConfig[] { sourceConfig },
7648e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        new AudioPortConfig[] { sinkConfig });
7658e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                mAudioPatch = audioPatchArray[0];
766ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
767ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
768ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
769c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
7708e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim        public void setStreamVolume(float volume) throws RemoteException {
771c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
772c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                if (mReleased) {
773c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    throw new IllegalStateException("Device already released.");
774c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
7758e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                mVolume = volume;
7767587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
777c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
778c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
779c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
780c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
781c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
782c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
783c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                if (mReleased) {
784c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    throw new IllegalStateException("Device already released.");
785c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
786c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
787610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim            if (mInfo.getType() != TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
788c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return false;
789c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
790c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
791c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return false;
792c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
793c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
794c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        private boolean startCapture(Surface surface, TvStreamConfig config) {
795c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            synchronized (mImplLock) {
796c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (mReleased) {
797c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
798c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
799c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (surface == null || config == null) {
800c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
801c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
802c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
803c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
804c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
805c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
806c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                int result = mHal.addStream(mInfo.getDeviceId(), surface, config);
807c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return result == TvInputHal.SUCCESS;
808c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
809c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
810c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
811c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        private boolean stopCapture(TvStreamConfig config) {
812c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            synchronized (mImplLock) {
813c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (mReleased) {
814c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
815c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
816c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (config == null) {
817c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
818c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
819c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
820c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                int result = mHal.removeStream(mInfo.getDeviceId(), config);
821c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return result == TvInputHal.SUCCESS;
822c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
823c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
824ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
8257587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        private boolean updateAudioSourceLocked() {
82606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
8277587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                return false;
82806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
8297587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            AudioDevicePort previousSource = mAudioSource;
8307587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
8317587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            return mAudioSource == null ? (previousSource != null)
8327587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    : !mAudioSource.equals(previousSource);
8337587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        }
8347587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim
8357587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        private boolean updateAudioSinkLocked() {
8367587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
8377587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                return false;
8387587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            }
8397587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            AudioDevicePort previousSink = mAudioSink;
84006c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            if (mOverrideAudioType == AudioManager.DEVICE_NONE) {
84106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mAudioSink = findAudioSinkFromAudioPolicy();
84206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            } else {
84306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                AudioDevicePort audioSink =
84406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                        findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress);
84506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                if (audioSink != null) {
84606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                    mAudioSink = audioSink;
84706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
84806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
8497587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            return mAudioSink == null ? (previousSink != null) : !mAudioSink.equals(previousSink);
85006c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        }
85106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
8521f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        private void handleAudioSinkUpdated() {
8531f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            synchronized (mImplLock) {
8547587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
8551f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            }
8561f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        }
8571f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang
858ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        @Override
859ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
860ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                int channelMask, int format) {
861ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            synchronized (mImplLock) {
86206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mOverrideAudioType = audioType;
86306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mOverrideAudioAddress = audioAddress;
86406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
865ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mDesiredSamplingRate = samplingRate;
866ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mDesiredChannelMask = channelMask;
867ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mDesiredFormat = format;
868ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
8697587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
870ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
871ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
872c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
873969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
874187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    interface Listener {
875187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        public void onStateChanged(String inputId, int state);
876187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        public void onHardwareDeviceAdded(TvInputHardwareInfo info);
8774f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        public void onHardwareDeviceRemoved(TvInputHardwareInfo info);
878546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        public void onHdmiDeviceAdded(HdmiDeviceInfo device);
879546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        public void onHdmiDeviceRemoved(HdmiDeviceInfo device);
88061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang        public void onHdmiDeviceUpdated(HdmiDeviceInfo device);
881187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    }
882969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
883187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    private class ListenerHandler extends Handler {
884187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        private static final int STATE_CHANGED = 1;
885187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        private static final int HARDWARE_DEVICE_ADDED = 2;
886187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        private static final int HARDWARE_DEVICE_REMOVED = 3;
887546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        private static final int HDMI_DEVICE_ADDED = 4;
888546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        private static final int HDMI_DEVICE_REMOVED = 5;
88961daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang        private static final int HDMI_DEVICE_UPDATED = 6;
890969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
891969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        @Override
892969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public final void handleMessage(Message msg) {
893969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            switch (msg.what) {
894187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                case STATE_CHANGED: {
895969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    String inputId = (String) msg.obj;
896969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    int state = msg.arg1;
897187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    mListener.onStateChanged(inputId, state);
898187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
899187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
900187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                case HARDWARE_DEVICE_ADDED: {
901187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
902187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    mListener.onHardwareDeviceAdded(info);
903187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
904187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
905187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                case HARDWARE_DEVICE_REMOVED: {
9064f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
9074f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    mListener.onHardwareDeviceRemoved(info);
908187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
909187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
910546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                case HDMI_DEVICE_ADDED: {
91161f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                    HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
912546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    mListener.onHdmiDeviceAdded(info);
913187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
914187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
915546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                case HDMI_DEVICE_REMOVED: {
91661f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                    HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
917546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    mListener.onHdmiDeviceRemoved(info);
918969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    break;
919969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
92061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                case HDMI_DEVICE_UPDATED: {
92161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
92261daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    mListener.onHdmiDeviceUpdated(info);
92361daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                }
924969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                default: {
925969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    Slog.w(TAG, "Unhandled message: " + msg);
926969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    break;
927969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
928969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
929969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
930969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
931187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
9327474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    // Listener implementations for HdmiControlService
9337474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim
9347474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub {
9357474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        @Override
9367474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        public void onReceived(HdmiHotplugEvent event) {
9377474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            synchronized (mLock) {
9387474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                mHdmiStateMap.put(event.getPort(), event.isConnected());
939d922a546b94119217fb790113d0001cad0432060Wonsik Kim                TvInputHardwareInfo hardwareInfo =
940d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        findHardwareInfoForHdmiPortLocked(event.getPort());
941d922a546b94119217fb790113d0001cad0432060Wonsik Kim                if (hardwareInfo == null) {
942d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    return;
943d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
944d922a546b94119217fb790113d0001cad0432060Wonsik Kim                String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
9457474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                if (inputId == null) {
9467474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                    return;
9477474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                }
9487474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
9497474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                        convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
9507474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            }
9517474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        }
9527474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    }
9537474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim
954187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
955187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        @Override
95661daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang        public void onStatusChanged(HdmiDeviceInfo deviceInfo, int status) {
957d922a546b94119217fb790113d0001cad0432060Wonsik Kim            synchronized (mLock) {
95861daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                int messageType = 0;
95961daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                switch (status) {
96061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    case HdmiControlManager.DEVICE_EVENT_ADD_DEVICE: {
96161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        if (!mHdmiDeviceList.contains(deviceInfo)) {
96261daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            mHdmiDeviceList.add(deviceInfo);
96361daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        } else {
96461daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
96561daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            return;
96661daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        }
96761daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        messageType = ListenerHandler.HDMI_DEVICE_ADDED;
96861daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        break;
969d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    }
97061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    case HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE: {
97161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        if (!mHdmiDeviceList.remove(deviceInfo)) {
97261daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
97361daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            return;
97461daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        }
97561daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        messageType = ListenerHandler.HDMI_DEVICE_REMOVED;
97661daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        break;
97761daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    }
97861daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    case HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE: {
97961daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        if (!mHdmiDeviceList.remove(deviceInfo)) {
98061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
98161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            return;
98261daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        }
98361daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        mHdmiDeviceList.add(deviceInfo);
98461daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        messageType = ListenerHandler.HDMI_DEVICE_UPDATED;
98561daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        break;
986d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    }
987d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
98861daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang
98961daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                Message msg = mHandler.obtainMessage(messageType, 0, 0, deviceInfo);
990d922a546b94119217fb790113d0001cad0432060Wonsik Kim                if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
991d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    msg.sendToTarget();
992d922a546b94119217fb790113d0001cad0432060Wonsik Kim                } else {
993d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    mPendingHdmiDeviceEvents.add(msg);
994d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
995d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
996187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        }
997187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    }
9987474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim
9991f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang    private final class HdmiSystemAudioModeChangeListener extends
10001f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        IHdmiSystemAudioModeChangeListener.Stub {
10011f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        @Override
10021f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        public void onStatusChanged(boolean enabled) throws RemoteException {
10031f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            synchronized (mLock) {
10041f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                for (int i = 0; i < mConnections.size(); ++i) {
10051f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                    TvInputHardwareImpl impl = mConnections.valueAt(i).getHardwareImplLocked();
10061f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                    impl.handleAudioSinkUpdated();
10071f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                }
10081f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            }
10091f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        }
10101f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang    }
1011c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim}
1012