TvInputHardwareManager.java revision ca17a9029dd7b1b87c155e68476eae5ee4efbede
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;
24187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kimimport android.hardware.hdmi.HdmiCecDeviceInfo;
25969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.hardware.hdmi.HdmiHotplugEvent;
267474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.hardware.hdmi.IHdmiControlService;
27187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kimimport android.hardware.hdmi.IHdmiDeviceEventListener;
287474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.hardware.hdmi.IHdmiHotplugEventListener;
297474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.hardware.hdmi.IHdmiInputChangeListener;
30d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioDevicePort;
31ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kimimport android.media.AudioFormat;
32d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioManager;
33d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioPatch;
34d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioPort;
35d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioPortConfig;
36d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardware;
37d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardwareCallback;
38d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvInputHardwareInfo;
39b2b3151a42bd563669b222b82efb64e294dc9049Jinsuk Kimimport android.media.tv.TvContract;
40969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.media.tv.TvInputInfo;
41d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvStreamConfig;
42969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.os.Handler;
43c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.os.IBinder;
44969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.os.Looper;
45969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.os.Message;
46c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.os.RemoteException;
477474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.os.ServiceManager;
484f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Leeimport android.util.ArrayMap;
49c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.util.Slog;
50c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.util.SparseArray;
51969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.util.SparseBooleanArray;
52c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.view.KeyEvent;
53c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.view.Surface;
54c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
55969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport com.android.server.SystemService;
56969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
57c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport java.util.ArrayList;
584f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Leeimport java.util.Collections;
59c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport java.util.HashSet;
60d922a546b94119217fb790113d0001cad0432060Wonsik Kimimport java.util.Iterator;
61d922a546b94119217fb790113d0001cad0432060Wonsik Kimimport java.util.LinkedList;
62c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport java.util.List;
634f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Leeimport java.util.Map;
64c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport java.util.Set;
65c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
66c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim/**
67c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * A helper class for TvInputManagerService to handle TV input hardware.
68c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
69c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * This class does a basic connection management and forwarding calls to TvInputHal which eventually
70c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * calls to tv_input HAL module.
71c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
72c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * @hide
73c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim */
747474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimclass TvInputHardwareManager implements TvInputHal.Callback {
75c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private static final String TAG = TvInputHardwareManager.class.getSimpleName();
764f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
774f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final Context mContext;
784f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final Listener mListener;
79c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private final TvInputHal mHal = new TvInputHal(this);
80d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final SparseArray<Connection> mConnections = new SparseArray<>();
81d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
82d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private List<HdmiCecDeviceInfo> mHdmiCecDeviceList = new LinkedList<>();
834f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    /* A map from a device ID to the matching TV input ID. */
84d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
854f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    /* A map from a HDMI logical address to the matching TV input ID. */
86d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final SparseArray<String> mHdmiCecInputIdMap = new SparseArray<>();
87d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>();
884f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
89d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim    private final AudioManager mAudioManager;
904f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private IHdmiControlService mHdmiControlService;
917474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    private final IHdmiHotplugEventListener mHdmiHotplugEventListener =
927474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            new HdmiHotplugEventListener();
934f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener();
947474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    private final IHdmiInputChangeListener mHdmiInputChangeListener = new HdmiInputChangeListener();
95d922a546b94119217fb790113d0001cad0432060Wonsik Kim    // TODO: Should handle STANDBY case.
964f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
97d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
98969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
99187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    // Calls to mListener should happen here.
100187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    private final Handler mHandler = new ListenerHandler();
101c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
102c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private final Object mLock = new Object();
103c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
104187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    public TvInputHardwareManager(Context context, Listener listener) {
105c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        mContext = context;
106187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        mListener = listener;
107d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
108c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        mHal.init();
109969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
110969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
111969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    public void onBootPhase(int phase) {
112969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1134f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mHdmiControlService = IHdmiControlService.Stub.asInterface(ServiceManager.getService(
1144f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Context.HDMI_CONTROL_SERVICE));
1154f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (mHdmiControlService != null) {
1167474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                try {
1174f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    mHdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
1184f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    mHdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
119d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    mHdmiCecDeviceList.addAll(mHdmiControlService.getInputDevices());
1204f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    mHdmiControlService.setInputChangeListener(mHdmiInputChangeListener);
1217474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                } catch (RemoteException e) {
1224f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
1237474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                }
1247474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            }
125969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
126c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
127c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
128c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    @Override
1294f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) {
130c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
131c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = new Connection(info);
132c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            connection.updateConfigsLocked(configs);
133c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mConnections.put(info.getDeviceId(), connection);
134d922a546b94119217fb790113d0001cad0432060Wonsik Kim            buildHardwareListLocked();
135187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            mHandler.obtainMessage(
136187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget();
137d922a546b94119217fb790113d0001cad0432060Wonsik Kim            if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
138d922a546b94119217fb790113d0001cad0432060Wonsik Kim                processPendingHdmiDeviceEventsLocked();
139d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
140c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
141c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
142c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
143d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private void buildHardwareListLocked() {
144d922a546b94119217fb790113d0001cad0432060Wonsik Kim        mHardwareList.clear();
145c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        for (int i = 0; i < mConnections.size(); ++i) {
146d922a546b94119217fb790113d0001cad0432060Wonsik Kim            mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked());
147c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
148c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
149c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
150c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    @Override
151c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public void onDeviceUnavailable(int deviceId) {
152c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
153c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
154c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
155c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
156c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
157c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
158969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            connection.resetLocked(null, null, null, null, null);
159c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mConnections.remove(deviceId);
160d922a546b94119217fb790113d0001cad0432060Wonsik Kim            buildHardwareListLocked();
1614f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            TvInputHardwareInfo info = connection.getHardwareInfoLocked();
162d922a546b94119217fb790113d0001cad0432060Wonsik Kim            if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
163d922a546b94119217fb790113d0001cad0432060Wonsik Kim                // Remove HDMI CEC devices linked with this hardware.
164d922a546b94119217fb790113d0001cad0432060Wonsik Kim                for (Iterator<HdmiCecDeviceInfo> it = mHdmiCecDeviceList.iterator();
165d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        it.hasNext(); ) {
166d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    HdmiCecDeviceInfo deviceInfo = it.next();
167d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    if (deviceInfo.getPortId() == info.getHdmiPortId()) {
168d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        mHandler.obtainMessage(ListenerHandler.HDMI_CEC_DEVICE_REMOVED, 0, 0,
169d922a546b94119217fb790113d0001cad0432060Wonsik Kim                                deviceInfo).sendToTarget();
170d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        it.remove();
171d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    }
172d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
173d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
174187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            mHandler.obtainMessage(
1754f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    ListenerHandler.HARDWARE_DEVICE_REMOVED, 0, 0, info).sendToTarget();
176c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
177c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
178c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
179c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    @Override
180c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) {
181c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
182c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
183c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
184c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with "
185c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                        + deviceId);
186c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
187c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
188c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            connection.updateConfigsLocked(configs);
189c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            try {
190c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                connection.getCallbackLocked().onStreamConfigChanged(configs);
191c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            } catch (RemoteException e) {
1924f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                Slog.e(TAG, "error in onStreamConfigurationChanged", e);
193c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
194c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
195c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
196c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
197c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    @Override
198c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    public void onFirstFrameCaptured(int deviceId, int streamId) {
199c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        synchronized (mLock) {
200c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(deviceId);
201c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (connection == null) {
202c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Slog.e(TAG, "FirstFrameCaptured: Cannot find a connection with "
203c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        + deviceId);
204c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return;
205c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
206c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Runnable runnable = connection.getOnFirstFrameCapturedLocked();
207c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (runnable != null) {
208c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                runnable.run();
209c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                connection.setOnFirstFrameCapturedLocked(null);
210c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
211c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
212c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
213c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
214c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public List<TvInputHardwareInfo> getHardwareList() {
215c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
216d922a546b94119217fb790113d0001cad0432060Wonsik Kim            return Collections.unmodifiableList(mHardwareList);
217c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
218c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
219c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
2204f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    public List<HdmiCecDeviceInfo> getHdmiCecInputDeviceList() {
221d922a546b94119217fb790113d0001cad0432060Wonsik Kim        synchronized (mLock) {
222d922a546b94119217fb790113d0001cad0432060Wonsik Kim            return Collections.unmodifiableList(mHdmiCecDeviceList);
2234f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
2244f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
2254f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
226e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim    private boolean checkUidChangedLocked(
227e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            Connection connection, int callingUid, int resolvedUserId) {
228e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        Integer connectionCallingUid = connection.getCallingUidLocked();
229e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        Integer connectionResolvedUserId = connection.getResolvedUserIdLocked();
230e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        if (connectionCallingUid == null || connectionResolvedUserId == null) {
231e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            return true;
232e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        }
233e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        if (connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId) {
234e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            return true;
235e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        }
236e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        return false;
237e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim    }
238e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim
239969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    private int convertConnectedToState(boolean connected) {
240969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (connected) {
241969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return INPUT_STATE_CONNECTED;
242969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        } else {
243969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return INPUT_STATE_DISCONNECTED;
244969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
245969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
246969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
2474f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    public void addHardwareTvInput(int deviceId, TvInputInfo info) {
248969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        synchronized (mLock) {
2494f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            String oldInputId = mHardwareInputIdMap.get(deviceId);
2504f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (oldInputId != null) {
251969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                Slog.w(TAG, "Trying to override previous registration: old = "
2524f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                        + mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
253969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        + info + ":" + deviceId);
254969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
2554f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mHardwareInputIdMap.put(deviceId, info.getId());
2564f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mInputMap.put(info.getId(), info);
257969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
258969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            for (int i = 0; i < mHdmiStateMap.size(); ++i) {
259d922a546b94119217fb790113d0001cad0432060Wonsik Kim                TvInputHardwareInfo hardwareInfo =
260d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i));
261d922a546b94119217fb790113d0001cad0432060Wonsik Kim                if (hardwareInfo == null) {
262d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    continue;
263d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
264d922a546b94119217fb790113d0001cad0432060Wonsik Kim                String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
265969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                if (inputId != null && inputId.equals(info.getId())) {
266187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
267969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                            convertConnectedToState(mHdmiStateMap.valueAt(i)), 0,
268969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                            inputId).sendToTarget();
269969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
270969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
271969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
272969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
273969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
27438feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim    private static <T> int indexOfEqualValue(SparseArray<T> map, T value) {
27538feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim        for (int i = 0; i < map.size(); ++i) {
27638feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            if (map.valueAt(i).equals(value)) {
27738feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim                return i;
27838feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            }
27938feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim        }
28038feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim        return -1;
28138feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim    }
28238feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim
2834f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    public void addHdmiCecTvInput(int logicalAddress, TvInputInfo info) {
2844f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        if (info.getType() != TvInputInfo.TYPE_HDMI) {
2854f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
2864f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
2874f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        synchronized (mLock) {
2884f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            String parentId = info.getParentId();
28938feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId);
29038feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            if (parentIndex < 0) {
2914f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
2924f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
2934f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            String oldInputId = mHdmiCecInputIdMap.get(logicalAddress);
2944f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (oldInputId != null) {
2954f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                Slog.w(TAG, "Trying to override previous registration: old = "
2964f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                        + mInputMap.get(oldInputId) + ":" + logicalAddress + ", new = "
2974f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                        + info + ":" + logicalAddress);
2984f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
2994f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mHdmiCecInputIdMap.put(logicalAddress, info.getId());
3004f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mInputMap.put(info.getId(), info);
3014f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
3024f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
3034f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
3044f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    public void removeTvInput(String inputId) {
3054f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        synchronized (mLock) {
3064f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mInputMap.remove(inputId);
30738feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
3084f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (hardwareIndex >= 0) {
3094f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                mHardwareInputIdMap.removeAt(hardwareIndex);
3104f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
31138feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            int cecIndex = indexOfEqualValue(mHdmiCecInputIdMap, inputId);
3124f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (cecIndex >= 0) {
3134f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                mHdmiCecInputIdMap.removeAt(cecIndex);
3144f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
3154f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
3164f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
3174f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
318c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    /**
319c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * Create a TvInputHardware object with a specific deviceId. One service at a time can access
320c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * the object, and if more than one process attempts to create hardware with the same deviceId,
321c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * the latest service will get the object and all the other hardware are released. The
322c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * release is notified via ITvInputHardwareCallback.onReleased().
323c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     */
324c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
325969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            TvInputInfo info, int callingUid, int resolvedUserId) {
326c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        if (callback == null) {
327c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            throw new NullPointerException();
328c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
329c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
330c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
331c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
332c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "Invalid deviceId : " + deviceId);
333c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return null;
334c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
335e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
336969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                TvInputHardwareImpl hardware =
337969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        new TvInputHardwareImpl(connection.getHardwareInfoLocked());
338c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                try {
339c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    callback.asBinder().linkToDeath(connection, 0);
340c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                } catch (RemoteException e) {
341c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    hardware.release();
342c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    return null;
343c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
344969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId);
345c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
346c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return connection.getHardwareLocked();
347c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
348c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
349c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
350c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    /**
351c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * Release the specified hardware.
352c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     */
353c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid,
354c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            int resolvedUserId) {
355c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
356c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
357c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
358c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "Invalid deviceId : " + deviceId);
359c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
360c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
361c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection.getHardwareLocked() != hardware
362e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim                    || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
363c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
364c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
365969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            connection.resetLocked(null, null, null, null, null);
366969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
367969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
368969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
369d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private TvInputHardwareInfo findHardwareInfoForHdmiPortLocked(int port) {
370d922a546b94119217fb790113d0001cad0432060Wonsik Kim        for (TvInputHardwareInfo hardwareInfo : mHardwareList) {
371969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
372969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    && hardwareInfo.getHdmiPortId() == port) {
373d922a546b94119217fb790113d0001cad0432060Wonsik Kim                return hardwareInfo;
374969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
375969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
376969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        return null;
377969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
378969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
379c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    private int findDeviceIdForInputIdLocked(String inputId) {
380c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        for (int i = 0; i < mConnections.size(); ++i) {
381c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(i);
382c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (connection.getInfoLocked().getId().equals(inputId)) {
383c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return i;
384c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
385c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
386c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        return -1;
387c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
388c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
389c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    /**
390c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     * Get the list of TvStreamConfig which is buffered mode.
391c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     */
392c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid,
393c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int resolvedUserId) {
394c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        List<TvStreamConfig> configsList = new ArrayList<TvStreamConfig>();
395c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        synchronized (mLock) {
396c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int deviceId = findDeviceIdForInputIdLocked(inputId);
397c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (deviceId < 0) {
398c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Slog.e(TAG, "Invalid inputId : " + inputId);
399c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return configsList;
400c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
401c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(deviceId);
402c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            for (TvStreamConfig config : connection.getConfigsLocked()) {
403c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
404c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    configsList.add(config);
405c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
406c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
407c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
408c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        return configsList;
409c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
410c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
411c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    /**
412c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     * Take a snapshot of the given TV input into the provided Surface.
413c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     */
414c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config,
415c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int callingUid, int resolvedUserId) {
416c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        synchronized (mLock) {
417c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int deviceId = findDeviceIdForInputIdLocked(inputId);
418c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (deviceId < 0) {
419c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Slog.e(TAG, "Invalid inputId : " + inputId);
420c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return false;
421c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
422c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(deviceId);
423c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
424c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (hardwareImpl != null) {
425c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                // Stop previous capture.
426c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Runnable runnable = connection.getOnFirstFrameCapturedLocked();
427c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (runnable != null) {
428c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    runnable.run();
429c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    connection.setOnFirstFrameCapturedLocked(null);
430c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
431c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
432c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                boolean result = hardwareImpl.startCapture(surface, config);
433c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (result) {
434c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    connection.setOnFirstFrameCapturedLocked(new Runnable() {
435c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        @Override
436c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        public void run() {
437c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                            hardwareImpl.stopCapture(config);
438c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        }
439c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    });
440c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
441c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return result;
442c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
443c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
444c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        return false;
445c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
446c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
447d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private void processPendingHdmiDeviceEventsLocked() {
448d922a546b94119217fb790113d0001cad0432060Wonsik Kim        for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) {
449d922a546b94119217fb790113d0001cad0432060Wonsik Kim            Message msg = it.next();
450d922a546b94119217fb790113d0001cad0432060Wonsik Kim            HdmiCecDeviceInfo deviceInfo = (HdmiCecDeviceInfo) msg.obj;
451d922a546b94119217fb790113d0001cad0432060Wonsik Kim            TvInputHardwareInfo hardwareInfo =
452d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId());
453d922a546b94119217fb790113d0001cad0432060Wonsik Kim            if (hardwareInfo != null) {
454d922a546b94119217fb790113d0001cad0432060Wonsik Kim                msg.sendToTarget();
455d922a546b94119217fb790113d0001cad0432060Wonsik Kim                it.remove();
456d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
457d922a546b94119217fb790113d0001cad0432060Wonsik Kim        }
458d922a546b94119217fb790113d0001cad0432060Wonsik Kim    }
459d922a546b94119217fb790113d0001cad0432060Wonsik Kim
460c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private class Connection implements IBinder.DeathRecipient {
461969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        private final TvInputHardwareInfo mHardwareInfo;
462969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        private TvInputInfo mInfo;
463c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private TvInputHardwareImpl mHardware = null;
464c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private ITvInputHardwareCallback mCallback;
465c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private TvStreamConfig[] mConfigs = null;
466c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private Integer mCallingUid = null;
467c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private Integer mResolvedUserId = null;
468c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        private Runnable mOnFirstFrameCaptured;
469c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
470969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public Connection(TvInputHardwareInfo hardwareInfo) {
471969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            mHardwareInfo = hardwareInfo;
472c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
473c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
474c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        // *Locked methods assume TvInputHardwareManager.mLock is held.
475c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
476969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
477969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                TvInputInfo info, Integer callingUid, Integer resolvedUserId) {
478c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mHardware != null) {
479c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                try {
480c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    mCallback.onReleased();
481c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                } catch (RemoteException e) {
4824f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Slog.e(TAG, "error in Connection::resetLocked", e);
483c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
484c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                mHardware.release();
485c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
486c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mHardware = hardware;
487c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mCallback = callback;
488969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            mInfo = info;
489c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mCallingUid = callingUid;
490c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mResolvedUserId = resolvedUserId;
491c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            mOnFirstFrameCaptured = null;
492c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
493c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mHardware != null && mCallback != null) {
494c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                try {
495c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    mCallback.onStreamConfigChanged(getConfigsLocked());
496c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                } catch (RemoteException e) {
4974f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Slog.e(TAG, "error in Connection::resetLocked", e);
498c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
499c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
500c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
501c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
502c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void updateConfigsLocked(TvStreamConfig[] configs) {
503c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mConfigs = configs;
504c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
505c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
506969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public TvInputHardwareInfo getHardwareInfoLocked() {
507969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return mHardwareInfo;
508969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
509969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
510969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public TvInputInfo getInfoLocked() {
511c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mInfo;
512c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
513c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
514c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public ITvInputHardware getHardwareLocked() {
515c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mHardware;
516c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
517c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
518c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public TvInputHardwareImpl getHardwareImplLocked() {
519c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            return mHardware;
520c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
521c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
522c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public ITvInputHardwareCallback getCallbackLocked() {
523c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mCallback;
524c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
525c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
526c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public TvStreamConfig[] getConfigsLocked() {
527c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mConfigs;
528c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
529c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
530e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        public Integer getCallingUidLocked() {
531c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mCallingUid;
532c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
533c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
534e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        public Integer getResolvedUserIdLocked() {
535c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mResolvedUserId;
536c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
537c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
538c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public void setOnFirstFrameCapturedLocked(Runnable runnable) {
539c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            mOnFirstFrameCaptured = runnable;
540c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
541c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
542c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public Runnable getOnFirstFrameCapturedLocked() {
543c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            return mOnFirstFrameCaptured;
544c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
545c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
546c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
547c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void binderDied() {
548c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mLock) {
549969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                resetLocked(null, null, null, null, null);
550c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
551c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
552c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
553c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
554c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private class TvInputHardwareImpl extends ITvInputHardware.Stub {
555c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private final TvInputHardwareInfo mInfo;
556c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private boolean mReleased = false;
557c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private final Object mImplLock = new Object();
558c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
559d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim        private final AudioDevicePort mAudioSource;
560ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private AudioDevicePort mAudioSink;
561d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim        private AudioPatch mAudioPatch = null;
562d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim
563839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        private TvStreamConfig mActiveConfig = null;
564839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim
565ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private int mDesiredSamplingRate = 0;
566ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
567ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT;
568ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
569c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public TvInputHardwareImpl(TvInputHardwareInfo info) {
570c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mInfo = info;
571d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim            AudioDevicePort audioSource = null;
572d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim            if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
573ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                audioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
574ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mAudioSink = findAudioSinkFromAudioPolicy();
575ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
576ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            mAudioSource = audioSource;
577ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
578ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
579ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private AudioDevicePort findAudioSinkFromAudioPolicy() {
580ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
581ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (mAudioManager.listAudioDevicePorts(devicePorts) == AudioManager.SUCCESS) {
582ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
583ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                for (AudioPort port : devicePorts) {
584ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    AudioDevicePort devicePort = (AudioDevicePort) port;
585ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    if (devicePort.type() == sinkDevice) {
586ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                        return devicePort;
587d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                    }
588d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                }
589d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim            }
590ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            return null;
591ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
592ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
593ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private AudioDevicePort findAudioDevicePort(int type, String address) {
594ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (type == AudioManager.DEVICE_NONE) {
595ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                return null;
596ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
597ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
598ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
599ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                return null;
600ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
601ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            for (AudioPort port : devicePorts) {
602ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                AudioDevicePort devicePort = (AudioDevicePort) port;
603ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                if (devicePort.type() == type && devicePort.address().equals(address)) {
604ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    return devicePort;
605ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                }
606ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
607ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            return null;
608c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
609c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
610c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void release() {
611c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
612d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                if (mAudioPatch != null) {
613d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                    mAudioManager.releaseAudioPatch(mAudioPatch);
614d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                    mAudioPatch = null;
615d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                }
616c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                mReleased = true;
617c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
618c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
619c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
620839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        // A TvInputHardwareImpl object holds only one active session. Therefore, if a client
621839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        // attempts to call setSurface with different TvStreamConfig objects, the last call will
622839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        // prevail.
623c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
624c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public boolean setSurface(Surface surface, TvStreamConfig config)
625c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                throws RemoteException {
626c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
627c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                if (mReleased) {
628c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    throw new IllegalStateException("Device already released.");
629c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
630839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                if (surface != null && config == null) {
631839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    return false;
632839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                }
633839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                if (surface == null && mActiveConfig == null) {
634839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    return false;
635839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                }
636d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                if (mAudioSource != null && mAudioSink != null) {
637d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                    if (surface != null) {
638ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                        updateAudioPatchLocked();
639d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                    } else {
640d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                        mAudioManager.releaseAudioPatch(mAudioPatch);
641d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                        mAudioPatch = null;
642d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                    }
643d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                }
644839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                int result = TvInputHal.ERROR_UNKNOWN;
645839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                if (surface == null) {
646839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
647839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    mActiveConfig = null;
648839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                } else {
649839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    if (config != mActiveConfig && mActiveConfig != null) {
650839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
651839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        if (result != TvInputHal.SUCCESS) {
652839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                            mActiveConfig = null;
653839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                            return false;
654839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        }
655839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    }
656839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    result = mHal.addStream(mInfo.getDeviceId(), surface, config);
657839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    if (result == TvInputHal.SUCCESS) {
658839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        mActiveConfig = config;
659839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    }
660839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                }
661839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                return result == TvInputHal.SUCCESS;
662c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
663c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
664c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
665ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private void updateAudioPatchLocked() {
666ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            AudioPortConfig sourceConfig = mAudioSource.activeConfig();
667ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            AudioPortConfig sinkConfig = mAudioSink.activeConfig();
668ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
669ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (sinkConfig == null
670ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    || (mDesiredSamplingRate != 0
671ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                            && sinkConfig.samplingRate() != mDesiredSamplingRate)
672ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    || (mDesiredChannelMask != AudioFormat.CHANNEL_OUT_DEFAULT
673ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                            && sinkConfig.channelMask() != mDesiredChannelMask)
674ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    || (mDesiredFormat != AudioFormat.ENCODING_DEFAULT
675ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                            && sinkConfig.format() != mDesiredFormat)) {
676ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                sinkConfig = mAudioSource.buildConfig(mDesiredSamplingRate, mDesiredChannelMask,
677ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                        mDesiredFormat, null);
678ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
679ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (sourceConfig == null) {
680ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                sourceConfig = mAudioSource.buildConfig(sinkConfig.samplingRate(),
681ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                        sinkConfig.channelMask(), sinkConfig.format(), null);
682ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
683ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            mAudioManager.createAudioPatch(
684ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    audioPatchArray,
685ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    new AudioPortConfig[] { sourceConfig },
686ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    new AudioPortConfig[] { sinkConfig });
687ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            mAudioPatch = audioPatchArray[0];
688ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
689ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
690c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
691c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void setVolume(float volume) throws RemoteException {
692c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
693c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                if (mReleased) {
694c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    throw new IllegalStateException("Device already released.");
695c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
696c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
697d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim            // TODO: Use AudioGain?
698c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
699c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
700c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
701c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
702c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
703c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                if (mReleased) {
704c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    throw new IllegalStateException("Device already released.");
705c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
706c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
707610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim            if (mInfo.getType() != TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
708c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return false;
709c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
710c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
711c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return false;
712c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
713c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
714c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        private boolean startCapture(Surface surface, TvStreamConfig config) {
715c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            synchronized (mImplLock) {
716c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (mReleased) {
717c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
718c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
719c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (surface == null || config == null) {
720c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
721c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
722c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
723c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
724c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
725c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
726c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                int result = mHal.addStream(mInfo.getDeviceId(), surface, config);
727c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return result == TvInputHal.SUCCESS;
728c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
729c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
730c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
731c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        private boolean stopCapture(TvStreamConfig config) {
732c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            synchronized (mImplLock) {
733c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (mReleased) {
734c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
735c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
736c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (config == null) {
737c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
738c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
739c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
740c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                int result = mHal.removeStream(mInfo.getDeviceId(), config);
741c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return result == TvInputHal.SUCCESS;
742c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
743c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
744ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
745ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        @Override
746ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
747ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                int channelMask, int format) {
748ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            synchronized (mImplLock) {
749ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                if (audioType == AudioManager.DEVICE_NONE) {
750ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    mAudioSink = findAudioSinkFromAudioPolicy();
751ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                } else {
752ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    AudioDevicePort audioSink = findAudioDevicePort(audioType, audioAddress);
753ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    if (audioSink != null) {
754ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                        mAudioSink = audioSink;
755ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    }
756ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                }
757ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mDesiredSamplingRate = samplingRate;
758ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mDesiredChannelMask = channelMask;
759ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mDesiredFormat = format;
760ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
761ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                if (mAudioPatch != null) {
762ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    updateAudioPatchLocked();
763ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                }
764ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
765ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
766c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
767969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
768187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    interface Listener {
769187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        public void onStateChanged(String inputId, int state);
770187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        public void onHardwareDeviceAdded(TvInputHardwareInfo info);
7714f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        public void onHardwareDeviceRemoved(TvInputHardwareInfo info);
772187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        public void onHdmiCecDeviceAdded(HdmiCecDeviceInfo cecDevice);
773187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        public void onHdmiCecDeviceRemoved(HdmiCecDeviceInfo cecDevice);
774187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    }
775969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
776187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    private class ListenerHandler extends Handler {
777187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        private static final int STATE_CHANGED = 1;
778187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        private static final int HARDWARE_DEVICE_ADDED = 2;
779187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        private static final int HARDWARE_DEVICE_REMOVED = 3;
7804f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        private static final int HDMI_CEC_DEVICE_ADDED = 4;
7814f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        private static final int HDMI_CEC_DEVICE_REMOVED = 5;
782969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
783969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        @Override
784969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public final void handleMessage(Message msg) {
785969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            switch (msg.what) {
786187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                case STATE_CHANGED: {
787969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    String inputId = (String) msg.obj;
788969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    int state = msg.arg1;
789187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    mListener.onStateChanged(inputId, state);
790187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
791187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
792187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                case HARDWARE_DEVICE_ADDED: {
793187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
794187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    mListener.onHardwareDeviceAdded(info);
795187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
796187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
797187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                case HARDWARE_DEVICE_REMOVED: {
7984f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
7994f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    mListener.onHardwareDeviceRemoved(info);
800187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
801187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
8024f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                case HDMI_CEC_DEVICE_ADDED: {
803187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    HdmiCecDeviceInfo info = (HdmiCecDeviceInfo) msg.obj;
804187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    mListener.onHdmiCecDeviceAdded(info);
805187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
806187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
8074f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                case HDMI_CEC_DEVICE_REMOVED: {
808187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    HdmiCecDeviceInfo info = (HdmiCecDeviceInfo) msg.obj;
809187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    mListener.onHdmiCecDeviceRemoved(info);
810969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    break;
811969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
812969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                default: {
813969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    Slog.w(TAG, "Unhandled message: " + msg);
814969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    break;
815969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
816969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
817969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
818969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
819187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
8207474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    // Listener implementations for HdmiControlService
8217474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim
8227474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub {
8237474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        @Override
8247474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        public void onReceived(HdmiHotplugEvent event) {
8257474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            synchronized (mLock) {
8267474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                mHdmiStateMap.put(event.getPort(), event.isConnected());
827d922a546b94119217fb790113d0001cad0432060Wonsik Kim                TvInputHardwareInfo hardwareInfo =
828d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        findHardwareInfoForHdmiPortLocked(event.getPort());
829d922a546b94119217fb790113d0001cad0432060Wonsik Kim                if (hardwareInfo == null) {
830d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    return;
831d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
832d922a546b94119217fb790113d0001cad0432060Wonsik Kim                String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
8337474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                if (inputId == null) {
8347474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                    return;
8357474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                }
8367474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
8377474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                        convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
8387474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            }
8397474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        }
8407474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    }
8417474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim
842187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
843187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        @Override
844187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        public void onStatusChanged(HdmiCecDeviceInfo deviceInfo, boolean activated) {
845d922a546b94119217fb790113d0001cad0432060Wonsik Kim            synchronized (mLock) {
846d922a546b94119217fb790113d0001cad0432060Wonsik Kim                if (activated) {
847d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    if (!mHdmiCecDeviceList.contains(deviceInfo)) {
848d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        mHdmiCecDeviceList.add(deviceInfo);
849d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    } else {
850d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
851d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        return;
852d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    }
853d922a546b94119217fb790113d0001cad0432060Wonsik Kim                } else {
854d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    if (!mHdmiCecDeviceList.remove(deviceInfo)) {
855d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
856d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        return;
857d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    }
858d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
859d922a546b94119217fb790113d0001cad0432060Wonsik Kim                Message msg = mHandler.obtainMessage(
860d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        activated ? ListenerHandler.HDMI_CEC_DEVICE_ADDED
861d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        : ListenerHandler.HDMI_CEC_DEVICE_REMOVED,
862d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        0, 0, deviceInfo);
863d922a546b94119217fb790113d0001cad0432060Wonsik Kim                if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
864d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    msg.sendToTarget();
865d922a546b94119217fb790113d0001cad0432060Wonsik Kim                } else {
866d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    mPendingHdmiDeviceEvents.add(msg);
867d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
868d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
869187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        }
870187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    }
8717474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim
8727474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    private final class HdmiInputChangeListener extends IHdmiInputChangeListener.Stub {
8737474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        @Override
8747474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        public void onChanged(HdmiCecDeviceInfo device) throws RemoteException {
875b2b3151a42bd563669b222b82efb64e294dc9049Jinsuk Kim            String inputId;
876b2b3151a42bd563669b222b82efb64e294dc9049Jinsuk Kim            synchronized (mLock) {
877b2b3151a42bd563669b222b82efb64e294dc9049Jinsuk Kim                if (device.isCecDevice()) {
878b2b3151a42bd563669b222b82efb64e294dc9049Jinsuk Kim                    inputId = mHdmiCecInputIdMap.get(device.getLogicalAddress());
879b2b3151a42bd563669b222b82efb64e294dc9049Jinsuk Kim                } else {
880d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    TvInputHardwareInfo hardwareInfo =
881d922a546b94119217fb790113d0001cad0432060Wonsik Kim                            findHardwareInfoForHdmiPortLocked(device.getPortId());
882d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    inputId = (hardwareInfo == null) ? null
883d922a546b94119217fb790113d0001cad0432060Wonsik Kim                            : mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
884b2b3151a42bd563669b222b82efb64e294dc9049Jinsuk Kim                }
885b2b3151a42bd563669b222b82efb64e294dc9049Jinsuk Kim            }
886b2b3151a42bd563669b222b82efb64e294dc9049Jinsuk Kim            if (inputId != null) {
887b2b3151a42bd563669b222b82efb64e294dc9049Jinsuk Kim                Intent intent = new Intent(Intent.ACTION_VIEW);
888b2b3151a42bd563669b222b82efb64e294dc9049Jinsuk Kim                intent.setData(TvContract.buildChannelUriForPassthroughTvInput(inputId));
889b2b3151a42bd563669b222b82efb64e294dc9049Jinsuk Kim                mContext.startActivity(intent);
890b2b3151a42bd563669b222b82efb64e294dc9049Jinsuk Kim            } else {
891b2b3151a42bd563669b222b82efb64e294dc9049Jinsuk Kim                Slog.w(TAG, "onChanged: InputId cannot be found for :" + device);
892b2b3151a42bd563669b222b82efb64e294dc9049Jinsuk Kim            }
8937474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        }
8947474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    }
895c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim}
896