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;
2071d5c76f19e8714102073bf774c025d5ccdebc11Shubangimport static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY;
21969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
22969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
2349e559353278a56963596d652af26fe1f886a280Wonsik Kimimport android.content.BroadcastReceiver;
24c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.content.Context;
2549e559353278a56963596d652af26fe1f886a280Wonsik Kimimport android.content.Intent;
2649e559353278a56963596d652af26fe1f886a280Wonsik Kimimport android.content.IntentFilter;
278d718e1c96fd11ba9c956fbb66247b06142fda2dyangrenimport android.content.pm.PackageManager;
2861daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jangimport android.hardware.hdmi.HdmiControlManager;
2961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jangimport android.hardware.hdmi.HdmiDeviceInfo;
30969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.hardware.hdmi.HdmiHotplugEvent;
317474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.hardware.hdmi.IHdmiControlService;
32187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kimimport android.hardware.hdmi.IHdmiDeviceEventListener;
337474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.hardware.hdmi.IHdmiHotplugEventListener;
341f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jangimport android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
35d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioDevicePort;
36ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kimimport android.media.AudioFormat;
378e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kimimport android.media.AudioGain;
388e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kimimport android.media.AudioGainConfig;
39d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioManager;
40d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioPatch;
41d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioPort;
42d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioPortConfig;
43ad36f647380ca54e49e851b6aab19c06cf6d02c4Wally Yauimport android.media.AudioSystem;
44d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardware;
45d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardwareCallback;
46546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seoimport android.media.tv.TvInputHardwareInfo;
47969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.media.tv.TvInputInfo;
48d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvStreamConfig;
498d718e1c96fd11ba9c956fbb66247b06142fda2dyangrenimport android.os.Binder;
50969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.os.Handler;
51c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.os.IBinder;
52969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.os.Message;
53c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.os.RemoteException;
547474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.os.ServiceManager;
554f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Leeimport android.util.ArrayMap;
56c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.util.Slog;
57c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.util.SparseArray;
58969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.util.SparseBooleanArray;
59c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.view.KeyEvent;
60c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.view.Surface;
61c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
62fe9a53bc45fd0124a876dc0a49680aaf86641d3eJeff Sharkeyimport com.android.internal.util.DumpUtils;
638d718e1c96fd11ba9c956fbb66247b06142fda2dyangrenimport com.android.internal.util.IndentingPrintWriter;
64969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport com.android.server.SystemService;
65969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
668d718e1c96fd11ba9c956fbb66247b06142fda2dyangrenimport java.io.FileDescriptor;
678d718e1c96fd11ba9c956fbb66247b06142fda2dyangrenimport java.io.PrintWriter;
68c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport java.util.ArrayList;
698d718e1c96fd11ba9c956fbb66247b06142fda2dyangrenimport java.util.Arrays;
704f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Leeimport java.util.Collections;
71d922a546b94119217fb790113d0001cad0432060Wonsik Kimimport java.util.Iterator;
72d922a546b94119217fb790113d0001cad0432060Wonsik Kimimport java.util.LinkedList;
73c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport java.util.List;
744f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Leeimport java.util.Map;
75c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
76c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim/**
77c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * A helper class for TvInputManagerService to handle TV input hardware.
78c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
79c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * This class does a basic connection management and forwarding calls to TvInputHal which eventually
80c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * calls to tv_input HAL module.
81c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
82c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * @hide
83c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim */
847474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimclass TvInputHardwareManager implements TvInputHal.Callback {
85c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private static final String TAG = TvInputHardwareManager.class.getSimpleName();
864f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
8749e559353278a56963596d652af26fe1f886a280Wonsik Kim    private final Context mContext;
884f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final Listener mListener;
89c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private final TvInputHal mHal = new TvInputHal(this);
90d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final SparseArray<Connection> mConnections = new SparseArray<>();
91d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
92546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo    private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>();
934f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    /* A map from a device ID to the matching TV input ID. */
94d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
954f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    /* A map from a HDMI logical address to the matching TV input ID. */
96546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo    private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>();
97d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>();
984f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
99d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim    private final AudioManager mAudioManager;
1007474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    private final IHdmiHotplugEventListener mHdmiHotplugEventListener =
1017474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            new HdmiHotplugEventListener();
1024f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener();
1031f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang    private final IHdmiSystemAudioModeChangeListener mHdmiSystemAudioModeChangeListener =
1041f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            new HdmiSystemAudioModeChangeListener();
10549e559353278a56963596d652af26fe1f886a280Wonsik Kim    private final BroadcastReceiver mVolumeReceiver = new BroadcastReceiver() {
10649e559353278a56963596d652af26fe1f886a280Wonsik Kim        @Override
10749e559353278a56963596d652af26fe1f886a280Wonsik Kim        public void onReceive(Context context, Intent intent) {
10849e559353278a56963596d652af26fe1f886a280Wonsik Kim            handleVolumeChange(context, intent);
10949e559353278a56963596d652af26fe1f886a280Wonsik Kim        }
11049e559353278a56963596d652af26fe1f886a280Wonsik Kim    };
11149e559353278a56963596d652af26fe1f886a280Wonsik Kim    private int mCurrentIndex = 0;
11249e559353278a56963596d652af26fe1f886a280Wonsik Kim    private int mCurrentMaxIndex = 0;
113d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim
1144f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
115d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
116969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
117187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    // Calls to mListener should happen here.
118187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    private final Handler mHandler = new ListenerHandler();
119c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
120c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private final Object mLock = new Object();
121c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
122187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    public TvInputHardwareManager(Context context, Listener listener) {
12349e559353278a56963596d652af26fe1f886a280Wonsik Kim        mContext = context;
124187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        mListener = listener;
125d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
126c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        mHal.init();
127969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
128969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
129969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    public void onBootPhase(int phase) {
130969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
13138b3257b7c0d80d282f1a1f7e1dd9c47e77c1081Jae Seo            IHdmiControlService hdmiControlService = IHdmiControlService.Stub.asInterface(
13238b3257b7c0d80d282f1a1f7e1dd9c47e77c1081Jae Seo                    ServiceManager.getService(Context.HDMI_CONTROL_SERVICE));
13338b3257b7c0d80d282f1a1f7e1dd9c47e77c1081Jae Seo            if (hdmiControlService != null) {
1347474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                try {
13538b3257b7c0d80d282f1a1f7e1dd9c47e77c1081Jae Seo                    hdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
13638b3257b7c0d80d282f1a1f7e1dd9c47e77c1081Jae Seo                    hdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
13738b3257b7c0d80d282f1a1f7e1dd9c47e77c1081Jae Seo                    hdmiControlService.addSystemAudioModeChangeListener(
1381f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                            mHdmiSystemAudioModeChangeListener);
13938b3257b7c0d80d282f1a1f7e1dd9c47e77c1081Jae Seo                    mHdmiDeviceList.addAll(hdmiControlService.getInputDevices());
1407474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                } catch (RemoteException e) {
1414f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
1427474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                }
14308f1ab02d6de42756825a2dfa7027137ff959bd8Jinsuk Kim            } else {
14408f1ab02d6de42756825a2dfa7027137ff959bd8Jinsuk Kim                Slog.w(TAG, "HdmiControlService is not available");
1457474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            }
146ee5ad729b90deff435f9875337cbc434be4f8fe7John Spurlock            final IntentFilter filter = new IntentFilter();
147ee5ad729b90deff435f9875337cbc434be4f8fe7John Spurlock            filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
148ee5ad729b90deff435f9875337cbc434be4f8fe7John Spurlock            filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION);
149ee5ad729b90deff435f9875337cbc434be4f8fe7John Spurlock            mContext.registerReceiver(mVolumeReceiver, filter);
15049e559353278a56963596d652af26fe1f886a280Wonsik Kim            updateVolume();
151969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
152c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
153c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
154c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    @Override
1554f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) {
156c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
157c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = new Connection(info);
158c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            connection.updateConfigsLocked(configs);
159c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mConnections.put(info.getDeviceId(), connection);
160d922a546b94119217fb790113d0001cad0432060Wonsik Kim            buildHardwareListLocked();
161187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            mHandler.obtainMessage(
162187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget();
163d922a546b94119217fb790113d0001cad0432060Wonsik Kim            if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
164d922a546b94119217fb790113d0001cad0432060Wonsik Kim                processPendingHdmiDeviceEventsLocked();
165d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
166c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
167c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
168c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
169d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private void buildHardwareListLocked() {
170d922a546b94119217fb790113d0001cad0432060Wonsik Kim        mHardwareList.clear();
171c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        for (int i = 0; i < mConnections.size(); ++i) {
172d922a546b94119217fb790113d0001cad0432060Wonsik Kim            mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked());
173c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
174c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
175c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
176c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    @Override
177c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public void onDeviceUnavailable(int deviceId) {
178c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
179c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
180c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
181c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
182c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
183c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
184969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            connection.resetLocked(null, null, null, null, null);
185c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mConnections.remove(deviceId);
186d922a546b94119217fb790113d0001cad0432060Wonsik Kim            buildHardwareListLocked();
1874f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            TvInputHardwareInfo info = connection.getHardwareInfoLocked();
188d922a546b94119217fb790113d0001cad0432060Wonsik Kim            if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
189546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                // Remove HDMI devices linked with this hardware.
190546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                for (Iterator<HdmiDeviceInfo> it = mHdmiDeviceList.iterator(); it.hasNext();) {
19161f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                    HdmiDeviceInfo deviceInfo = it.next();
192d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    if (deviceInfo.getPortId() == info.getHdmiPortId()) {
193546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                        mHandler.obtainMessage(ListenerHandler.HDMI_DEVICE_REMOVED, 0, 0,
194d922a546b94119217fb790113d0001cad0432060Wonsik Kim                                deviceInfo).sendToTarget();
195d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        it.remove();
196d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    }
197d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
198d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
199187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim            mHandler.obtainMessage(
2004f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    ListenerHandler.HARDWARE_DEVICE_REMOVED, 0, 0, info).sendToTarget();
201c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
202c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
203c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
204c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    @Override
205c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) {
206c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
207c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
208c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
209c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with "
210c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                        + deviceId);
211c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
212c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
21371d5c76f19e8714102073bf774c025d5ccdebc11Shubang            int previousConfigsLength = connection.getConfigsLengthLocked();
214c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            connection.updateConfigsLocked(configs);
2159a1036575182705bc27c6bf255dd17669821181dWonsik Kim            String inputId = mHardwareInputIdMap.get(deviceId);
21671d5c76f19e8714102073bf774c025d5ccdebc11Shubang            if (inputId != null
21771d5c76f19e8714102073bf774c025d5ccdebc11Shubang                    && (previousConfigsLength == 0) != (connection.getConfigsLengthLocked() == 0)) {
2189a1036575182705bc27c6bf255dd17669821181dWonsik Kim                mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
21971d5c76f19e8714102073bf774c025d5ccdebc11Shubang                    connection.getInputStateLocked(), 0, inputId).sendToTarget();
2209a1036575182705bc27c6bf255dd17669821181dWonsik Kim            }
221017bb8523dd7c3dbfd00c055fde241de4dca9da2Dongwon Kang            ITvInputHardwareCallback callback = connection.getCallbackLocked();
222017bb8523dd7c3dbfd00c055fde241de4dca9da2Dongwon Kang            if (callback != null) {
223017bb8523dd7c3dbfd00c055fde241de4dca9da2Dongwon Kang                try {
224017bb8523dd7c3dbfd00c055fde241de4dca9da2Dongwon Kang                    callback.onStreamConfigChanged(configs);
225017bb8523dd7c3dbfd00c055fde241de4dca9da2Dongwon Kang                } catch (RemoteException e) {
226017bb8523dd7c3dbfd00c055fde241de4dca9da2Dongwon Kang                    Slog.e(TAG, "error in onStreamConfigurationChanged", e);
227017bb8523dd7c3dbfd00c055fde241de4dca9da2Dongwon Kang                }
228c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
229c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
230c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
231c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
232c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    @Override
233c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    public void onFirstFrameCaptured(int deviceId, int streamId) {
234c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        synchronized (mLock) {
235c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(deviceId);
236c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (connection == null) {
237c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Slog.e(TAG, "FirstFrameCaptured: Cannot find a connection with "
238c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        + deviceId);
239c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return;
240c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
241c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Runnable runnable = connection.getOnFirstFrameCapturedLocked();
242c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (runnable != null) {
243c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                runnable.run();
244c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                connection.setOnFirstFrameCapturedLocked(null);
245c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
246c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
247c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
248c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
249c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public List<TvInputHardwareInfo> getHardwareList() {
250c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
251d922a546b94119217fb790113d0001cad0432060Wonsik Kim            return Collections.unmodifiableList(mHardwareList);
252c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
253c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
254c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
255546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo    public List<HdmiDeviceInfo> getHdmiDeviceList() {
256d922a546b94119217fb790113d0001cad0432060Wonsik Kim        synchronized (mLock) {
257546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo            return Collections.unmodifiableList(mHdmiDeviceList);
2584f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
2594f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
2604f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
261e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim    private boolean checkUidChangedLocked(
262e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            Connection connection, int callingUid, int resolvedUserId) {
263e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        Integer connectionCallingUid = connection.getCallingUidLocked();
264e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        Integer connectionResolvedUserId = connection.getResolvedUserIdLocked();
2656e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        return connectionCallingUid == null || connectionResolvedUserId == null
2666e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                || connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId;
267e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim    }
268e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim
2691abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo    public void addHardwareInput(int deviceId, TvInputInfo info) {
270969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        synchronized (mLock) {
2714f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            String oldInputId = mHardwareInputIdMap.get(deviceId);
2724f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (oldInputId != null) {
273969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                Slog.w(TAG, "Trying to override previous registration: old = "
2744f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                        + mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
275969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        + info + ":" + deviceId);
276969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
2774f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mHardwareInputIdMap.put(deviceId, info.getId());
2784f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mInputMap.put(info.getId(), info);
279969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
2809a1036575182705bc27c6bf255dd17669821181dWonsik Kim            // Process pending state changes
2819a1036575182705bc27c6bf255dd17669821181dWonsik Kim
2829a1036575182705bc27c6bf255dd17669821181dWonsik Kim            // For logical HDMI devices, they have information from HDMI CEC signals.
283969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            for (int i = 0; i < mHdmiStateMap.size(); ++i) {
284d922a546b94119217fb790113d0001cad0432060Wonsik Kim                TvInputHardwareInfo hardwareInfo =
285d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i));
286d922a546b94119217fb790113d0001cad0432060Wonsik Kim                if (hardwareInfo == null) {
287d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    continue;
288d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
289d922a546b94119217fb790113d0001cad0432060Wonsik Kim                String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
290969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                if (inputId != null && inputId.equals(info.getId())) {
29171d5c76f19e8714102073bf774c025d5ccdebc11Shubang                    // No HDMI hotplug does not necessarily mean disconnected, as old devices may
29271d5c76f19e8714102073bf774c025d5ccdebc11Shubang                    // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to
29371d5c76f19e8714102073bf774c025d5ccdebc11Shubang                    // denote unknown state.
29471d5c76f19e8714102073bf774c025d5ccdebc11Shubang                    int state = mHdmiStateMap.valueAt(i)
29571d5c76f19e8714102073bf774c025d5ccdebc11Shubang                            ? INPUT_STATE_CONNECTED
29671d5c76f19e8714102073bf774c025d5ccdebc11Shubang                            : INPUT_STATE_CONNECTED_STANDBY;
29771d5c76f19e8714102073bf774c025d5ccdebc11Shubang                    mHandler.obtainMessage(
29871d5c76f19e8714102073bf774c025d5ccdebc11Shubang                        ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget();
2999a1036575182705bc27c6bf255dd17669821181dWonsik Kim                    return;
300969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
301969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
30271d5c76f19e8714102073bf774c025d5ccdebc11Shubang            // For the rest of the devices, we can tell by the cable connection status.
3039a1036575182705bc27c6bf255dd17669821181dWonsik Kim            Connection connection = mConnections.get(deviceId);
3049a1036575182705bc27c6bf255dd17669821181dWonsik Kim            if (connection != null) {
3059a1036575182705bc27c6bf255dd17669821181dWonsik Kim                mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
30671d5c76f19e8714102073bf774c025d5ccdebc11Shubang                    connection.getInputStateLocked(), 0, info.getId()).sendToTarget();
3079a1036575182705bc27c6bf255dd17669821181dWonsik Kim            }
308969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
309969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
310969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
31138feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim    private static <T> int indexOfEqualValue(SparseArray<T> map, T value) {
31238feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim        for (int i = 0; i < map.size(); ++i) {
31338feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            if (map.valueAt(i).equals(value)) {
31438feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim                return i;
31538feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            }
31638feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim        }
31738feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim        return -1;
31838feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim    }
31938feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim
32071dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim    private static boolean intArrayContains(int[] array, int value) {
32171dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim        for (int element : array) {
32271dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim            if (element == value) return true;
32371dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim        }
32471dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim        return false;
32571dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim    }
32671dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim
3271abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo    public void addHdmiInput(int id, TvInputInfo info) {
3284f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        if (info.getType() != TvInputInfo.TYPE_HDMI) {
3294f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
3304f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
3314f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        synchronized (mLock) {
3324f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            String parentId = info.getParentId();
33338feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId);
33438feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            if (parentIndex < 0) {
3354f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
3364f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
3378960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim            String oldInputId = mHdmiInputIdMap.get(id);
3384f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (oldInputId != null) {
3394f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                Slog.w(TAG, "Trying to override previous registration: old = "
3408960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim                        + mInputMap.get(oldInputId) + ":" + id + ", new = "
3418960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim                        + info + ":" + id);
3424f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
3438960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim            mHdmiInputIdMap.put(id, info.getId());
3444f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mInputMap.put(info.getId(), info);
3454f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
3464f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
3474f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
3481abbbcd33efb8e5897c0fad5b6dd3af9a6b49b0dJae Seo    public void removeHardwareInput(String inputId) {
3494f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        synchronized (mLock) {
3504f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mInputMap.remove(inputId);
35138feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
3524f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (hardwareIndex >= 0) {
3534f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                mHardwareInputIdMap.removeAt(hardwareIndex);
3544f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
355546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo            int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId);
356546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo            if (deviceIndex >= 0) {
357546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                mHdmiInputIdMap.removeAt(deviceIndex);
3584f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
3594f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
3604f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
3614f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
362c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    /**
363c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * Create a TvInputHardware object with a specific deviceId. One service at a time can access
364c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * the object, and if more than one process attempts to create hardware with the same deviceId,
365c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * the latest service will get the object and all the other hardware are released. The
366c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * release is notified via ITvInputHardwareCallback.onReleased().
367c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     */
368c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
369969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            TvInputInfo info, int callingUid, int resolvedUserId) {
370c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        if (callback == null) {
371c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            throw new NullPointerException();
372c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
373c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
374c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
375c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
376c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "Invalid deviceId : " + deviceId);
377c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return null;
378c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
379e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
380969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                TvInputHardwareImpl hardware =
381969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        new TvInputHardwareImpl(connection.getHardwareInfoLocked());
382c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                try {
383c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    callback.asBinder().linkToDeath(connection, 0);
384c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                } catch (RemoteException e) {
385c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    hardware.release();
386c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    return null;
387c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
388969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId);
389c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
390c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return connection.getHardwareLocked();
391c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
392c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
393c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
394c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    /**
395c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * Release the specified hardware.
396c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     */
397c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid,
398c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            int resolvedUserId) {
399c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
400c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
401c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
402c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "Invalid deviceId : " + deviceId);
403c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
404c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
405c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection.getHardwareLocked() != hardware
406e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim                    || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
407c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
408c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
409969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            connection.resetLocked(null, null, null, null, null);
410969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
411969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
412969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
413d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private TvInputHardwareInfo findHardwareInfoForHdmiPortLocked(int port) {
414d922a546b94119217fb790113d0001cad0432060Wonsik Kim        for (TvInputHardwareInfo hardwareInfo : mHardwareList) {
415969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
416969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    && hardwareInfo.getHdmiPortId() == port) {
417d922a546b94119217fb790113d0001cad0432060Wonsik Kim                return hardwareInfo;
418969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
419969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
420969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        return null;
421969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
422969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
423c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    private int findDeviceIdForInputIdLocked(String inputId) {
424c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        for (int i = 0; i < mConnections.size(); ++i) {
425c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(i);
426c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (connection.getInfoLocked().getId().equals(inputId)) {
427c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return i;
428c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
429c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
430c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        return -1;
431c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
432c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
433c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    /**
434c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     * Get the list of TvStreamConfig which is buffered mode.
435c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     */
436c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid,
437c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int resolvedUserId) {
4386e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        List<TvStreamConfig> configsList = new ArrayList<>();
439c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        synchronized (mLock) {
440c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int deviceId = findDeviceIdForInputIdLocked(inputId);
441c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (deviceId < 0) {
442c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Slog.e(TAG, "Invalid inputId : " + inputId);
443c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return configsList;
444c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
445c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(deviceId);
446c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            for (TvStreamConfig config : connection.getConfigsLocked()) {
447c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
448c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    configsList.add(config);
449c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
450c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
451c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
452c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        return configsList;
453c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
454c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
455c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    /**
456c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     * Take a snapshot of the given TV input into the provided Surface.
457c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     */
458c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config,
459c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int callingUid, int resolvedUserId) {
460c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        synchronized (mLock) {
461c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int deviceId = findDeviceIdForInputIdLocked(inputId);
462c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (deviceId < 0) {
463c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Slog.e(TAG, "Invalid inputId : " + inputId);
464c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return false;
465c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
466c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(deviceId);
467c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
468c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (hardwareImpl != null) {
469c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                // Stop previous capture.
470c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Runnable runnable = connection.getOnFirstFrameCapturedLocked();
471c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (runnable != null) {
472c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    runnable.run();
473c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    connection.setOnFirstFrameCapturedLocked(null);
474c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
475c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
476c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                boolean result = hardwareImpl.startCapture(surface, config);
477c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (result) {
478c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    connection.setOnFirstFrameCapturedLocked(new Runnable() {
479c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        @Override
480c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        public void run() {
481c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                            hardwareImpl.stopCapture(config);
482c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        }
483c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    });
484c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
485c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return result;
486c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
487c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
488c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        return false;
489c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
490c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
491d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private void processPendingHdmiDeviceEventsLocked() {
492d922a546b94119217fb790113d0001cad0432060Wonsik Kim        for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) {
493d922a546b94119217fb790113d0001cad0432060Wonsik Kim            Message msg = it.next();
49461f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang            HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
495d922a546b94119217fb790113d0001cad0432060Wonsik Kim            TvInputHardwareInfo hardwareInfo =
496d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId());
497d922a546b94119217fb790113d0001cad0432060Wonsik Kim            if (hardwareInfo != null) {
498d922a546b94119217fb790113d0001cad0432060Wonsik Kim                msg.sendToTarget();
499d922a546b94119217fb790113d0001cad0432060Wonsik Kim                it.remove();
500d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
501d922a546b94119217fb790113d0001cad0432060Wonsik Kim        }
502d922a546b94119217fb790113d0001cad0432060Wonsik Kim    }
503d922a546b94119217fb790113d0001cad0432060Wonsik Kim
50449e559353278a56963596d652af26fe1f886a280Wonsik Kim    private void updateVolume() {
50549e559353278a56963596d652af26fe1f886a280Wonsik Kim        mCurrentMaxIndex = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
50649e559353278a56963596d652af26fe1f886a280Wonsik Kim        mCurrentIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
50749e559353278a56963596d652af26fe1f886a280Wonsik Kim    }
50849e559353278a56963596d652af26fe1f886a280Wonsik Kim
50949e559353278a56963596d652af26fe1f886a280Wonsik Kim    private void handleVolumeChange(Context context, Intent intent) {
51049e559353278a56963596d652af26fe1f886a280Wonsik Kim        String action = intent.getAction();
5116e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        switch (action) {
5126e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo            case AudioManager.VOLUME_CHANGED_ACTION: {
5136e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
5146e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                if (streamType != AudioManager.STREAM_MUSIC) {
5156e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                    return;
5166e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                }
5176e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                int index = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
5186e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                if (index == mCurrentIndex) {
5196e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                    return;
5206e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                }
5216e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                mCurrentIndex = index;
5226e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                break;
52349e559353278a56963596d652af26fe1f886a280Wonsik Kim            }
5246e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo            case AudioManager.STREAM_MUTE_CHANGED_ACTION: {
5256e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
5266e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                if (streamType != AudioManager.STREAM_MUSIC) {
5276e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                    return;
5286e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                }
5296e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                // volume index will be updated at onMediaStreamVolumeChanged() through
5306e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                // updateVolume().
5316e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                break;
53249e559353278a56963596d652af26fe1f886a280Wonsik Kim            }
5336e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo            default:
5346e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                Slog.w(TAG, "Unrecognized intent: " + intent);
53549e559353278a56963596d652af26fe1f886a280Wonsik Kim                return;
53649e559353278a56963596d652af26fe1f886a280Wonsik Kim        }
53749e559353278a56963596d652af26fe1f886a280Wonsik Kim        synchronized (mLock) {
53849e559353278a56963596d652af26fe1f886a280Wonsik Kim            for (int i = 0; i < mConnections.size(); ++i) {
53949e559353278a56963596d652af26fe1f886a280Wonsik Kim                TvInputHardwareImpl hardwareImpl = mConnections.valueAt(i).getHardwareImplLocked();
54049e559353278a56963596d652af26fe1f886a280Wonsik Kim                if (hardwareImpl != null) {
54149e559353278a56963596d652af26fe1f886a280Wonsik Kim                    hardwareImpl.onMediaStreamVolumeChanged();
54249e559353278a56963596d652af26fe1f886a280Wonsik Kim                }
54349e559353278a56963596d652af26fe1f886a280Wonsik Kim            }
54449e559353278a56963596d652af26fe1f886a280Wonsik Kim        }
54549e559353278a56963596d652af26fe1f886a280Wonsik Kim    }
54649e559353278a56963596d652af26fe1f886a280Wonsik Kim
54749e559353278a56963596d652af26fe1f886a280Wonsik Kim    private float getMediaStreamVolume() {
548ee5ad729b90deff435f9875337cbc434be4f8fe7John Spurlock        return (float) mCurrentIndex / (float) mCurrentMaxIndex;
54949e559353278a56963596d652af26fe1f886a280Wonsik Kim    }
55049e559353278a56963596d652af26fe1f886a280Wonsik Kim
5518d718e1c96fd11ba9c956fbb66247b06142fda2dyangren    public void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
5528d718e1c96fd11ba9c956fbb66247b06142fda2dyangren        final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
553fe9a53bc45fd0124a876dc0a49680aaf86641d3eJeff Sharkey        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
5548d718e1c96fd11ba9c956fbb66247b06142fda2dyangren
5558d718e1c96fd11ba9c956fbb66247b06142fda2dyangren        synchronized (mLock) {
5568d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.println("TvInputHardwareManager Info:");
5578d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.increaseIndent();
5588d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.println("mConnections: deviceId -> Connection");
5598d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.increaseIndent();
5608d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            for (int i = 0; i < mConnections.size(); i++) {
5618d718e1c96fd11ba9c956fbb66247b06142fda2dyangren                int deviceId = mConnections.keyAt(i);
5628d718e1c96fd11ba9c956fbb66247b06142fda2dyangren                Connection mConnection = mConnections.valueAt(i);
5638d718e1c96fd11ba9c956fbb66247b06142fda2dyangren                pw.println(deviceId + ": " + mConnection);
5648d718e1c96fd11ba9c956fbb66247b06142fda2dyangren
5658d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            }
5668d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.decreaseIndent();
5678d718e1c96fd11ba9c956fbb66247b06142fda2dyangren
5688d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.println("mHardwareList:");
5698d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.increaseIndent();
5708d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            for (TvInputHardwareInfo tvInputHardwareInfo : mHardwareList) {
5718d718e1c96fd11ba9c956fbb66247b06142fda2dyangren                pw.println(tvInputHardwareInfo);
5728d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            }
5738d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.decreaseIndent();
5748d718e1c96fd11ba9c956fbb66247b06142fda2dyangren
5758d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.println("mHdmiDeviceList:");
5768d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.increaseIndent();
5778d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            for (HdmiDeviceInfo hdmiDeviceInfo : mHdmiDeviceList) {
5788d718e1c96fd11ba9c956fbb66247b06142fda2dyangren                pw.println(hdmiDeviceInfo);
5798d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            }
5808d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.decreaseIndent();
5818d718e1c96fd11ba9c956fbb66247b06142fda2dyangren
5828d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.println("mHardwareInputIdMap: deviceId -> inputId");
5838d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.increaseIndent();
5848d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            for (int i = 0 ; i < mHardwareInputIdMap.size(); i++) {
5858d718e1c96fd11ba9c956fbb66247b06142fda2dyangren                int deviceId = mHardwareInputIdMap.keyAt(i);
5868d718e1c96fd11ba9c956fbb66247b06142fda2dyangren                String inputId = mHardwareInputIdMap.valueAt(i);
5878d718e1c96fd11ba9c956fbb66247b06142fda2dyangren                pw.println(deviceId + ": " + inputId);
5888d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            }
5898d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.decreaseIndent();
5908d718e1c96fd11ba9c956fbb66247b06142fda2dyangren
5918d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.println("mHdmiInputIdMap: id -> inputId");
5928d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.increaseIndent();
5938d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            for (int i = 0; i < mHdmiInputIdMap.size(); i++) {
5948d718e1c96fd11ba9c956fbb66247b06142fda2dyangren                int id = mHdmiInputIdMap.keyAt(i);
5958d718e1c96fd11ba9c956fbb66247b06142fda2dyangren                String inputId = mHdmiInputIdMap.valueAt(i);
5968d718e1c96fd11ba9c956fbb66247b06142fda2dyangren                pw.println(id + ": " + inputId);
5978d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            }
5988d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.decreaseIndent();
5998d718e1c96fd11ba9c956fbb66247b06142fda2dyangren
6008d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.println("mInputMap: inputId -> inputInfo");
6018d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.increaseIndent();
6028d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            for(Map.Entry<String, TvInputInfo> entry : mInputMap.entrySet()) {
6038d718e1c96fd11ba9c956fbb66247b06142fda2dyangren                pw.println(entry.getKey() + ": " + entry.getValue());
6048d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            }
6058d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.decreaseIndent();
6068d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            pw.decreaseIndent();
6078d718e1c96fd11ba9c956fbb66247b06142fda2dyangren        }
6088d718e1c96fd11ba9c956fbb66247b06142fda2dyangren    }
6098d718e1c96fd11ba9c956fbb66247b06142fda2dyangren
610c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private class Connection implements IBinder.DeathRecipient {
611969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        private final TvInputHardwareInfo mHardwareInfo;
612969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        private TvInputInfo mInfo;
613c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private TvInputHardwareImpl mHardware = null;
614c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private ITvInputHardwareCallback mCallback;
615c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private TvStreamConfig[] mConfigs = null;
616c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private Integer mCallingUid = null;
617c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private Integer mResolvedUserId = null;
618c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        private Runnable mOnFirstFrameCaptured;
619c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
620969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public Connection(TvInputHardwareInfo hardwareInfo) {
621969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            mHardwareInfo = hardwareInfo;
622c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
623c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
624c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        // *Locked methods assume TvInputHardwareManager.mLock is held.
625c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
626969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
627969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                TvInputInfo info, Integer callingUid, Integer resolvedUserId) {
628c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mHardware != null) {
629c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                try {
630c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    mCallback.onReleased();
631c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                } catch (RemoteException e) {
6324f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Slog.e(TAG, "error in Connection::resetLocked", e);
633c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
634c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                mHardware.release();
635c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
636c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mHardware = hardware;
637c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mCallback = callback;
638969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            mInfo = info;
639c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mCallingUid = callingUid;
640c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mResolvedUserId = resolvedUserId;
641c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            mOnFirstFrameCaptured = null;
642c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
643c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mHardware != null && mCallback != null) {
644c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                try {
645c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    mCallback.onStreamConfigChanged(getConfigsLocked());
646c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                } catch (RemoteException e) {
6474f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Slog.e(TAG, "error in Connection::resetLocked", e);
648c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
649c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
650c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
651c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
652c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void updateConfigsLocked(TvStreamConfig[] configs) {
653c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mConfigs = configs;
654c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
655c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
656969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public TvInputHardwareInfo getHardwareInfoLocked() {
657969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return mHardwareInfo;
658969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
659969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
660969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public TvInputInfo getInfoLocked() {
661c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mInfo;
662c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
663c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
664c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public ITvInputHardware getHardwareLocked() {
665c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mHardware;
666c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
667c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
668c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public TvInputHardwareImpl getHardwareImplLocked() {
669c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            return mHardware;
670c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
671c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
672c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public ITvInputHardwareCallback getCallbackLocked() {
673c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mCallback;
674c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
675c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
676c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public TvStreamConfig[] getConfigsLocked() {
677c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mConfigs;
678c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
679c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
680e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        public Integer getCallingUidLocked() {
681c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mCallingUid;
682c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
683c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
684e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        public Integer getResolvedUserIdLocked() {
685c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mResolvedUserId;
686c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
687c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
688c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public void setOnFirstFrameCapturedLocked(Runnable runnable) {
689c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            mOnFirstFrameCaptured = runnable;
690c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
691c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
692c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public Runnable getOnFirstFrameCapturedLocked() {
693c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            return mOnFirstFrameCaptured;
694c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
695c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
696c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
697c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void binderDied() {
698c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mLock) {
699969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                resetLocked(null, null, null, null, null);
700c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
701c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
7028d718e1c96fd11ba9c956fbb66247b06142fda2dyangren
7038d718e1c96fd11ba9c956fbb66247b06142fda2dyangren        public String toString() {
7048d718e1c96fd11ba9c956fbb66247b06142fda2dyangren            return "Connection{"
7058d718e1c96fd11ba9c956fbb66247b06142fda2dyangren                    + " mHardwareInfo: " + mHardwareInfo
7068d718e1c96fd11ba9c956fbb66247b06142fda2dyangren                    + ", mInfo: " + mInfo
7078d718e1c96fd11ba9c956fbb66247b06142fda2dyangren                    + ", mCallback: " + mCallback
7088d718e1c96fd11ba9c956fbb66247b06142fda2dyangren                    + ", mConfigs: " + Arrays.toString(mConfigs)
7098d718e1c96fd11ba9c956fbb66247b06142fda2dyangren                    + ", mCallingUid: " + mCallingUid
7108d718e1c96fd11ba9c956fbb66247b06142fda2dyangren                    + ", mResolvedUserId: " + mResolvedUserId
7118d718e1c96fd11ba9c956fbb66247b06142fda2dyangren                    + " }";
7128d718e1c96fd11ba9c956fbb66247b06142fda2dyangren        }
71371d5c76f19e8714102073bf774c025d5ccdebc11Shubang
71471d5c76f19e8714102073bf774c025d5ccdebc11Shubang        private int getConfigsLengthLocked() {
71571d5c76f19e8714102073bf774c025d5ccdebc11Shubang            return mConfigs == null ? 0 : mConfigs.length;
71671d5c76f19e8714102073bf774c025d5ccdebc11Shubang        }
71771d5c76f19e8714102073bf774c025d5ccdebc11Shubang
71871d5c76f19e8714102073bf774c025d5ccdebc11Shubang        private int getInputStateLocked() {
71971d5c76f19e8714102073bf774c025d5ccdebc11Shubang            int configsLength = getConfigsLengthLocked();
72071d5c76f19e8714102073bf774c025d5ccdebc11Shubang            if (configsLength > 0) {
72171d5c76f19e8714102073bf774c025d5ccdebc11Shubang                return INPUT_STATE_CONNECTED;
72271d5c76f19e8714102073bf774c025d5ccdebc11Shubang            }
72371d5c76f19e8714102073bf774c025d5ccdebc11Shubang            switch (mHardwareInfo.getCableConnectionStatus()) {
72471d5c76f19e8714102073bf774c025d5ccdebc11Shubang                case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_CONNECTED:
72571d5c76f19e8714102073bf774c025d5ccdebc11Shubang                    return INPUT_STATE_CONNECTED;
72671d5c76f19e8714102073bf774c025d5ccdebc11Shubang                case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_DISCONNECTED:
72771d5c76f19e8714102073bf774c025d5ccdebc11Shubang                    return INPUT_STATE_DISCONNECTED;
72871d5c76f19e8714102073bf774c025d5ccdebc11Shubang                case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_UNKNOWN:
72971d5c76f19e8714102073bf774c025d5ccdebc11Shubang                default:
73071d5c76f19e8714102073bf774c025d5ccdebc11Shubang                    return INPUT_STATE_CONNECTED_STANDBY;
73171d5c76f19e8714102073bf774c025d5ccdebc11Shubang            }
73271d5c76f19e8714102073bf774c025d5ccdebc11Shubang        }
733c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
734c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
735c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private class TvInputHardwareImpl extends ITvInputHardware.Stub {
736c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private final TvInputHardwareInfo mInfo;
737c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private boolean mReleased = false;
738c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private final Object mImplLock = new Object();
739c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
74006c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private final AudioManager.OnAudioPortUpdateListener mAudioListener =
74106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                new AudioManager.OnAudioPortUpdateListener() {
74206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            @Override
74306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            public void onAudioPortListUpdate(AudioPort[] portList) {
74406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                synchronized (mImplLock) {
7457587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    updateAudioConfigLocked();
74606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
74706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
74806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
74906c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            @Override
75006c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            public void onAudioPatchListUpdate(AudioPatch[] patchList) {
75106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                // No-op
75206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
75306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
75406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            @Override
75506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            public void onServiceDied() {
75606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                synchronized (mImplLock) {
75706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                    mAudioSource = null;
758b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    mAudioSink.clear();
759ee32ede54063415fd6c48742ff6a2bf407adf971Jae Seo                    if (mAudioPatch != null) {
760ee32ede54063415fd6c48742ff6a2bf407adf971Jae Seo                        mAudioManager.releaseAudioPatch(mAudioPatch);
761ee32ede54063415fd6c48742ff6a2bf407adf971Jae Seo                        mAudioPatch = null;
762ee32ede54063415fd6c48742ff6a2bf407adf971Jae Seo                    }
76306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
76406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
76506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        };
76606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private int mOverrideAudioType = AudioManager.DEVICE_NONE;
76706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private String mOverrideAudioAddress = "";
76806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private AudioDevicePort mAudioSource;
769b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim        private List<AudioDevicePort> mAudioSink = new ArrayList<>();
770d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim        private AudioPatch mAudioPatch = null;
771485e7e13f410072c7c79368338ec3eba92c39169Wonsik Kim        // Set to an invalid value for a volume, so that current volume can be applied at the
772485e7e13f410072c7c79368338ec3eba92c39169Wonsik Kim        // first call to updateAudioConfigLocked().
773485e7e13f410072c7c79368338ec3eba92c39169Wonsik Kim        private float mCommittedVolume = -1f;
77449e559353278a56963596d652af26fe1f886a280Wonsik Kim        private float mSourceVolume = 0.0f;
775d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim
776839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        private TvStreamConfig mActiveConfig = null;
777839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim
778ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private int mDesiredSamplingRate = 0;
779ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
780ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT;
781ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
782c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public TvInputHardwareImpl(TvInputHardwareInfo info) {
783c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mInfo = info;
78406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            mAudioManager.registerAudioPortUpdateListener(mAudioListener);
785d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim            if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
78606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
787b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                findAudioSinkFromAudioPolicy(mAudioSink);
788ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
789ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
790ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
791b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim        private void findAudioSinkFromAudioPolicy(List<AudioDevicePort> sinks) {
792b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            sinks.clear();
7936e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo            ArrayList<AudioDevicePort> devicePorts = new ArrayList<>();
794b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
795b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                return;
796b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            }
797b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
798ceb47aae5a045856ad13a5534a4d96f9b36c9a65Paul McLean            for (AudioDevicePort port : devicePorts) {
799ad36f647380ca54e49e851b6aab19c06cf6d02c4Wally Yau                if ((port.type() & sinkDevice) != 0 &&
800ad36f647380ca54e49e851b6aab19c06cf6d02c4Wally Yau                    (port.type() & AudioSystem.DEVICE_BIT_IN) == 0) {
801ceb47aae5a045856ad13a5534a4d96f9b36c9a65Paul McLean                    sinks.add(port);
802d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                }
803d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim            }
804ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
805ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
806ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private AudioDevicePort findAudioDevicePort(int type, String address) {
807ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (type == AudioManager.DEVICE_NONE) {
808ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                return null;
809ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
8106e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo            ArrayList<AudioDevicePort> devicePorts = new ArrayList<>();
811ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
812ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                return null;
813ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
814ceb47aae5a045856ad13a5534a4d96f9b36c9a65Paul McLean            for (AudioDevicePort port : devicePorts) {
815ceb47aae5a045856ad13a5534a4d96f9b36c9a65Paul McLean                if (port.type() == type && port.address().equals(address)) {
816ceb47aae5a045856ad13a5534a4d96f9b36c9a65Paul McLean                    return port;
817ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                }
818ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
819ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            return null;
820c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
821c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
822c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void release() {
823c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
82406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mAudioManager.unregisterAudioPortUpdateListener(mAudioListener);
825d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                if (mAudioPatch != null) {
826d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                    mAudioManager.releaseAudioPatch(mAudioPatch);
827d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                    mAudioPatch = null;
828d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                }
829c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                mReleased = true;
830c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
831c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
832c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
833839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        // A TvInputHardwareImpl object holds only one active session. Therefore, if a client
834839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        // attempts to call setSurface with different TvStreamConfig objects, the last call will
835839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        // prevail.
836c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
837c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public boolean setSurface(Surface surface, TvStreamConfig config)
838c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                throws RemoteException {
839c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
840c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                if (mReleased) {
841c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    throw new IllegalStateException("Device already released.");
842c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
8437587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim
844102670f16a44d4b25d7e3a252bedee0208b68c55Wonsik Kim                int result = TvInputHal.SUCCESS;
845839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                if (surface == null) {
846b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    // The value of config is ignored when surface == null.
847b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    if (mActiveConfig != null) {
848b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
849b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        mActiveConfig = null;
850b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    } else {
851b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        // We already have no active stream.
852b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        return true;
853b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    }
854839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                } else {
855b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    // It's impossible to set a non-null surface with a null config.
856b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    if (config == null) {
857b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        return false;
858b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    }
859b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    // Remove stream only if we have an existing active configuration.
860b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    if (mActiveConfig != null && !config.equals(mActiveConfig)) {
861839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
862839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        if (result != TvInputHal.SUCCESS) {
863839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                            mActiveConfig = null;
864839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        }
865839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    }
866b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    // Proceed only if all previous operations succeeded.
867839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    if (result == TvInputHal.SUCCESS) {
868b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config);
869b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        if (result == TvInputHal.SUCCESS) {
870b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                            mActiveConfig = config;
871b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        }
872839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    }
873839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                }
8747587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
875839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                return result == TvInputHal.SUCCESS;
876c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
877c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
878c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
8797587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        /**
8807587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim         * Update audio configuration (source, sink, patch) all up to current state.
8817587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim         */
8827587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        private void updateAudioConfigLocked() {
8837587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            boolean sinkUpdated = updateAudioSinkLocked();
8847587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            boolean sourceUpdated = updateAudioSourceLocked();
8857587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            // We can't do updated = updateAudioSinkLocked() || updateAudioSourceLocked() here
8867587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            // because Java won't evaluate the latter if the former is true.
8877587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim
888b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            if (mAudioSource == null || mAudioSink.isEmpty() || mActiveConfig == null) {
88906c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                if (mAudioPatch != null) {
8907587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    mAudioManager.releaseAudioPatch(mAudioPatch);
8917587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    mAudioPatch = null;
89206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
89306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                return;
89406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
89506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
89649e559353278a56963596d652af26fe1f886a280Wonsik Kim            updateVolume();
89749e559353278a56963596d652af26fe1f886a280Wonsik Kim            float volume = mSourceVolume * getMediaStreamVolume();
8988e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            AudioGainConfig sourceGainConfig = null;
89949e559353278a56963596d652af26fe1f886a280Wonsik Kim            if (mAudioSource.gains().length > 0 && volume != mCommittedVolume) {
9008e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                AudioGain sourceGain = null;
9018e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                for (AudioGain gain : mAudioSource.gains()) {
9028e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    if ((gain.mode() & AudioGain.MODE_JOINT) != 0) {
9038e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        sourceGain = gain;
9048e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        break;
9058e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    }
9068e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                }
9078e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                // NOTE: we only change the source gain in MODE_JOINT here.
9088e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                if (sourceGain != null) {
9098e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    int steps = (sourceGain.maxValue() - sourceGain.minValue())
9108e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                            / sourceGain.stepValue();
9118e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    int gainValue = sourceGain.minValue();
91249e559353278a56963596d652af26fe1f886a280Wonsik Kim                    if (volume < 1.0f) {
91349e559353278a56963596d652af26fe1f886a280Wonsik Kim                        gainValue += sourceGain.stepValue() * (int) (volume * steps + 0.5);
9148e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    } else {
9158e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        gainValue = sourceGain.maxValue();
9168e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    }
91749e559353278a56963596d652af26fe1f886a280Wonsik Kim                    // size of gain values is 1 in MODE_JOINT
91849e559353278a56963596d652af26fe1f886a280Wonsik Kim                    int[] gainValues = new int[] { gainValue };
9198e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    sourceGainConfig = sourceGain.buildConfig(AudioGain.MODE_JOINT,
9208e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                            sourceGain.channelMask(), gainValues, 0);
9218e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                } else {
9228e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    Slog.w(TAG, "No audio source gain with MODE_JOINT support exists.");
9238e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                }
9248e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            }
9258e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim
926ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            AudioPortConfig sourceConfig = mAudioSource.activeConfig();
927b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            List<AudioPortConfig> sinkConfigs = new ArrayList<>();
928ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
9297587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
93071dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim
931b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            for (AudioDevicePort audioSink : mAudioSink) {
932b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                AudioPortConfig sinkConfig = audioSink.activeConfig();
933b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                int sinkSamplingRate = mDesiredSamplingRate;
934b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                int sinkChannelMask = mDesiredChannelMask;
935b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                int sinkFormat = mDesiredFormat;
936b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                // If sinkConfig != null and values are set to default,
937b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                // fill in the sinkConfig values.
938b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                if (sinkConfig != null) {
939b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    if (sinkSamplingRate == 0) {
940b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                        sinkSamplingRate = sinkConfig.samplingRate();
941b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    }
942b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    if (sinkChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT) {
943b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                        sinkChannelMask = sinkConfig.channelMask();
944b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    }
945b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    if (sinkFormat == AudioFormat.ENCODING_DEFAULT) {
946b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                        sinkChannelMask = sinkConfig.format();
947b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    }
94871dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                }
94971dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim
950b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                if (sinkConfig == null
951b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                        || sinkConfig.samplingRate() != sinkSamplingRate
952b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                        || sinkConfig.channelMask() != sinkChannelMask
953b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                        || sinkConfig.format() != sinkFormat) {
954b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    // Check for compatibility and reset to default if necessary.
955b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    if (!intArrayContains(audioSink.samplingRates(), sinkSamplingRate)
956b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                            && audioSink.samplingRates().length > 0) {
957b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                        sinkSamplingRate = audioSink.samplingRates()[0];
958b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    }
959b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    if (!intArrayContains(audioSink.channelMasks(), sinkChannelMask)) {
960b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                        sinkChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
961b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    }
962b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    if (!intArrayContains(audioSink.formats(), sinkFormat)) {
963b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                        sinkFormat = AudioFormat.ENCODING_DEFAULT;
964b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    }
965b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    sinkConfig = audioSink.buildConfig(sinkSamplingRate, sinkChannelMask,
966b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                            sinkFormat, null);
967b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    shouldRecreateAudioPatch = true;
96871dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                }
969b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                sinkConfigs.add(sinkConfig);
970ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
971b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            // sinkConfigs.size() == mAudioSink.size(), and mAudioSink is guaranteed to be
972b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            // non-empty at the beginning of this method.
973b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            AudioPortConfig sinkConfig = sinkConfigs.get(0);
9748e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            if (sourceConfig == null || sourceGainConfig != null) {
97571dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                int sourceSamplingRate = 0;
97671dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                if (intArrayContains(mAudioSource.samplingRates(), sinkConfig.samplingRate())) {
97771dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                    sourceSamplingRate = sinkConfig.samplingRate();
97871dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                } else if (mAudioSource.samplingRates().length > 0) {
97971dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                    // Use any sampling rate and hope audio patch can handle resampling...
98071dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                    sourceSamplingRate = mAudioSource.samplingRates()[0];
98171dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                }
98271dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                int sourceChannelMask = AudioFormat.CHANNEL_IN_DEFAULT;
98371dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                for (int inChannelMask : mAudioSource.channelMasks()) {
98471dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                    if (AudioFormat.channelCountFromOutChannelMask(sinkConfig.channelMask())
98571dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                            == AudioFormat.channelCountFromInChannelMask(inChannelMask)) {
98671dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                        sourceChannelMask = inChannelMask;
98771dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                        break;
98871dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                    }
98971dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                }
99071dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                int sourceFormat = AudioFormat.ENCODING_DEFAULT;
99171dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                if (intArrayContains(mAudioSource.formats(), sinkConfig.format())) {
99271dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                    sourceFormat = sinkConfig.format();
99371dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                }
99471dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                sourceConfig = mAudioSource.buildConfig(sourceSamplingRate, sourceChannelMask,
99571dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                        sourceFormat, sourceGainConfig);
9968e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                shouldRecreateAudioPatch = true;
9978e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            }
9988e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            if (shouldRecreateAudioPatch) {
99949e559353278a56963596d652af26fe1f886a280Wonsik Kim                mCommittedVolume = volume;
1000ee32ede54063415fd6c48742ff6a2bf407adf971Jae Seo                if (mAudioPatch != null) {
1001ee32ede54063415fd6c48742ff6a2bf407adf971Jae Seo                    mAudioManager.releaseAudioPatch(mAudioPatch);
1002ee32ede54063415fd6c48742ff6a2bf407adf971Jae Seo                }
10038e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                mAudioManager.createAudioPatch(
10048e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        audioPatchArray,
10058e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        new AudioPortConfig[] { sourceConfig },
100693ff14b7a5d1a9b4d3f57da85b286069fe9d8303Jae Seo                        sinkConfigs.toArray(new AudioPortConfig[sinkConfigs.size()]));
10078e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                mAudioPatch = audioPatchArray[0];
100871dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                if (sourceGainConfig != null) {
100971dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                    mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig);
101071dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                }
1011ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
1012ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
1013ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
1014c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
10158e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim        public void setStreamVolume(float volume) throws RemoteException {
1016c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
1017c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                if (mReleased) {
1018c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    throw new IllegalStateException("Device already released.");
1019c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
102049e559353278a56963596d652af26fe1f886a280Wonsik Kim                mSourceVolume = volume;
10217587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
1022c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1023c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
1024c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1025c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
1026c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
1027c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
1028c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                if (mReleased) {
1029c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    throw new IllegalStateException("Device already released.");
1030c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
1031c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1032610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim            if (mInfo.getType() != TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
1033c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return false;
1034c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1035c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
1036c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return false;
1037c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
1038c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
1039c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        private boolean startCapture(Surface surface, TvStreamConfig config) {
1040c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            synchronized (mImplLock) {
1041c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (mReleased) {
1042c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
1043c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
1044c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (surface == null || config == null) {
1045c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
1046c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
1047c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
1048c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
1049c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
1050c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
10518f24a8b60f9afc1aedb89e7ee80ce65515439600Wonsik Kim                int result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config);
1052c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return result == TvInputHal.SUCCESS;
1053c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
1054c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
1055c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
1056c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        private boolean stopCapture(TvStreamConfig config) {
1057c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            synchronized (mImplLock) {
1058c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (mReleased) {
1059c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
1060c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
1061c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (config == null) {
1062c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
1063c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
1064c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
1065c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                int result = mHal.removeStream(mInfo.getDeviceId(), config);
1066c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return result == TvInputHal.SUCCESS;
1067c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
1068c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
1069ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
10707587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        private boolean updateAudioSourceLocked() {
107106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
10727587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                return false;
107306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
10747587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            AudioDevicePort previousSource = mAudioSource;
10757587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
10767587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            return mAudioSource == null ? (previousSource != null)
10777587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    : !mAudioSource.equals(previousSource);
10787587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        }
10797587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim
10807587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        private boolean updateAudioSinkLocked() {
10817587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
10827587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                return false;
10837587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            }
1084b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            List<AudioDevicePort> previousSink = mAudioSink;
1085b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            mAudioSink = new ArrayList<>();
108606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            if (mOverrideAudioType == AudioManager.DEVICE_NONE) {
1087b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                findAudioSinkFromAudioPolicy(mAudioSink);
108806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            } else {
108906c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                AudioDevicePort audioSink =
109006c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                        findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress);
109106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                if (audioSink != null) {
1092b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    mAudioSink.add(audioSink);
109306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
109406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
1095b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim
1096b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            // Returns true if mAudioSink and previousSink differs.
1097b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            if (mAudioSink.size() != previousSink.size()) {
1098b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                return true;
1099b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            }
1100b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            previousSink.removeAll(mAudioSink);
1101b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            return !previousSink.isEmpty();
110206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        }
110306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
11041f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        private void handleAudioSinkUpdated() {
11051f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            synchronized (mImplLock) {
11067587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
11071f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            }
11081f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        }
11091f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang
1110ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        @Override
1111ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
1112ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                int channelMask, int format) {
1113ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            synchronized (mImplLock) {
111406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mOverrideAudioType = audioType;
111506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mOverrideAudioAddress = audioAddress;
111606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
1117ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mDesiredSamplingRate = samplingRate;
1118ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mDesiredChannelMask = channelMask;
1119ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mDesiredFormat = format;
1120ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
11217587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
112249e559353278a56963596d652af26fe1f886a280Wonsik Kim            }
112349e559353278a56963596d652af26fe1f886a280Wonsik Kim        }
112449e559353278a56963596d652af26fe1f886a280Wonsik Kim
112549e559353278a56963596d652af26fe1f886a280Wonsik Kim        public void onMediaStreamVolumeChanged() {
112649e559353278a56963596d652af26fe1f886a280Wonsik Kim            synchronized (mImplLock) {
112749e559353278a56963596d652af26fe1f886a280Wonsik Kim                updateAudioConfigLocked();
1128ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
1129ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
1130c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
1131969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
1132187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    interface Listener {
11336e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        void onStateChanged(String inputId, int state);
11346e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        void onHardwareDeviceAdded(TvInputHardwareInfo info);
11356e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        void onHardwareDeviceRemoved(TvInputHardwareInfo info);
11366e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        void onHdmiDeviceAdded(HdmiDeviceInfo device);
11376e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        void onHdmiDeviceRemoved(HdmiDeviceInfo device);
11386e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo device);
1139187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    }
1140969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
1141187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    private class ListenerHandler extends Handler {
1142187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        private static final int STATE_CHANGED = 1;
1143187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        private static final int HARDWARE_DEVICE_ADDED = 2;
1144187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        private static final int HARDWARE_DEVICE_REMOVED = 3;
1145546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        private static final int HDMI_DEVICE_ADDED = 4;
1146546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        private static final int HDMI_DEVICE_REMOVED = 5;
114761daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang        private static final int HDMI_DEVICE_UPDATED = 6;
1148969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
1149969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        @Override
1150969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public final void handleMessage(Message msg) {
1151969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            switch (msg.what) {
1152187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                case STATE_CHANGED: {
1153969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    String inputId = (String) msg.obj;
1154969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    int state = msg.arg1;
1155187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    mListener.onStateChanged(inputId, state);
1156187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
1157187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
1158187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                case HARDWARE_DEVICE_ADDED: {
1159187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
1160187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    mListener.onHardwareDeviceAdded(info);
1161187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
1162187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
1163187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                case HARDWARE_DEVICE_REMOVED: {
11644f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
11654f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    mListener.onHardwareDeviceRemoved(info);
1166187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
1167187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
1168546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                case HDMI_DEVICE_ADDED: {
116961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                    HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
1170546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    mListener.onHdmiDeviceAdded(info);
1171187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
1172187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
1173546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                case HDMI_DEVICE_REMOVED: {
117461f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                    HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
1175546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    mListener.onHdmiDeviceRemoved(info);
1176969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    break;
1177969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
117861daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                case HDMI_DEVICE_UPDATED: {
1179184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
11806e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo                    String inputId;
1181184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    synchronized (mLock) {
1182184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                        inputId = mHdmiInputIdMap.get(info.getId());
1183184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    }
1184184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    if (inputId != null) {
1185184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                        mListener.onHdmiDeviceUpdated(inputId, info);
1186184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    } else {
1187184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                        Slog.w(TAG, "Could not resolve input ID matching the device info; "
1188184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                                + "ignoring.");
1189184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    }
1190184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    break;
119161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                }
1192969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                default: {
1193969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    Slog.w(TAG, "Unhandled message: " + msg);
1194969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    break;
1195969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
1196969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
1197969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
1198969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
1199187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
12007474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    // Listener implementations for HdmiControlService
12017474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim
12027474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub {
12037474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        @Override
12047474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        public void onReceived(HdmiHotplugEvent event) {
12057474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            synchronized (mLock) {
12067474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                mHdmiStateMap.put(event.getPort(), event.isConnected());
1207d922a546b94119217fb790113d0001cad0432060Wonsik Kim                TvInputHardwareInfo hardwareInfo =
1208d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        findHardwareInfoForHdmiPortLocked(event.getPort());
1209d922a546b94119217fb790113d0001cad0432060Wonsik Kim                if (hardwareInfo == null) {
1210d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    return;
1211d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
1212d922a546b94119217fb790113d0001cad0432060Wonsik Kim                String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
12137474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                if (inputId == null) {
12147474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                    return;
12157474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                }
121671d5c76f19e8714102073bf774c025d5ccdebc11Shubang                // No HDMI hotplug does not necessarily mean disconnected, as old devices may
121771d5c76f19e8714102073bf774c025d5ccdebc11Shubang                // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to
121871d5c76f19e8714102073bf774c025d5ccdebc11Shubang                // denote unknown state.
121971d5c76f19e8714102073bf774c025d5ccdebc11Shubang                int state = event.isConnected()
122071d5c76f19e8714102073bf774c025d5ccdebc11Shubang                        ? INPUT_STATE_CONNECTED
122171d5c76f19e8714102073bf774c025d5ccdebc11Shubang                        : INPUT_STATE_CONNECTED_STANDBY;
122271d5c76f19e8714102073bf774c025d5ccdebc11Shubang                mHandler.obtainMessage(
122371d5c76f19e8714102073bf774c025d5ccdebc11Shubang                    ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget();
12247474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            }
12257474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        }
12267474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    }
12277474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim
1228187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
1229187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        @Override
123061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang        public void onStatusChanged(HdmiDeviceInfo deviceInfo, int status) {
123198d760e1c3123e6db8459f605d59a5689d56268cJinsuk Kim            if (!deviceInfo.isSourceType()) return;
1232d922a546b94119217fb790113d0001cad0432060Wonsik Kim            synchronized (mLock) {
123361daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                int messageType = 0;
1234e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                Object obj = null;
123561daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                switch (status) {
123661daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    case HdmiControlManager.DEVICE_EVENT_ADD_DEVICE: {
1237cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                        if (findHdmiDeviceInfo(deviceInfo.getId()) == null) {
123861daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            mHdmiDeviceList.add(deviceInfo);
123961daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        } else {
124061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
124161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            return;
124261daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        }
124361daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        messageType = ListenerHandler.HDMI_DEVICE_ADDED;
1244e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        obj = deviceInfo;
124561daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        break;
1246d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    }
124761daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    case HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE: {
1248cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                        HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
1249cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                        if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
125061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
125161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            return;
125261daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        }
125361daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        messageType = ListenerHandler.HDMI_DEVICE_REMOVED;
1254e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        obj = deviceInfo;
125561daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        break;
125661daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    }
125761daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    case HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE: {
1258cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                        HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
1259cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                        if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
126061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
126161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            return;
126261daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        }
126361daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        mHdmiDeviceList.add(deviceInfo);
126461daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        messageType = ListenerHandler.HDMI_DEVICE_UPDATED;
1265184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                        obj = deviceInfo;
126661daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        break;
1267d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    }
1268d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
126961daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang
1270e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                Message msg = mHandler.obtainMessage(messageType, 0, 0, obj);
1271d922a546b94119217fb790113d0001cad0432060Wonsik Kim                if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
1272d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    msg.sendToTarget();
1273d922a546b94119217fb790113d0001cad0432060Wonsik Kim                } else {
1274d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    mPendingHdmiDeviceEvents.add(msg);
1275d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
1276d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
1277187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        }
1278cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang
1279cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang        private HdmiDeviceInfo findHdmiDeviceInfo(int id) {
1280cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang            for (HdmiDeviceInfo info : mHdmiDeviceList) {
1281cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                if (info.getId() == id) {
1282cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                    return info;
1283cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                }
1284cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang            }
1285cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang            return null;
1286cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang        }
1287187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    }
12887474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim
12891f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang    private final class HdmiSystemAudioModeChangeListener extends
12901f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        IHdmiSystemAudioModeChangeListener.Stub {
12911f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        @Override
12921f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        public void onStatusChanged(boolean enabled) throws RemoteException {
12931f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            synchronized (mLock) {
12941f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                for (int i = 0; i < mConnections.size(); ++i) {
12951f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                    TvInputHardwareImpl impl = mConnections.valueAt(i).getHardwareImplLocked();
129644bb3a5bd630cfe0a4813586e840d59b9cbc9f64Jungshik Jang                    if (impl != null) {
129744bb3a5bd630cfe0a4813586e840d59b9cbc9f64Jungshik Jang                        impl.handleAudioSinkUpdated();
129844bb3a5bd630cfe0a4813586e840d59b9cbc9f64Jungshik Jang                    }
12991f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                }
13001f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            }
13011f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        }
13021f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang    }
1303c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim}
1304