TvInputHardwareManager.java revision e92f857d50d7259a4cf7ef5b88309e098338c9c1
1c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim/*
2c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * Copyright (C) 2014 The Android Open Source Project
3c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
4c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * Licensed under the Apache License, Version 2.0 (the "License");
5c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * you may not use this file except in compliance with the License.
6c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * You may obtain a copy of the License at
7c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
8c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *      http://www.apache.org/licenses/LICENSE-2.0
9c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
10c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * Unless required by applicable law or agreed to in writing, software
11c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * distributed under the License is distributed on an "AS IS" BASIS,
12c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * See the License for the specific language governing permissions and
14c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * limitations under the License.
15c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim */
16c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
17c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimpackage com.android.server.tv;
18c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
19969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
20969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
21969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
22c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.content.Context;
23b2b3151a42bd563669b222b82efb64e294dc9049Jinsuk Kimimport android.content.Intent;
2461daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jangimport android.hardware.hdmi.HdmiControlManager;
2561f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jangimport android.hardware.hdmi.HdmiDeviceInfo;
26969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.hardware.hdmi.HdmiHotplugEvent;
277474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.hardware.hdmi.IHdmiControlService;
28187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kimimport android.hardware.hdmi.IHdmiDeviceEventListener;
297474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.hardware.hdmi.IHdmiHotplugEventListener;
307474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.hardware.hdmi.IHdmiInputChangeListener;
311f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jangimport android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
32d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioDevicePort;
33ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kimimport android.media.AudioFormat;
348e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kimimport android.media.AudioGain;
358e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kimimport android.media.AudioGainConfig;
36d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioManager;
37d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioPatch;
38d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioPort;
39d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioPortConfig;
40d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardware;
41d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardwareCallback;
42b2b3151a42bd563669b222b82efb64e294dc9049Jinsuk Kimimport android.media.tv.TvContract;
43546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seoimport android.media.tv.TvInputHardwareInfo;
44969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.media.tv.TvInputInfo;
45d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvStreamConfig;
46969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.os.Handler;
47c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.os.IBinder;
48969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.os.Message;
49c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.os.RemoteException;
507474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.os.ServiceManager;
514f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Leeimport android.util.ArrayMap;
52c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.util.Slog;
53c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.util.SparseArray;
54969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.util.SparseBooleanArray;
55c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.view.KeyEvent;
56c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.view.Surface;
57c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
58e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kimimport com.android.internal.os.SomeArgs;
59969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport com.android.server.SystemService;
60969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
61c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport java.util.ArrayList;
628e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kimimport java.util.Arrays;
634f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Leeimport java.util.Collections;
64d922a546b94119217fb790113d0001cad0432060Wonsik Kimimport java.util.Iterator;
65d922a546b94119217fb790113d0001cad0432060Wonsik Kimimport java.util.LinkedList;
66c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport java.util.List;
674f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Leeimport java.util.Map;
68c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
69c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim/**
70c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * A helper class for TvInputManagerService to handle TV input hardware.
71c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
72c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * This class does a basic connection management and forwarding calls to TvInputHal which eventually
73c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * calls to tv_input HAL module.
74c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
75c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * @hide
76c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim */
777474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimclass TvInputHardwareManager implements TvInputHal.Callback {
78c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private static final String TAG = TvInputHardwareManager.class.getSimpleName();
794f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
804f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final Context mContext;
814f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final Listener mListener;
82c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private final TvInputHal mHal = new TvInputHal(this);
83d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final SparseArray<Connection> mConnections = new SparseArray<>();
84d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
85546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo    private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>();
864f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    /* A map from a device ID to the matching TV input ID. */
87d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
884f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    /* A map from a HDMI logical address to the matching TV input ID. */
89546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo    private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>();
90d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>();
914f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
92d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim    private final AudioManager mAudioManager;
934f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private IHdmiControlService mHdmiControlService;
947474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    private final IHdmiHotplugEventListener mHdmiHotplugEventListener =
957474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            new HdmiHotplugEventListener();
964f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener();
971f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang    private final IHdmiSystemAudioModeChangeListener mHdmiSystemAudioModeChangeListener =
981f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            new HdmiSystemAudioModeChangeListener();
99d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim
100d922a546b94119217fb790113d0001cad0432060Wonsik Kim    // TODO: Should handle STANDBY case.
1014f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
102d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
103969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
104187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    // Calls to mListener should happen here.
105187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    private final Handler mHandler = new ListenerHandler();
106c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
107c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private final Object mLock = new Object();
108c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
109187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    public TvInputHardwareManager(Context context, Listener listener) {
110c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        mContext = context;
111187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        mListener = listener;
112d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
113c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        mHal.init();
114969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
115969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
116969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    public void onBootPhase(int phase) {
117969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1184f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mHdmiControlService = IHdmiControlService.Stub.asInterface(ServiceManager.getService(
1194f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Context.HDMI_CONTROL_SERVICE));
1204f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (mHdmiControlService != null) {
1217474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                try {
1224f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    mHdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
1234f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    mHdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
1241f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                    mHdmiControlService.addSystemAudioModeChangeListener(
1251f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                            mHdmiSystemAudioModeChangeListener);
126546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    mHdmiDeviceList.addAll(mHdmiControlService.getInputDevices());
1277474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                } catch (RemoteException e) {
1284f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
1297474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                }
1307474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            }
131969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
132c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
133c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
134c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    @Override
1354f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) {
136c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
137c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = new Connection(info);
138c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            connection.updateConfigsLocked(configs);
139c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mConnections.put(info.getDeviceId(), connection);
140d922a546b94119217fb790113d0001cad0432060Wonsik Kim            buildHardwareListLocked();
141187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            mHandler.obtainMessage(
142187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget();
143d922a546b94119217fb790113d0001cad0432060Wonsik Kim            if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
144d922a546b94119217fb790113d0001cad0432060Wonsik Kim                processPendingHdmiDeviceEventsLocked();
145d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
146c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
147c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
148c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
149d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private void buildHardwareListLocked() {
150d922a546b94119217fb790113d0001cad0432060Wonsik Kim        mHardwareList.clear();
151c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        for (int i = 0; i < mConnections.size(); ++i) {
152d922a546b94119217fb790113d0001cad0432060Wonsik Kim            mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked());
153c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
154c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
155c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
156c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    @Override
157c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public void onDeviceUnavailable(int deviceId) {
158c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
159c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
160c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
161c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
162c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
163c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
164969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            connection.resetLocked(null, null, null, null, null);
165c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mConnections.remove(deviceId);
166d922a546b94119217fb790113d0001cad0432060Wonsik Kim            buildHardwareListLocked();
1674f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            TvInputHardwareInfo info = connection.getHardwareInfoLocked();
168d922a546b94119217fb790113d0001cad0432060Wonsik Kim            if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
169546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                // Remove HDMI devices linked with this hardware.
170546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                for (Iterator<HdmiDeviceInfo> it = mHdmiDeviceList.iterator(); it.hasNext();) {
17161f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                    HdmiDeviceInfo deviceInfo = it.next();
172d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    if (deviceInfo.getPortId() == info.getHdmiPortId()) {
173546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                        mHandler.obtainMessage(ListenerHandler.HDMI_DEVICE_REMOVED, 0, 0,
174d922a546b94119217fb790113d0001cad0432060Wonsik Kim                                deviceInfo).sendToTarget();
175d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        it.remove();
176d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    }
177d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
178d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
179187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            mHandler.obtainMessage(
1804f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    ListenerHandler.HARDWARE_DEVICE_REMOVED, 0, 0, info).sendToTarget();
181c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
182c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
183c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
184c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    @Override
185c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) {
186c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
187c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
188c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
189c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with "
190c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                        + deviceId);
191c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
192c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
193c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            connection.updateConfigsLocked(configs);
194c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
195c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                connection.getCallbackLocked().onStreamConfigChanged(configs);
196c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } catch (RemoteException e) {
1974f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                Slog.e(TAG, "error in onStreamConfigurationChanged", e);
198c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
199c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
200c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
201c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
202c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    @Override
203c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    public void onFirstFrameCaptured(int deviceId, int streamId) {
204c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        synchronized (mLock) {
205c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(deviceId);
206c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (connection == null) {
207c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Slog.e(TAG, "FirstFrameCaptured: Cannot find a connection with "
208c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        + deviceId);
209c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return;
210c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
211c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Runnable runnable = connection.getOnFirstFrameCapturedLocked();
212c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (runnable != null) {
213c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                runnable.run();
214c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                connection.setOnFirstFrameCapturedLocked(null);
215c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
216c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
217c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
218c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
219c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public List<TvInputHardwareInfo> getHardwareList() {
220c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
221d922a546b94119217fb790113d0001cad0432060Wonsik Kim            return Collections.unmodifiableList(mHardwareList);
222c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
223c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
224c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
225546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo    public List<HdmiDeviceInfo> getHdmiDeviceList() {
226d922a546b94119217fb790113d0001cad0432060Wonsik Kim        synchronized (mLock) {
227546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo            return Collections.unmodifiableList(mHdmiDeviceList);
2284f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
2294f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
2304f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
231e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim    private boolean checkUidChangedLocked(
232e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            Connection connection, int callingUid, int resolvedUserId) {
233e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        Integer connectionCallingUid = connection.getCallingUidLocked();
234e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        Integer connectionResolvedUserId = connection.getResolvedUserIdLocked();
235e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        if (connectionCallingUid == null || connectionResolvedUserId == null) {
236e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            return true;
237e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        }
238e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        if (connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId) {
239e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            return true;
240e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        }
241e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        return false;
242e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim    }
243e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim
244969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    private int convertConnectedToState(boolean connected) {
245969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (connected) {
246969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return INPUT_STATE_CONNECTED;
247969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        } else {
248969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return INPUT_STATE_DISCONNECTED;
249969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
250969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
251969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
2524f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    public void addHardwareTvInput(int deviceId, TvInputInfo info) {
253969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        synchronized (mLock) {
2544f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            String oldInputId = mHardwareInputIdMap.get(deviceId);
2554f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (oldInputId != null) {
256969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                Slog.w(TAG, "Trying to override previous registration: old = "
2574f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                        + mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
258969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        + info + ":" + deviceId);
259969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
2604f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mHardwareInputIdMap.put(deviceId, info.getId());
2614f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mInputMap.put(info.getId(), info);
262969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
263969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            for (int i = 0; i < mHdmiStateMap.size(); ++i) {
264d922a546b94119217fb790113d0001cad0432060Wonsik Kim                TvInputHardwareInfo hardwareInfo =
265d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i));
266d922a546b94119217fb790113d0001cad0432060Wonsik Kim                if (hardwareInfo == null) {
267d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    continue;
268d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
269d922a546b94119217fb790113d0001cad0432060Wonsik Kim                String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
270969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                if (inputId != null && inputId.equals(info.getId())) {
271187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
272969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                            convertConnectedToState(mHdmiStateMap.valueAt(i)), 0,
273969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                            inputId).sendToTarget();
274969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
275969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
276969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
277969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
278969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
27938feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim    private static <T> int indexOfEqualValue(SparseArray<T> map, T value) {
28038feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim        for (int i = 0; i < map.size(); ++i) {
28138feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            if (map.valueAt(i).equals(value)) {
28238feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim                return i;
28338feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            }
28438feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim        }
28538feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim        return -1;
28638feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim    }
28738feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim
288546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo    public void addHdmiTvInput(int logicalAddress, TvInputInfo info) {
2894f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        if (info.getType() != TvInputInfo.TYPE_HDMI) {
2904f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
2914f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
2924f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        synchronized (mLock) {
2934f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            String parentId = info.getParentId();
29438feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId);
29538feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            if (parentIndex < 0) {
2964f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
2974f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
298546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo            String oldInputId = mHdmiInputIdMap.get(logicalAddress);
2994f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (oldInputId != null) {
3004f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                Slog.w(TAG, "Trying to override previous registration: old = "
3014f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                        + mInputMap.get(oldInputId) + ":" + logicalAddress + ", new = "
3024f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                        + info + ":" + logicalAddress);
3034f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
304546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo            mHdmiInputIdMap.put(logicalAddress, info.getId());
3054f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mInputMap.put(info.getId(), info);
3064f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
3074f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
3084f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
3094f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    public void removeTvInput(String inputId) {
3104f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        synchronized (mLock) {
3114f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mInputMap.remove(inputId);
31238feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
3134f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (hardwareIndex >= 0) {
3144f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                mHardwareInputIdMap.removeAt(hardwareIndex);
3154f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
316546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo            int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId);
317546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo            if (deviceIndex >= 0) {
318546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                mHdmiInputIdMap.removeAt(deviceIndex);
3194f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
3204f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
3214f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
3224f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
323c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    /**
324c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * Create a TvInputHardware object with a specific deviceId. One service at a time can access
325c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * the object, and if more than one process attempts to create hardware with the same deviceId,
326c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * the latest service will get the object and all the other hardware are released. The
327c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * release is notified via ITvInputHardwareCallback.onReleased().
328c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     */
329c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
330969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            TvInputInfo info, int callingUid, int resolvedUserId) {
331c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        if (callback == null) {
332c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            throw new NullPointerException();
333c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
334c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
335c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
336c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
337c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "Invalid deviceId : " + deviceId);
338c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return null;
339c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
340e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
341969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                TvInputHardwareImpl hardware =
342969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        new TvInputHardwareImpl(connection.getHardwareInfoLocked());
343c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                try {
344c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    callback.asBinder().linkToDeath(connection, 0);
345c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                } catch (RemoteException e) {
346c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    hardware.release();
347c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    return null;
348c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
349969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId);
350c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
351c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return connection.getHardwareLocked();
352c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
353c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
354c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
355c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    /**
356c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * Release the specified hardware.
357c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     */
358c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid,
359c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            int resolvedUserId) {
360c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
361c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
362c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
363c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "Invalid deviceId : " + deviceId);
364c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
365c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
366c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection.getHardwareLocked() != hardware
367e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim                    || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
368c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
369c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
370969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            connection.resetLocked(null, null, null, null, null);
371969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
372969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
373969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
374d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private TvInputHardwareInfo findHardwareInfoForHdmiPortLocked(int port) {
375d922a546b94119217fb790113d0001cad0432060Wonsik Kim        for (TvInputHardwareInfo hardwareInfo : mHardwareList) {
376969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
377969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    && hardwareInfo.getHdmiPortId() == port) {
378d922a546b94119217fb790113d0001cad0432060Wonsik Kim                return hardwareInfo;
379969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
380969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
381969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        return null;
382969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
383969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
384c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    private int findDeviceIdForInputIdLocked(String inputId) {
385c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        for (int i = 0; i < mConnections.size(); ++i) {
386c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(i);
387c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (connection.getInfoLocked().getId().equals(inputId)) {
388c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return i;
389c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
390c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
391c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        return -1;
392c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
393c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
394c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    /**
395c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     * Get the list of TvStreamConfig which is buffered mode.
396c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     */
397c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid,
398c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int resolvedUserId) {
399c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        List<TvStreamConfig> configsList = new ArrayList<TvStreamConfig>();
400c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        synchronized (mLock) {
401c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int deviceId = findDeviceIdForInputIdLocked(inputId);
402c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (deviceId < 0) {
403c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Slog.e(TAG, "Invalid inputId : " + inputId);
404c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return configsList;
405c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
406c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(deviceId);
407c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            for (TvStreamConfig config : connection.getConfigsLocked()) {
408c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
409c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    configsList.add(config);
410c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
411c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
412c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
413c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        return configsList;
414c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
415c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
416c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    /**
417c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     * Take a snapshot of the given TV input into the provided Surface.
418c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     */
419c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config,
420c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int callingUid, int resolvedUserId) {
421c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        synchronized (mLock) {
422c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int deviceId = findDeviceIdForInputIdLocked(inputId);
423c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (deviceId < 0) {
424c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Slog.e(TAG, "Invalid inputId : " + inputId);
425c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return false;
426c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
427c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(deviceId);
428c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
429c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (hardwareImpl != null) {
430c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                // Stop previous capture.
431c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Runnable runnable = connection.getOnFirstFrameCapturedLocked();
432c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (runnable != null) {
433c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    runnable.run();
434c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    connection.setOnFirstFrameCapturedLocked(null);
435c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
436c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
437c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                boolean result = hardwareImpl.startCapture(surface, config);
438c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (result) {
439c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    connection.setOnFirstFrameCapturedLocked(new Runnable() {
440c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        @Override
441c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        public void run() {
442c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                            hardwareImpl.stopCapture(config);
443c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        }
444c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    });
445c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
446c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return result;
447c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
448c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
449c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        return false;
450c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
451c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
452d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private void processPendingHdmiDeviceEventsLocked() {
453d922a546b94119217fb790113d0001cad0432060Wonsik Kim        for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) {
454d922a546b94119217fb790113d0001cad0432060Wonsik Kim            Message msg = it.next();
45561f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang            HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
456d922a546b94119217fb790113d0001cad0432060Wonsik Kim            TvInputHardwareInfo hardwareInfo =
457d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId());
458d922a546b94119217fb790113d0001cad0432060Wonsik Kim            if (hardwareInfo != null) {
459d922a546b94119217fb790113d0001cad0432060Wonsik Kim                msg.sendToTarget();
460d922a546b94119217fb790113d0001cad0432060Wonsik Kim                it.remove();
461d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
462d922a546b94119217fb790113d0001cad0432060Wonsik Kim        }
463d922a546b94119217fb790113d0001cad0432060Wonsik Kim    }
464d922a546b94119217fb790113d0001cad0432060Wonsik Kim
465c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private class Connection implements IBinder.DeathRecipient {
466969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        private final TvInputHardwareInfo mHardwareInfo;
467969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        private TvInputInfo mInfo;
468c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private TvInputHardwareImpl mHardware = null;
469c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private ITvInputHardwareCallback mCallback;
470c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private TvStreamConfig[] mConfigs = null;
471c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private Integer mCallingUid = null;
472c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private Integer mResolvedUserId = null;
473c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        private Runnable mOnFirstFrameCaptured;
474c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
475969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public Connection(TvInputHardwareInfo hardwareInfo) {
476969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            mHardwareInfo = hardwareInfo;
477c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
478c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
479c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        // *Locked methods assume TvInputHardwareManager.mLock is held.
480c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
481969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
482969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                TvInputInfo info, Integer callingUid, Integer resolvedUserId) {
483c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mHardware != null) {
484c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                try {
485c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    mCallback.onReleased();
486c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                } catch (RemoteException e) {
4874f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Slog.e(TAG, "error in Connection::resetLocked", e);
488c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
489c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                mHardware.release();
490c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
491c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mHardware = hardware;
492c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mCallback = callback;
493969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            mInfo = info;
494c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mCallingUid = callingUid;
495c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mResolvedUserId = resolvedUserId;
496c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            mOnFirstFrameCaptured = null;
497c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
498c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mHardware != null && mCallback != null) {
499c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                try {
500c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    mCallback.onStreamConfigChanged(getConfigsLocked());
501c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                } catch (RemoteException e) {
5024f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Slog.e(TAG, "error in Connection::resetLocked", e);
503c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
504c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
505c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
506c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
507c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void updateConfigsLocked(TvStreamConfig[] configs) {
508c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mConfigs = configs;
509c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
510c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
511969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public TvInputHardwareInfo getHardwareInfoLocked() {
512969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return mHardwareInfo;
513969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
514969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
515969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public TvInputInfo getInfoLocked() {
516c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mInfo;
517c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
518c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
519c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public ITvInputHardware getHardwareLocked() {
520c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mHardware;
521c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
522c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
523c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public TvInputHardwareImpl getHardwareImplLocked() {
524c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            return mHardware;
525c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
526c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
527c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public ITvInputHardwareCallback getCallbackLocked() {
528c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mCallback;
529c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
530c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
531c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public TvStreamConfig[] getConfigsLocked() {
532c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mConfigs;
533c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
534c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
535e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        public Integer getCallingUidLocked() {
536c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mCallingUid;
537c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
538c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
539e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        public Integer getResolvedUserIdLocked() {
540c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mResolvedUserId;
541c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
542c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
543c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public void setOnFirstFrameCapturedLocked(Runnable runnable) {
544c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            mOnFirstFrameCaptured = runnable;
545c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
546c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
547c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public Runnable getOnFirstFrameCapturedLocked() {
548c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            return mOnFirstFrameCaptured;
549c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
550c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
551c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
552c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void binderDied() {
553c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mLock) {
554969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                resetLocked(null, null, null, null, null);
555c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
556c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
557c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
558c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
559c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private class TvInputHardwareImpl extends ITvInputHardware.Stub {
560c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private final TvInputHardwareInfo mInfo;
561c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private boolean mReleased = false;
562c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private final Object mImplLock = new Object();
563c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
56406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private final AudioManager.OnAudioPortUpdateListener mAudioListener =
56506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                new AudioManager.OnAudioPortUpdateListener() {
56606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            @Override
56706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            public void onAudioPortListUpdate(AudioPort[] portList) {
56806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                synchronized (mImplLock) {
5697587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    updateAudioConfigLocked();
57006c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
57106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
57206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
57306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            @Override
57406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            public void onAudioPatchListUpdate(AudioPatch[] patchList) {
57506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                // No-op
57606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
57706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
57806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            @Override
57906c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            public void onServiceDied() {
58006c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                synchronized (mImplLock) {
58106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                    mAudioSource = null;
58206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                    mAudioSink = null;
58306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                    mAudioPatch = null;
58406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
58506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
58606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        };
58706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private int mOverrideAudioType = AudioManager.DEVICE_NONE;
58806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private String mOverrideAudioAddress = "";
58906c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private AudioDevicePort mAudioSource;
590ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private AudioDevicePort mAudioSink;
591d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim        private AudioPatch mAudioPatch = null;
5928e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim        private float mCommittedVolume = 0.0f;
5938e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim        private float mVolume = 0.0f;
594d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim
595839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        private TvStreamConfig mActiveConfig = null;
596839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim
597ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private int mDesiredSamplingRate = 0;
598ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
599ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT;
600ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
601c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public TvInputHardwareImpl(TvInputHardwareInfo info) {
602c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mInfo = info;
60306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            mAudioManager.registerAudioPortUpdateListener(mAudioListener);
604d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim            if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
60506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
606ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mAudioSink = findAudioSinkFromAudioPolicy();
607ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
608ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
609ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
610ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private AudioDevicePort findAudioSinkFromAudioPolicy() {
611ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
612ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (mAudioManager.listAudioDevicePorts(devicePorts) == AudioManager.SUCCESS) {
613ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
614ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                for (AudioPort port : devicePorts) {
615ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    AudioDevicePort devicePort = (AudioDevicePort) port;
6161f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                    if ((devicePort.type() & sinkDevice) != 0) {
617ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                        return devicePort;
618d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                    }
619d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                }
620d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim            }
621ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            return null;
622ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
623ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
624ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private AudioDevicePort findAudioDevicePort(int type, String address) {
625ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (type == AudioManager.DEVICE_NONE) {
626ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                return null;
627ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
628ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
629ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
630ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                return null;
631ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
632ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            for (AudioPort port : devicePorts) {
633ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                AudioDevicePort devicePort = (AudioDevicePort) port;
634ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                if (devicePort.type() == type && devicePort.address().equals(address)) {
635ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    return devicePort;
636ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                }
637ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
638ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            return null;
639c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
640c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
641c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void release() {
642c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
64306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mAudioManager.unregisterAudioPortUpdateListener(mAudioListener);
644d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                if (mAudioPatch != null) {
645d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                    mAudioManager.releaseAudioPatch(mAudioPatch);
646d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                    mAudioPatch = null;
647d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                }
648c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                mReleased = true;
649c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
650c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
651c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
652839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        // A TvInputHardwareImpl object holds only one active session. Therefore, if a client
653839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        // attempts to call setSurface with different TvStreamConfig objects, the last call will
654839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        // prevail.
655c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
656c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public boolean setSurface(Surface surface, TvStreamConfig config)
657c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                throws RemoteException {
658c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
659c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                if (mReleased) {
660c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    throw new IllegalStateException("Device already released.");
661c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
662839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                if (surface != null && config == null) {
663839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    return false;
664839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                }
665839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                if (surface == null && mActiveConfig == null) {
666839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    return false;
667839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                }
6687587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim
669839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                int result = TvInputHal.ERROR_UNKNOWN;
670839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                if (surface == null) {
671839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
672839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    mActiveConfig = null;
673839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                } else {
674839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    if (config != mActiveConfig && mActiveConfig != null) {
675839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
676839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        if (result != TvInputHal.SUCCESS) {
677839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                            mActiveConfig = null;
678839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                            return false;
679839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        }
680839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    }
681839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    result = mHal.addStream(mInfo.getDeviceId(), surface, config);
682839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    if (result == TvInputHal.SUCCESS) {
683839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        mActiveConfig = config;
684839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    }
685839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                }
6867587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
687839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                return result == TvInputHal.SUCCESS;
688c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
689c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
690c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
6917587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        /**
6927587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim         * Update audio configuration (source, sink, patch) all up to current state.
6937587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim         */
6947587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        private void updateAudioConfigLocked() {
6957587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            boolean sinkUpdated = updateAudioSinkLocked();
6967587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            boolean sourceUpdated = updateAudioSourceLocked();
6977587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            // We can't do updated = updateAudioSinkLocked() || updateAudioSourceLocked() here
6987587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            // because Java won't evaluate the latter if the former is true.
6997587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim
7007587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            if (mAudioSource == null || mAudioSink == null || mActiveConfig == null) {
70106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                if (mAudioPatch != null) {
7027587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    mAudioManager.releaseAudioPatch(mAudioPatch);
7037587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    mAudioPatch = null;
70406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
70506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                return;
70606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
70706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
7088e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            AudioGainConfig sourceGainConfig = null;
7098e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            if (mAudioSource.gains().length > 0 && mVolume != mCommittedVolume) {
7108e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                AudioGain sourceGain = null;
7118e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                for (AudioGain gain : mAudioSource.gains()) {
7128e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    if ((gain.mode() & AudioGain.MODE_JOINT) != 0) {
7138e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        sourceGain = gain;
7148e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        break;
7158e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    }
7168e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                }
7178e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                // NOTE: we only change the source gain in MODE_JOINT here.
7188e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                if (sourceGain != null) {
7198e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    int steps = (sourceGain.maxValue() - sourceGain.minValue())
7208e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                            / sourceGain.stepValue();
7218e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    int gainValue = sourceGain.minValue();
7228e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    if (mVolume < 1.0f) {
7238e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        gainValue += sourceGain.stepValue() * (int) (mVolume * steps + 0.5);
7248e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    } else {
7258e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        gainValue = sourceGain.maxValue();
7268e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    }
7278e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    int numChannels = 0;
7288e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    for (int mask = sourceGain.channelMask(); mask > 0; mask >>= 1) {
7298e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        numChannels += (mask & 1);
7308e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    }
7318e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    int[] gainValues = new int[numChannels];
7328e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    Arrays.fill(gainValues, gainValue);
7338e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    sourceGainConfig = sourceGain.buildConfig(AudioGain.MODE_JOINT,
7348e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                            sourceGain.channelMask(), gainValues, 0);
7358e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                } else {
7368e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    Slog.w(TAG, "No audio source gain with MODE_JOINT support exists.");
7378e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                }
7388e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            }
7398e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim
740ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            AudioPortConfig sourceConfig = mAudioSource.activeConfig();
741ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            AudioPortConfig sinkConfig = mAudioSink.activeConfig();
742ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
7437587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
744ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (sinkConfig == null
745ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    || (mDesiredSamplingRate != 0
746ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                            && sinkConfig.samplingRate() != mDesiredSamplingRate)
747ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    || (mDesiredChannelMask != AudioFormat.CHANNEL_OUT_DEFAULT
748ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                            && sinkConfig.channelMask() != mDesiredChannelMask)
749ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    || (mDesiredFormat != AudioFormat.ENCODING_DEFAULT
750ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                            && sinkConfig.format() != mDesiredFormat)) {
751ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                sinkConfig = mAudioSource.buildConfig(mDesiredSamplingRate, mDesiredChannelMask,
752ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                        mDesiredFormat, null);
7538e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                shouldRecreateAudioPatch = true;
754ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
7558e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            if (sourceConfig == null || sourceGainConfig != null) {
756ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                sourceConfig = mAudioSource.buildConfig(sinkConfig.samplingRate(),
7578e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        sinkConfig.channelMask(), sinkConfig.format(), sourceGainConfig);
7588e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                shouldRecreateAudioPatch = true;
7598e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            }
7608e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            if (shouldRecreateAudioPatch) {
7618e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                mCommittedVolume = mVolume;
7628e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                mAudioManager.createAudioPatch(
7638e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        audioPatchArray,
7648e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        new AudioPortConfig[] { sourceConfig },
7658e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        new AudioPortConfig[] { sinkConfig });
7668e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                mAudioPatch = audioPatchArray[0];
767ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
768ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
769ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
770c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
7718e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim        public void setStreamVolume(float volume) throws RemoteException {
772c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
773c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                if (mReleased) {
774c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    throw new IllegalStateException("Device already released.");
775c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
7768e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                mVolume = volume;
7777587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
778c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
779c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
780c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
781c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
782c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
783c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
784c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                if (mReleased) {
785c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    throw new IllegalStateException("Device already released.");
786c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
787c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
788610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim            if (mInfo.getType() != TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
789c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return false;
790c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
791c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
792c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return false;
793c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
794c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
795c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        private boolean startCapture(Surface surface, TvStreamConfig config) {
796c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            synchronized (mImplLock) {
797c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (mReleased) {
798c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
799c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
800c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (surface == null || config == null) {
801c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
802c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
803c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
804c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
805c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
806c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
807c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                int result = mHal.addStream(mInfo.getDeviceId(), surface, config);
808c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return result == TvInputHal.SUCCESS;
809c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
810c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
811c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
812c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        private boolean stopCapture(TvStreamConfig config) {
813c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            synchronized (mImplLock) {
814c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (mReleased) {
815c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
816c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
817c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (config == null) {
818c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
819c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
820c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
821c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                int result = mHal.removeStream(mInfo.getDeviceId(), config);
822c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return result == TvInputHal.SUCCESS;
823c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
824c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
825ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
8267587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        private boolean updateAudioSourceLocked() {
82706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
8287587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                return false;
82906c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
8307587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            AudioDevicePort previousSource = mAudioSource;
8317587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
8327587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            return mAudioSource == null ? (previousSource != null)
8337587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    : !mAudioSource.equals(previousSource);
8347587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        }
8357587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim
8367587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        private boolean updateAudioSinkLocked() {
8377587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
8387587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                return false;
8397587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            }
8407587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            AudioDevicePort previousSink = mAudioSink;
84106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            if (mOverrideAudioType == AudioManager.DEVICE_NONE) {
84206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mAudioSink = findAudioSinkFromAudioPolicy();
84306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            } else {
84406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                AudioDevicePort audioSink =
84506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                        findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress);
84606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                if (audioSink != null) {
84706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                    mAudioSink = audioSink;
84806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
84906c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
8507587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            return mAudioSink == null ? (previousSink != null) : !mAudioSink.equals(previousSink);
85106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        }
85206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
8531f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        private void handleAudioSinkUpdated() {
8541f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            synchronized (mImplLock) {
8557587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
8561f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            }
8571f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        }
8581f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang
859ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        @Override
860ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
861ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                int channelMask, int format) {
862ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            synchronized (mImplLock) {
86306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mOverrideAudioType = audioType;
86406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mOverrideAudioAddress = audioAddress;
86506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
866ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mDesiredSamplingRate = samplingRate;
867ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mDesiredChannelMask = channelMask;
868ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mDesiredFormat = format;
869ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
8707587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
871ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
872ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
873c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
874969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
875187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    interface Listener {
876187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        public void onStateChanged(String inputId, int state);
877187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        public void onHardwareDeviceAdded(TvInputHardwareInfo info);
8784f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        public void onHardwareDeviceRemoved(TvInputHardwareInfo info);
879546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        public void onHdmiDeviceAdded(HdmiDeviceInfo device);
880546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        public void onHdmiDeviceRemoved(HdmiDeviceInfo device);
881e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim        public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo device);
882187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    }
883969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
884187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    private class ListenerHandler extends Handler {
885187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        private static final int STATE_CHANGED = 1;
886187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        private static final int HARDWARE_DEVICE_ADDED = 2;
887187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        private static final int HARDWARE_DEVICE_REMOVED = 3;
888546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        private static final int HDMI_DEVICE_ADDED = 4;
889546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        private static final int HDMI_DEVICE_REMOVED = 5;
89061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang        private static final int HDMI_DEVICE_UPDATED = 6;
891969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
892969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        @Override
893969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public final void handleMessage(Message msg) {
894969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            switch (msg.what) {
895187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                case STATE_CHANGED: {
896969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    String inputId = (String) msg.obj;
897969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    int state = msg.arg1;
898187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    mListener.onStateChanged(inputId, state);
899187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
900187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
901187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                case HARDWARE_DEVICE_ADDED: {
902187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
903187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    mListener.onHardwareDeviceAdded(info);
904187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
905187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
906187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                case HARDWARE_DEVICE_REMOVED: {
9074f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
9084f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    mListener.onHardwareDeviceRemoved(info);
909187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
910187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
911546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                case HDMI_DEVICE_ADDED: {
91261f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                    HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
913546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    mListener.onHdmiDeviceAdded(info);
914187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
915187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
916546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                case HDMI_DEVICE_REMOVED: {
91761f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                    HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
918546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    mListener.onHdmiDeviceRemoved(info);
919969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    break;
920969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
92161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                case HDMI_DEVICE_UPDATED: {
922e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                    SomeArgs args = (SomeArgs) msg.obj;
923e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                    String inputId = (String) args.arg1;
924e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                    HdmiDeviceInfo info = (HdmiDeviceInfo) args.arg2;
925e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                    args.recycle();
926e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                    mListener.onHdmiDeviceUpdated(inputId, info);
92761daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                }
928969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                default: {
929969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    Slog.w(TAG, "Unhandled message: " + msg);
930969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    break;
931969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
932969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
933969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
934969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
935187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
9367474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    // Listener implementations for HdmiControlService
9377474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim
9387474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub {
9397474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        @Override
9407474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        public void onReceived(HdmiHotplugEvent event) {
9417474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            synchronized (mLock) {
9427474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                mHdmiStateMap.put(event.getPort(), event.isConnected());
943d922a546b94119217fb790113d0001cad0432060Wonsik Kim                TvInputHardwareInfo hardwareInfo =
944d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        findHardwareInfoForHdmiPortLocked(event.getPort());
945d922a546b94119217fb790113d0001cad0432060Wonsik Kim                if (hardwareInfo == null) {
946d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    return;
947d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
948d922a546b94119217fb790113d0001cad0432060Wonsik Kim                String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
9497474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                if (inputId == null) {
9507474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                    return;
9517474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                }
9527474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
9537474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                        convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
9547474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            }
9557474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        }
9567474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    }
9577474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim
958187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
959187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        @Override
96061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang        public void onStatusChanged(HdmiDeviceInfo deviceInfo, int status) {
961d922a546b94119217fb790113d0001cad0432060Wonsik Kim            synchronized (mLock) {
96261daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                int messageType = 0;
963e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                Object obj = null;
96461daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                switch (status) {
96561daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    case HdmiControlManager.DEVICE_EVENT_ADD_DEVICE: {
96661daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        if (!mHdmiDeviceList.contains(deviceInfo)) {
96761daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            mHdmiDeviceList.add(deviceInfo);
96861daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        } else {
96961daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
97061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            return;
97161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        }
97261daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        messageType = ListenerHandler.HDMI_DEVICE_ADDED;
973e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        obj = deviceInfo;
97461daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        break;
975d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    }
97661daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    case HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE: {
97761daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        if (!mHdmiDeviceList.remove(deviceInfo)) {
97861daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
97961daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            return;
98061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        }
98161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        messageType = ListenerHandler.HDMI_DEVICE_REMOVED;
982e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        obj = deviceInfo;
98361daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        break;
98461daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    }
98561daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    case HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE: {
98661daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        if (!mHdmiDeviceList.remove(deviceInfo)) {
98761daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
98861daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            return;
98961daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        }
99061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        mHdmiDeviceList.add(deviceInfo);
99161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        messageType = ListenerHandler.HDMI_DEVICE_UPDATED;
992e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        String inputId = mHdmiInputIdMap.get(deviceInfo.getLogicalAddress());
993e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        SomeArgs args = SomeArgs.obtain();
994e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        args.arg1 = inputId;
995e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        args.arg2 = deviceInfo;
996e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        obj = args;
99761daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        break;
998d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    }
999d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
100061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang
1001e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                Message msg = mHandler.obtainMessage(messageType, 0, 0, obj);
1002d922a546b94119217fb790113d0001cad0432060Wonsik Kim                if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
1003d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    msg.sendToTarget();
1004d922a546b94119217fb790113d0001cad0432060Wonsik Kim                } else {
1005d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    mPendingHdmiDeviceEvents.add(msg);
1006d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
1007d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
1008187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        }
1009187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    }
10107474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim
10111f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang    private final class HdmiSystemAudioModeChangeListener extends
10121f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        IHdmiSystemAudioModeChangeListener.Stub {
10131f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        @Override
10141f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        public void onStatusChanged(boolean enabled) throws RemoteException {
10151f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            synchronized (mLock) {
10161f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                for (int i = 0; i < mConnections.size(); ++i) {
10171f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                    TvInputHardwareImpl impl = mConnections.valueAt(i).getHardwareImplLocked();
10181f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                    impl.handleAudioSinkUpdated();
10191f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                }
10201f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            }
10211f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        }
10221f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang    }
1023c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim}
1024