TvInputHardwareManager.java revision 102670f16a44d4b25d7e3a252bedee0208b68c55
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;
2361daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jangimport android.hardware.hdmi.HdmiControlManager;
2461f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jangimport android.hardware.hdmi.HdmiDeviceInfo;
25969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.hardware.hdmi.HdmiHotplugEvent;
267474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.hardware.hdmi.IHdmiControlService;
27187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kimimport android.hardware.hdmi.IHdmiDeviceEventListener;
287474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.hardware.hdmi.IHdmiHotplugEventListener;
291f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jangimport android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
30d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioDevicePort;
31ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kimimport android.media.AudioFormat;
328e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kimimport android.media.AudioGain;
338e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kimimport android.media.AudioGainConfig;
34d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioManager;
35d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioPatch;
36d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioPort;
37d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioPortConfig;
38d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardware;
39d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardwareCallback;
40546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seoimport android.media.tv.TvInputHardwareInfo;
41969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.media.tv.TvInputInfo;
42d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvStreamConfig;
43969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.os.Handler;
44c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.os.IBinder;
45969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.os.Message;
46c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.os.RemoteException;
477474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.os.ServiceManager;
484f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Leeimport android.util.ArrayMap;
49c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.util.Slog;
50c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.util.SparseArray;
51969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.util.SparseBooleanArray;
52c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.view.KeyEvent;
53c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.view.Surface;
54c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
55e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kimimport com.android.internal.os.SomeArgs;
56969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport com.android.server.SystemService;
57969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
58c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport java.util.ArrayList;
598e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kimimport java.util.Arrays;
604f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Leeimport java.util.Collections;
61d922a546b94119217fb790113d0001cad0432060Wonsik Kimimport java.util.Iterator;
62d922a546b94119217fb790113d0001cad0432060Wonsik Kimimport java.util.LinkedList;
63c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport java.util.List;
644f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Leeimport java.util.Map;
65c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
66c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim/**
67c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * A helper class for TvInputManagerService to handle TV input hardware.
68c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
69c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * This class does a basic connection management and forwarding calls to TvInputHal which eventually
70c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * calls to tv_input HAL module.
71c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
72c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * @hide
73c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim */
747474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimclass TvInputHardwareManager implements TvInputHal.Callback {
75c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private static final String TAG = TvInputHardwareManager.class.getSimpleName();
764f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
774f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final Listener mListener;
78c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private final TvInputHal mHal = new TvInputHal(this);
79d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final SparseArray<Connection> mConnections = new SparseArray<>();
80d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
81546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo    private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>();
824f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    /* A map from a device ID to the matching TV input ID. */
83d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
844f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    /* A map from a HDMI logical address to the matching TV input ID. */
85546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo    private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>();
86d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>();
874f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
88d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim    private final AudioManager mAudioManager;
894f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private IHdmiControlService mHdmiControlService;
907474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    private final IHdmiHotplugEventListener mHdmiHotplugEventListener =
917474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            new HdmiHotplugEventListener();
924f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener();
931f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang    private final IHdmiSystemAudioModeChangeListener mHdmiSystemAudioModeChangeListener =
941f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            new HdmiSystemAudioModeChangeListener();
95d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim
96d922a546b94119217fb790113d0001cad0432060Wonsik Kim    // TODO: Should handle STANDBY case.
974f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
98d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
99969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
100187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    // Calls to mListener should happen here.
101187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    private final Handler mHandler = new ListenerHandler();
102c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
103c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private final Object mLock = new Object();
104c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
105187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    public TvInputHardwareManager(Context context, Listener listener) {
106187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        mListener = listener;
107d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
108c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        mHal.init();
109969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
110969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
111969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    public void onBootPhase(int phase) {
112969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1134f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mHdmiControlService = IHdmiControlService.Stub.asInterface(ServiceManager.getService(
1144f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Context.HDMI_CONTROL_SERVICE));
1154f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (mHdmiControlService != null) {
1167474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                try {
1174f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    mHdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
1184f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    mHdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
1191f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                    mHdmiControlService.addSystemAudioModeChangeListener(
1201f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                            mHdmiSystemAudioModeChangeListener);
121546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    mHdmiDeviceList.addAll(mHdmiControlService.getInputDevices());
1227474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                } catch (RemoteException e) {
1234f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
1247474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                }
12508f1ab02d6de42756825a2dfa7027137ff959bd8Jinsuk Kim            } else {
12608f1ab02d6de42756825a2dfa7027137ff959bd8Jinsuk Kim                Slog.w(TAG, "HdmiControlService is not available");
1277474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            }
128969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
129c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
130c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
131c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    @Override
1324f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) {
133c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
134c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = new Connection(info);
135c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            connection.updateConfigsLocked(configs);
136c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mConnections.put(info.getDeviceId(), connection);
137d922a546b94119217fb790113d0001cad0432060Wonsik Kim            buildHardwareListLocked();
138187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            mHandler.obtainMessage(
139187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget();
140d922a546b94119217fb790113d0001cad0432060Wonsik Kim            if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
141d922a546b94119217fb790113d0001cad0432060Wonsik Kim                processPendingHdmiDeviceEventsLocked();
142d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
143c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
144c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
145c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
146d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private void buildHardwareListLocked() {
147d922a546b94119217fb790113d0001cad0432060Wonsik Kim        mHardwareList.clear();
148c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        for (int i = 0; i < mConnections.size(); ++i) {
149d922a546b94119217fb790113d0001cad0432060Wonsik Kim            mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked());
150c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
151c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
152c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
153c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    @Override
154c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public void onDeviceUnavailable(int deviceId) {
155c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
156c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
157c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
158c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
159c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
160c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
161969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            connection.resetLocked(null, null, null, null, null);
162c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mConnections.remove(deviceId);
163d922a546b94119217fb790113d0001cad0432060Wonsik Kim            buildHardwareListLocked();
1644f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            TvInputHardwareInfo info = connection.getHardwareInfoLocked();
165d922a546b94119217fb790113d0001cad0432060Wonsik Kim            if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
166546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                // Remove HDMI devices linked with this hardware.
167546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                for (Iterator<HdmiDeviceInfo> it = mHdmiDeviceList.iterator(); it.hasNext();) {
16861f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                    HdmiDeviceInfo deviceInfo = it.next();
169d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    if (deviceInfo.getPortId() == info.getHdmiPortId()) {
170546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                        mHandler.obtainMessage(ListenerHandler.HDMI_DEVICE_REMOVED, 0, 0,
171d922a546b94119217fb790113d0001cad0432060Wonsik Kim                                deviceInfo).sendToTarget();
172d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        it.remove();
173d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    }
174d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
175d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
176187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            mHandler.obtainMessage(
1774f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    ListenerHandler.HARDWARE_DEVICE_REMOVED, 0, 0, info).sendToTarget();
178c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
179c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
180c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
181c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    @Override
182c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) {
183c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
184c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
185c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
186c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with "
187c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                        + deviceId);
188c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
189c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
190c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            connection.updateConfigsLocked(configs);
1919a1036575182705bc27c6bf255dd17669821181dWonsik Kim            String inputId = mHardwareInputIdMap.get(deviceId);
1929a1036575182705bc27c6bf255dd17669821181dWonsik Kim            if (inputId != null) {
1939a1036575182705bc27c6bf255dd17669821181dWonsik Kim                mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
1945b820a8aa1a200824e44aede6bc1e381a6d69dd5Wonsik Kim                        convertConnectedToState(configs.length > 0), 0, inputId).sendToTarget();
1959a1036575182705bc27c6bf255dd17669821181dWonsik Kim            }
196c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
197c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                connection.getCallbackLocked().onStreamConfigChanged(configs);
198c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } catch (RemoteException e) {
1994f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                Slog.e(TAG, "error in onStreamConfigurationChanged", e);
200c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
201c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
202c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
203c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
204c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    @Override
205c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    public void onFirstFrameCaptured(int deviceId, int streamId) {
206c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        synchronized (mLock) {
207c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(deviceId);
208c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (connection == null) {
209c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Slog.e(TAG, "FirstFrameCaptured: Cannot find a connection with "
210c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        + deviceId);
211c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return;
212c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
213c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Runnable runnable = connection.getOnFirstFrameCapturedLocked();
214c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (runnable != null) {
215c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                runnable.run();
216c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                connection.setOnFirstFrameCapturedLocked(null);
217c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
218c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
219c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
220c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
221c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public List<TvInputHardwareInfo> getHardwareList() {
222c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
223d922a546b94119217fb790113d0001cad0432060Wonsik Kim            return Collections.unmodifiableList(mHardwareList);
224c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
225c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
226c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
227546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo    public List<HdmiDeviceInfo> getHdmiDeviceList() {
228d922a546b94119217fb790113d0001cad0432060Wonsik Kim        synchronized (mLock) {
229546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo            return Collections.unmodifiableList(mHdmiDeviceList);
2304f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
2314f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
2324f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
233e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim    private boolean checkUidChangedLocked(
234e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            Connection connection, int callingUid, int resolvedUserId) {
235e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        Integer connectionCallingUid = connection.getCallingUidLocked();
236e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        Integer connectionResolvedUserId = connection.getResolvedUserIdLocked();
237e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        if (connectionCallingUid == null || connectionResolvedUserId == null) {
238e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            return true;
239e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        }
240e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        if (connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId) {
241e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            return true;
242e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        }
243e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        return false;
244e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim    }
245e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim
246969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    private int convertConnectedToState(boolean connected) {
247969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (connected) {
248969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return INPUT_STATE_CONNECTED;
249969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        } else {
250969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return INPUT_STATE_DISCONNECTED;
251969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
252969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
253969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
2544f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    public void addHardwareTvInput(int deviceId, TvInputInfo info) {
255969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        synchronized (mLock) {
2564f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            String oldInputId = mHardwareInputIdMap.get(deviceId);
2574f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (oldInputId != null) {
258969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                Slog.w(TAG, "Trying to override previous registration: old = "
2594f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                        + mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
260969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        + info + ":" + deviceId);
261969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
2624f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mHardwareInputIdMap.put(deviceId, info.getId());
2634f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mInputMap.put(info.getId(), info);
264969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
2659a1036575182705bc27c6bf255dd17669821181dWonsik Kim            // Process pending state changes
2669a1036575182705bc27c6bf255dd17669821181dWonsik Kim
2679a1036575182705bc27c6bf255dd17669821181dWonsik Kim            // For logical HDMI devices, they have information from HDMI CEC signals.
268969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            for (int i = 0; i < mHdmiStateMap.size(); ++i) {
269d922a546b94119217fb790113d0001cad0432060Wonsik Kim                TvInputHardwareInfo hardwareInfo =
270d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i));
271d922a546b94119217fb790113d0001cad0432060Wonsik Kim                if (hardwareInfo == null) {
272d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    continue;
273d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
274d922a546b94119217fb790113d0001cad0432060Wonsik Kim                String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
275969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                if (inputId != null && inputId.equals(info.getId())) {
276187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
277969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                            convertConnectedToState(mHdmiStateMap.valueAt(i)), 0,
278969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                            inputId).sendToTarget();
2799a1036575182705bc27c6bf255dd17669821181dWonsik Kim                    return;
280969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
281969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
2829a1036575182705bc27c6bf255dd17669821181dWonsik Kim            // For the rest of the devices, we can tell by the number of available streams.
2839a1036575182705bc27c6bf255dd17669821181dWonsik Kim            Connection connection = mConnections.get(deviceId);
2849a1036575182705bc27c6bf255dd17669821181dWonsik Kim            if (connection != null) {
2859a1036575182705bc27c6bf255dd17669821181dWonsik Kim                mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
2869a1036575182705bc27c6bf255dd17669821181dWonsik Kim                        convertConnectedToState(connection.getConfigsLocked().length > 0), 0,
2879a1036575182705bc27c6bf255dd17669821181dWonsik Kim                        info.getId()).sendToTarget();
2889a1036575182705bc27c6bf255dd17669821181dWonsik Kim                return;
2899a1036575182705bc27c6bf255dd17669821181dWonsik Kim            }
290969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
291969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
292969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
29338feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim    private static <T> int indexOfEqualValue(SparseArray<T> map, T value) {
29438feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim        for (int i = 0; i < map.size(); ++i) {
29538feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            if (map.valueAt(i).equals(value)) {
29638feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim                return i;
29738feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            }
29838feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim        }
29938feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim        return -1;
30038feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim    }
30138feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim
3028960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim    public void addHdmiTvInput(int id, TvInputInfo info) {
3034f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        if (info.getType() != TvInputInfo.TYPE_HDMI) {
3044f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
3054f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
3064f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        synchronized (mLock) {
3074f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            String parentId = info.getParentId();
30838feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId);
30938feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            if (parentIndex < 0) {
3104f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
3114f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
3128960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim            String oldInputId = mHdmiInputIdMap.get(id);
3134f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (oldInputId != null) {
3144f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                Slog.w(TAG, "Trying to override previous registration: old = "
3158960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim                        + mInputMap.get(oldInputId) + ":" + id + ", new = "
3168960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim                        + info + ":" + id);
3174f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
3188960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim            mHdmiInputIdMap.put(id, info.getId());
3194f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mInputMap.put(info.getId(), info);
3204f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
3214f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
3224f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
3234f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    public void removeTvInput(String inputId) {
3244f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        synchronized (mLock) {
3254f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mInputMap.remove(inputId);
32638feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
3274f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (hardwareIndex >= 0) {
3284f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                mHardwareInputIdMap.removeAt(hardwareIndex);
3294f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
330546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo            int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId);
331546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo            if (deviceIndex >= 0) {
332546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                mHdmiInputIdMap.removeAt(deviceIndex);
3334f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
3344f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
3354f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
3364f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
337c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    /**
338c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * Create a TvInputHardware object with a specific deviceId. One service at a time can access
339c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * the object, and if more than one process attempts to create hardware with the same deviceId,
340c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * the latest service will get the object and all the other hardware are released. The
341c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * release is notified via ITvInputHardwareCallback.onReleased().
342c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     */
343c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
344969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            TvInputInfo info, int callingUid, int resolvedUserId) {
345c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        if (callback == null) {
346c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            throw new NullPointerException();
347c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
348c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
349c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
350c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
351c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "Invalid deviceId : " + deviceId);
352c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return null;
353c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
354e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
355969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                TvInputHardwareImpl hardware =
356969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        new TvInputHardwareImpl(connection.getHardwareInfoLocked());
357c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                try {
358c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    callback.asBinder().linkToDeath(connection, 0);
359c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                } catch (RemoteException e) {
360c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    hardware.release();
361c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    return null;
362c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
363969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId);
364c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
365c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return connection.getHardwareLocked();
366c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
367c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
368c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
369c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    /**
370c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * Release the specified hardware.
371c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     */
372c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid,
373c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            int resolvedUserId) {
374c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
375c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
376c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
377c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "Invalid deviceId : " + deviceId);
378c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
379c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
380c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection.getHardwareLocked() != hardware
381e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim                    || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
382c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
383c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
384969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            connection.resetLocked(null, null, null, null, null);
385969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
386969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
387969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
388d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private TvInputHardwareInfo findHardwareInfoForHdmiPortLocked(int port) {
389d922a546b94119217fb790113d0001cad0432060Wonsik Kim        for (TvInputHardwareInfo hardwareInfo : mHardwareList) {
390969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
391969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    && hardwareInfo.getHdmiPortId() == port) {
392d922a546b94119217fb790113d0001cad0432060Wonsik Kim                return hardwareInfo;
393969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
394969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
395969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        return null;
396969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
397969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
398c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    private int findDeviceIdForInputIdLocked(String inputId) {
399c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        for (int i = 0; i < mConnections.size(); ++i) {
400c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(i);
401c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (connection.getInfoLocked().getId().equals(inputId)) {
402c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return i;
403c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
404c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
405c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        return -1;
406c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
407c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
408c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    /**
409c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     * Get the list of TvStreamConfig which is buffered mode.
410c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     */
411c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid,
412c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int resolvedUserId) {
413c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        List<TvStreamConfig> configsList = new ArrayList<TvStreamConfig>();
414c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        synchronized (mLock) {
415c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int deviceId = findDeviceIdForInputIdLocked(inputId);
416c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (deviceId < 0) {
417c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Slog.e(TAG, "Invalid inputId : " + inputId);
418c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return configsList;
419c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
420c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(deviceId);
421c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            for (TvStreamConfig config : connection.getConfigsLocked()) {
422c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
423c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    configsList.add(config);
424c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
425c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
426c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
427c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        return configsList;
428c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
429c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
430c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    /**
431c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     * Take a snapshot of the given TV input into the provided Surface.
432c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     */
433c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config,
434c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int callingUid, int resolvedUserId) {
435c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        synchronized (mLock) {
436c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int deviceId = findDeviceIdForInputIdLocked(inputId);
437c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (deviceId < 0) {
438c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Slog.e(TAG, "Invalid inputId : " + inputId);
439c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return false;
440c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
441c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(deviceId);
442c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
443c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (hardwareImpl != null) {
444c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                // Stop previous capture.
445c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Runnable runnable = connection.getOnFirstFrameCapturedLocked();
446c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (runnable != null) {
447c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    runnable.run();
448c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    connection.setOnFirstFrameCapturedLocked(null);
449c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
450c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
451c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                boolean result = hardwareImpl.startCapture(surface, config);
452c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (result) {
453c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    connection.setOnFirstFrameCapturedLocked(new Runnable() {
454c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        @Override
455c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        public void run() {
456c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                            hardwareImpl.stopCapture(config);
457c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        }
458c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    });
459c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
460c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return result;
461c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
462c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
463c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        return false;
464c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
465c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
466d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private void processPendingHdmiDeviceEventsLocked() {
467d922a546b94119217fb790113d0001cad0432060Wonsik Kim        for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) {
468d922a546b94119217fb790113d0001cad0432060Wonsik Kim            Message msg = it.next();
46961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang            HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
470d922a546b94119217fb790113d0001cad0432060Wonsik Kim            TvInputHardwareInfo hardwareInfo =
471d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId());
472d922a546b94119217fb790113d0001cad0432060Wonsik Kim            if (hardwareInfo != null) {
473d922a546b94119217fb790113d0001cad0432060Wonsik Kim                msg.sendToTarget();
474d922a546b94119217fb790113d0001cad0432060Wonsik Kim                it.remove();
475d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
476d922a546b94119217fb790113d0001cad0432060Wonsik Kim        }
477d922a546b94119217fb790113d0001cad0432060Wonsik Kim    }
478d922a546b94119217fb790113d0001cad0432060Wonsik Kim
479c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private class Connection implements IBinder.DeathRecipient {
480969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        private final TvInputHardwareInfo mHardwareInfo;
481969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        private TvInputInfo mInfo;
482c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private TvInputHardwareImpl mHardware = null;
483c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private ITvInputHardwareCallback mCallback;
484c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private TvStreamConfig[] mConfigs = null;
485c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private Integer mCallingUid = null;
486c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private Integer mResolvedUserId = null;
487c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        private Runnable mOnFirstFrameCaptured;
488c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
489969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public Connection(TvInputHardwareInfo hardwareInfo) {
490969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            mHardwareInfo = hardwareInfo;
491c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
492c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
493c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        // *Locked methods assume TvInputHardwareManager.mLock is held.
494c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
495969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
496969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                TvInputInfo info, Integer callingUid, Integer resolvedUserId) {
497c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mHardware != null) {
498c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                try {
499c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    mCallback.onReleased();
500c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                } catch (RemoteException e) {
5014f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Slog.e(TAG, "error in Connection::resetLocked", e);
502c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
503c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                mHardware.release();
504c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
505c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mHardware = hardware;
506c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mCallback = callback;
507969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            mInfo = info;
508c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mCallingUid = callingUid;
509c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mResolvedUserId = resolvedUserId;
510c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            mOnFirstFrameCaptured = null;
511c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
512c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mHardware != null && mCallback != null) {
513c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                try {
514c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    mCallback.onStreamConfigChanged(getConfigsLocked());
515c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                } catch (RemoteException e) {
5164f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Slog.e(TAG, "error in Connection::resetLocked", e);
517c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
518c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
519c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
520c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
521c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void updateConfigsLocked(TvStreamConfig[] configs) {
522c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mConfigs = configs;
523c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
524c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
525969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public TvInputHardwareInfo getHardwareInfoLocked() {
526969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return mHardwareInfo;
527969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
528969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
529969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public TvInputInfo getInfoLocked() {
530c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mInfo;
531c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
532c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
533c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public ITvInputHardware getHardwareLocked() {
534c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mHardware;
535c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
536c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
537c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public TvInputHardwareImpl getHardwareImplLocked() {
538c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            return mHardware;
539c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
540c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
541c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public ITvInputHardwareCallback getCallbackLocked() {
542c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mCallback;
543c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
544c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
545c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public TvStreamConfig[] getConfigsLocked() {
546c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mConfigs;
547c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
548c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
549e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        public Integer getCallingUidLocked() {
550c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mCallingUid;
551c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
552c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
553e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        public Integer getResolvedUserIdLocked() {
554c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mResolvedUserId;
555c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
556c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
557c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public void setOnFirstFrameCapturedLocked(Runnable runnable) {
558c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            mOnFirstFrameCaptured = runnable;
559c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
560c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
561c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public Runnable getOnFirstFrameCapturedLocked() {
562c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            return mOnFirstFrameCaptured;
563c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
564c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
565c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
566c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void binderDied() {
567c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mLock) {
568969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                resetLocked(null, null, null, null, null);
569c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
570c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
571c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
572c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
573c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private class TvInputHardwareImpl extends ITvInputHardware.Stub {
574c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private final TvInputHardwareInfo mInfo;
575c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private boolean mReleased = false;
576c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private final Object mImplLock = new Object();
577c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
57806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private final AudioManager.OnAudioPortUpdateListener mAudioListener =
57906c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                new AudioManager.OnAudioPortUpdateListener() {
58006c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            @Override
58106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            public void onAudioPortListUpdate(AudioPort[] portList) {
58206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                synchronized (mImplLock) {
5837587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    updateAudioConfigLocked();
58406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
58506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
58606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
58706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            @Override
58806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            public void onAudioPatchListUpdate(AudioPatch[] patchList) {
58906c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                // No-op
59006c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
59106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
59206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            @Override
59306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            public void onServiceDied() {
59406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                synchronized (mImplLock) {
59506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                    mAudioSource = null;
59606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                    mAudioSink = null;
59706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                    mAudioPatch = null;
59806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
59906c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
60006c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        };
60106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private int mOverrideAudioType = AudioManager.DEVICE_NONE;
60206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private String mOverrideAudioAddress = "";
60306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private AudioDevicePort mAudioSource;
604ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private AudioDevicePort mAudioSink;
605d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim        private AudioPatch mAudioPatch = null;
6068e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim        private float mCommittedVolume = 0.0f;
6078e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim        private float mVolume = 0.0f;
608d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim
609839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        private TvStreamConfig mActiveConfig = null;
610839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim
611ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private int mDesiredSamplingRate = 0;
612ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
613ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT;
614ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
615c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public TvInputHardwareImpl(TvInputHardwareInfo info) {
616c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mInfo = info;
61706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            mAudioManager.registerAudioPortUpdateListener(mAudioListener);
618d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim            if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
61906c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
620ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mAudioSink = findAudioSinkFromAudioPolicy();
621ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
622ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
623ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
624ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private AudioDevicePort findAudioSinkFromAudioPolicy() {
625ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
626ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (mAudioManager.listAudioDevicePorts(devicePorts) == AudioManager.SUCCESS) {
627ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
628ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                for (AudioPort port : devicePorts) {
629ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    AudioDevicePort devicePort = (AudioDevicePort) port;
6301f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                    if ((devicePort.type() & sinkDevice) != 0) {
631ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                        return devicePort;
632d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                    }
633d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                }
634d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim            }
635ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            return null;
636ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
637ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
638ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private AudioDevicePort findAudioDevicePort(int type, String address) {
639ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (type == AudioManager.DEVICE_NONE) {
640ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                return null;
641ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
642ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
643ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
644ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                return null;
645ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
646ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            for (AudioPort port : devicePorts) {
647ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                AudioDevicePort devicePort = (AudioDevicePort) port;
648ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                if (devicePort.type() == type && devicePort.address().equals(address)) {
649ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    return devicePort;
650ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                }
651ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
652ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            return null;
653c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
654c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
655c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void release() {
656c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
65706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mAudioManager.unregisterAudioPortUpdateListener(mAudioListener);
658d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                if (mAudioPatch != null) {
659d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                    mAudioManager.releaseAudioPatch(mAudioPatch);
660d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                    mAudioPatch = null;
661d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                }
662c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                mReleased = true;
663c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
664c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
665c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
666839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        // A TvInputHardwareImpl object holds only one active session. Therefore, if a client
667839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        // attempts to call setSurface with different TvStreamConfig objects, the last call will
668839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        // prevail.
669c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
670c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public boolean setSurface(Surface surface, TvStreamConfig config)
671c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                throws RemoteException {
672c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
673c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                if (mReleased) {
674c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    throw new IllegalStateException("Device already released.");
675c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
6767587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim
677102670f16a44d4b25d7e3a252bedee0208b68c55Wonsik Kim                int result = TvInputHal.SUCCESS;
678839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                if (surface == null) {
679b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    // The value of config is ignored when surface == null.
680b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    if (mActiveConfig != null) {
681b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
682b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        mActiveConfig = null;
683b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    } else {
684b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        // We already have no active stream.
685b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        return true;
686b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    }
687839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                } else {
688b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    // It's impossible to set a non-null surface with a null config.
689b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    if (config == null) {
690b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        return false;
691b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    }
692b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    // Remove stream only if we have an existing active configuration.
693b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    if (mActiveConfig != null && !config.equals(mActiveConfig)) {
694839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
695839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        if (result != TvInputHal.SUCCESS) {
696839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                            mActiveConfig = null;
697839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        }
698839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    }
699b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    // Proceed only if all previous operations succeeded.
700839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    if (result == TvInputHal.SUCCESS) {
701b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config);
702b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        if (result == TvInputHal.SUCCESS) {
703b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                            mActiveConfig = config;
704b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        }
705839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    }
706839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                }
7077587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
708839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                return result == TvInputHal.SUCCESS;
709c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
710c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
711c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
7127587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        /**
7137587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim         * Update audio configuration (source, sink, patch) all up to current state.
7147587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim         */
7157587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        private void updateAudioConfigLocked() {
7167587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            boolean sinkUpdated = updateAudioSinkLocked();
7177587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            boolean sourceUpdated = updateAudioSourceLocked();
7187587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            // We can't do updated = updateAudioSinkLocked() || updateAudioSourceLocked() here
7197587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            // because Java won't evaluate the latter if the former is true.
7207587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim
7217587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            if (mAudioSource == null || mAudioSink == null || mActiveConfig == null) {
72206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                if (mAudioPatch != null) {
7237587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    mAudioManager.releaseAudioPatch(mAudioPatch);
7247587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    mAudioPatch = null;
72506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
72606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                return;
72706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
72806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
7298e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            AudioGainConfig sourceGainConfig = null;
7308e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            if (mAudioSource.gains().length > 0 && mVolume != mCommittedVolume) {
7318e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                AudioGain sourceGain = null;
7328e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                for (AudioGain gain : mAudioSource.gains()) {
7338e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    if ((gain.mode() & AudioGain.MODE_JOINT) != 0) {
7348e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        sourceGain = gain;
7358e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        break;
7368e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    }
7378e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                }
7388e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                // NOTE: we only change the source gain in MODE_JOINT here.
7398e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                if (sourceGain != null) {
7408e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    int steps = (sourceGain.maxValue() - sourceGain.minValue())
7418e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                            / sourceGain.stepValue();
7428e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    int gainValue = sourceGain.minValue();
7438e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    if (mVolume < 1.0f) {
7448e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        gainValue += sourceGain.stepValue() * (int) (mVolume * steps + 0.5);
7458e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    } else {
7468e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        gainValue = sourceGain.maxValue();
7478e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    }
7488e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    int numChannels = 0;
7498e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    for (int mask = sourceGain.channelMask(); mask > 0; mask >>= 1) {
7508e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        numChannels += (mask & 1);
7518e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    }
7528e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    int[] gainValues = new int[numChannels];
7538e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    Arrays.fill(gainValues, gainValue);
7548e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    sourceGainConfig = sourceGain.buildConfig(AudioGain.MODE_JOINT,
7558e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                            sourceGain.channelMask(), gainValues, 0);
7568e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                } else {
7578e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    Slog.w(TAG, "No audio source gain with MODE_JOINT support exists.");
7588e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                }
7598e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            }
7608e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim
761ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            AudioPortConfig sourceConfig = mAudioSource.activeConfig();
762ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            AudioPortConfig sinkConfig = mAudioSink.activeConfig();
763ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
7647587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
765ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (sinkConfig == null
766ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    || (mDesiredSamplingRate != 0
767ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                            && sinkConfig.samplingRate() != mDesiredSamplingRate)
768ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    || (mDesiredChannelMask != AudioFormat.CHANNEL_OUT_DEFAULT
769ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                            && sinkConfig.channelMask() != mDesiredChannelMask)
770ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    || (mDesiredFormat != AudioFormat.ENCODING_DEFAULT
771ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                            && sinkConfig.format() != mDesiredFormat)) {
772aa4b0d82a1fdb92fbf4f8852f326f2687ee55fa7Wonsik Kim                sinkConfig = mAudioSink.buildConfig(mDesiredSamplingRate, mDesiredChannelMask,
773ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                        mDesiredFormat, null);
7748e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                shouldRecreateAudioPatch = true;
775ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
7768e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            if (sourceConfig == null || sourceGainConfig != null) {
777ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                sourceConfig = mAudioSource.buildConfig(sinkConfig.samplingRate(),
7788e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        sinkConfig.channelMask(), sinkConfig.format(), sourceGainConfig);
7798e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                shouldRecreateAudioPatch = true;
7808e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            }
7818e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            if (shouldRecreateAudioPatch) {
7828e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                mCommittedVolume = mVolume;
7838e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                mAudioManager.createAudioPatch(
7848e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        audioPatchArray,
7858e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        new AudioPortConfig[] { sourceConfig },
7868e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        new AudioPortConfig[] { sinkConfig });
7878e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                mAudioPatch = audioPatchArray[0];
788ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
789ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
790ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
791c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
7928e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim        public void setStreamVolume(float volume) throws RemoteException {
793c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
794c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                if (mReleased) {
795c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    throw new IllegalStateException("Device already released.");
796c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
7978e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                mVolume = volume;
7987587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
799c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
800c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
801c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
802c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
803c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
804c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
805c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                if (mReleased) {
806c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    throw new IllegalStateException("Device already released.");
807c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
808c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
809610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim            if (mInfo.getType() != TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
810c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return false;
811c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
812c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
813c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return false;
814c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
815c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
816c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        private boolean startCapture(Surface surface, TvStreamConfig config) {
817c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            synchronized (mImplLock) {
818c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (mReleased) {
819c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
820c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
821c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (surface == null || config == null) {
822c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
823c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
824c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
825c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
826c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
827c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
8288f24a8b60f9afc1aedb89e7ee80ce65515439600Wonsik Kim                int result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config);
829c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return result == TvInputHal.SUCCESS;
830c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
831c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
832c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
833c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        private boolean stopCapture(TvStreamConfig config) {
834c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            synchronized (mImplLock) {
835c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (mReleased) {
836c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
837c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
838c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (config == null) {
839c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
840c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
841c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
842c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                int result = mHal.removeStream(mInfo.getDeviceId(), config);
843c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return result == TvInputHal.SUCCESS;
844c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
845c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
846ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
8477587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        private boolean updateAudioSourceLocked() {
84806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
8497587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                return false;
85006c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
8517587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            AudioDevicePort previousSource = mAudioSource;
8527587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
8537587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            return mAudioSource == null ? (previousSource != null)
8547587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    : !mAudioSource.equals(previousSource);
8557587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        }
8567587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim
8577587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        private boolean updateAudioSinkLocked() {
8587587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
8597587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                return false;
8607587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            }
8617587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            AudioDevicePort previousSink = mAudioSink;
86206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            if (mOverrideAudioType == AudioManager.DEVICE_NONE) {
86306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mAudioSink = findAudioSinkFromAudioPolicy();
86406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            } else {
86506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                AudioDevicePort audioSink =
86606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                        findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress);
86706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                if (audioSink != null) {
86806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                    mAudioSink = audioSink;
86906c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
87006c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
8717587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            return mAudioSink == null ? (previousSink != null) : !mAudioSink.equals(previousSink);
87206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        }
87306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
8741f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        private void handleAudioSinkUpdated() {
8751f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            synchronized (mImplLock) {
8767587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
8771f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            }
8781f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        }
8791f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang
880ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        @Override
881ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
882ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                int channelMask, int format) {
883ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            synchronized (mImplLock) {
88406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mOverrideAudioType = audioType;
88506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mOverrideAudioAddress = audioAddress;
88606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
887ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mDesiredSamplingRate = samplingRate;
888ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mDesiredChannelMask = channelMask;
889ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mDesiredFormat = format;
890ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
8917587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
892ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
893ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
894c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
895969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
896187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    interface Listener {
897187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        public void onStateChanged(String inputId, int state);
898187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        public void onHardwareDeviceAdded(TvInputHardwareInfo info);
8994f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        public void onHardwareDeviceRemoved(TvInputHardwareInfo info);
900546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        public void onHdmiDeviceAdded(HdmiDeviceInfo device);
901546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        public void onHdmiDeviceRemoved(HdmiDeviceInfo device);
902e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim        public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo device);
903187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    }
904969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
905187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    private class ListenerHandler extends Handler {
906187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        private static final int STATE_CHANGED = 1;
907187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        private static final int HARDWARE_DEVICE_ADDED = 2;
908187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        private static final int HARDWARE_DEVICE_REMOVED = 3;
909546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        private static final int HDMI_DEVICE_ADDED = 4;
910546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        private static final int HDMI_DEVICE_REMOVED = 5;
91161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang        private static final int HDMI_DEVICE_UPDATED = 6;
912969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
913969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        @Override
914969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public final void handleMessage(Message msg) {
915969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            switch (msg.what) {
916187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                case STATE_CHANGED: {
917969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    String inputId = (String) msg.obj;
918969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    int state = msg.arg1;
919187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    mListener.onStateChanged(inputId, state);
920187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
921187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
922187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                case HARDWARE_DEVICE_ADDED: {
923187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
924187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    mListener.onHardwareDeviceAdded(info);
925187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
926187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
927187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                case HARDWARE_DEVICE_REMOVED: {
9284f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
9294f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    mListener.onHardwareDeviceRemoved(info);
930187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
931187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
932546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                case HDMI_DEVICE_ADDED: {
93361f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                    HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
934546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    mListener.onHdmiDeviceAdded(info);
935187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
936187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
937546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                case HDMI_DEVICE_REMOVED: {
93861f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                    HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
939546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    mListener.onHdmiDeviceRemoved(info);
940969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    break;
941969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
94261daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                case HDMI_DEVICE_UPDATED: {
943184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
944184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    String inputId = null;
945184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    synchronized (mLock) {
946184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                        inputId = mHdmiInputIdMap.get(info.getId());
947184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    }
948184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    if (inputId != null) {
949184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                        mListener.onHdmiDeviceUpdated(inputId, info);
950184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    } else {
951184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                        Slog.w(TAG, "Could not resolve input ID matching the device info; "
952184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                                + "ignoring.");
953184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    }
954184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    break;
95561daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                }
956969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                default: {
957969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    Slog.w(TAG, "Unhandled message: " + msg);
958969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    break;
959969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
960969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
961969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
962969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
963187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
9647474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    // Listener implementations for HdmiControlService
9657474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim
9667474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub {
9677474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        @Override
9687474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        public void onReceived(HdmiHotplugEvent event) {
9697474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            synchronized (mLock) {
9707474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                mHdmiStateMap.put(event.getPort(), event.isConnected());
971d922a546b94119217fb790113d0001cad0432060Wonsik Kim                TvInputHardwareInfo hardwareInfo =
972d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        findHardwareInfoForHdmiPortLocked(event.getPort());
973d922a546b94119217fb790113d0001cad0432060Wonsik Kim                if (hardwareInfo == null) {
974d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    return;
975d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
976d922a546b94119217fb790113d0001cad0432060Wonsik Kim                String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
9777474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                if (inputId == null) {
9787474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                    return;
9797474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                }
9807474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
9817474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                        convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
9827474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            }
9837474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        }
9847474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    }
9857474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim
986187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
987187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        @Override
98861daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang        public void onStatusChanged(HdmiDeviceInfo deviceInfo, int status) {
989d922a546b94119217fb790113d0001cad0432060Wonsik Kim            synchronized (mLock) {
99061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                int messageType = 0;
991e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                Object obj = null;
99261daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                switch (status) {
99361daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    case HdmiControlManager.DEVICE_EVENT_ADD_DEVICE: {
994cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                        if (findHdmiDeviceInfo(deviceInfo.getId()) == null) {
99561daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            mHdmiDeviceList.add(deviceInfo);
99661daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        } else {
99761daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
99861daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            return;
99961daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        }
100061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        messageType = ListenerHandler.HDMI_DEVICE_ADDED;
1001e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        obj = deviceInfo;
100261daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        break;
1003d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    }
100461daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    case HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE: {
1005cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                        HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
1006cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                        if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
100761daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
100861daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            return;
100961daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        }
101061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        messageType = ListenerHandler.HDMI_DEVICE_REMOVED;
1011e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        obj = deviceInfo;
101261daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        break;
101361daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    }
101461daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    case HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE: {
1015cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                        HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
1016cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                        if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
101761daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
101861daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            return;
101961daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        }
102061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        mHdmiDeviceList.add(deviceInfo);
102161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        messageType = ListenerHandler.HDMI_DEVICE_UPDATED;
1022184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                        obj = deviceInfo;
102361daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        break;
1024d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    }
1025d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
102661daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang
1027e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                Message msg = mHandler.obtainMessage(messageType, 0, 0, obj);
1028d922a546b94119217fb790113d0001cad0432060Wonsik Kim                if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
1029d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    msg.sendToTarget();
1030d922a546b94119217fb790113d0001cad0432060Wonsik Kim                } else {
1031d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    mPendingHdmiDeviceEvents.add(msg);
1032d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
1033d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
1034187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        }
1035cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang
1036cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang        private HdmiDeviceInfo findHdmiDeviceInfo(int id) {
1037cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang            for (HdmiDeviceInfo info : mHdmiDeviceList) {
1038cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                if (info.getId() == id) {
1039cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                    return info;
1040cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                }
1041cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang            }
1042cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang            return null;
1043cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang        }
1044187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    }
10457474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim
10461f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang    private final class HdmiSystemAudioModeChangeListener extends
10471f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        IHdmiSystemAudioModeChangeListener.Stub {
10481f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        @Override
10491f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        public void onStatusChanged(boolean enabled) throws RemoteException {
10501f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            synchronized (mLock) {
10511f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                for (int i = 0; i < mConnections.size(); ++i) {
10521f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                    TvInputHardwareImpl impl = mConnections.valueAt(i).getHardwareImplLocked();
105344bb3a5bd630cfe0a4813586e840d59b9cbc9f64Jungshik Jang                    if (impl != null) {
105444bb3a5bd630cfe0a4813586e840d59b9cbc9f64Jungshik Jang                        impl.handleAudioSinkUpdated();
105544bb3a5bd630cfe0a4813586e840d59b9cbc9f64Jungshik Jang                    }
10561f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                }
10571f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            }
10581f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        }
10591f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang    }
1060c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim}
1061