1c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim/*
2c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * Copyright (C) 2014 The Android Open Source Project
3c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
4c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * Licensed under the Apache License, Version 2.0 (the "License");
5c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * you may not use this file except in compliance with the License.
6c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * You may obtain a copy of the License at
7c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
8c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *      http://www.apache.org/licenses/LICENSE-2.0
9c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
10c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * Unless required by applicable law or agreed to in writing, software
11c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * distributed under the License is distributed on an "AS IS" BASIS,
12c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * See the License for the specific language governing permissions and
14c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * limitations under the License.
15c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim */
16c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
17c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimpackage com.android.server.tv;
18c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
19969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
20969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
21969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
2249e559353278a56963596d652af26fe1f886a280Wonsik Kimimport android.content.BroadcastReceiver;
23c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.content.Context;
2449e559353278a56963596d652af26fe1f886a280Wonsik Kimimport android.content.Intent;
2549e559353278a56963596d652af26fe1f886a280Wonsik Kimimport android.content.IntentFilter;
2661daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jangimport android.hardware.hdmi.HdmiControlManager;
2761f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jangimport android.hardware.hdmi.HdmiDeviceInfo;
28969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.hardware.hdmi.HdmiHotplugEvent;
297474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.hardware.hdmi.IHdmiControlService;
30187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kimimport android.hardware.hdmi.IHdmiDeviceEventListener;
317474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.hardware.hdmi.IHdmiHotplugEventListener;
321f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jangimport android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
33d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioDevicePort;
34ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kimimport android.media.AudioFormat;
358e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kimimport android.media.AudioGain;
368e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kimimport android.media.AudioGainConfig;
37d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioManager;
38d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioPatch;
39d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioPort;
40d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kimimport android.media.AudioPortConfig;
41d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardware;
42d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.ITvInputHardwareCallback;
43546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seoimport android.media.tv.TvInputHardwareInfo;
44969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.media.tv.TvInputInfo;
45d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvStreamConfig;
46969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.os.Handler;
47c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.os.IBinder;
48969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.os.Message;
49c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.os.RemoteException;
507474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimimport android.os.ServiceManager;
514f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Leeimport android.util.ArrayMap;
52c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.util.Slog;
53c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.util.SparseArray;
54969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport android.util.SparseBooleanArray;
55c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.view.KeyEvent;
56c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.view.Surface;
57c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
58e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kimimport com.android.internal.os.SomeArgs;
59969167dc05a6485a32d160895871cff46fd81884Wonsik Kimimport com.android.server.SystemService;
60969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
61c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport java.util.ArrayList;
628e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kimimport java.util.Arrays;
634f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Leeimport java.util.Collections;
64d922a546b94119217fb790113d0001cad0432060Wonsik Kimimport java.util.Iterator;
65d922a546b94119217fb790113d0001cad0432060Wonsik Kimimport java.util.LinkedList;
66c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport java.util.List;
674f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Leeimport java.util.Map;
68c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
69c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim/**
70c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * A helper class for TvInputManagerService to handle TV input hardware.
71c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
72c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * This class does a basic connection management and forwarding calls to TvInputHal which eventually
73c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * calls to tv_input HAL module.
74c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
75c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * @hide
76c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim */
777474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kimclass TvInputHardwareManager implements TvInputHal.Callback {
78c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private static final String TAG = TvInputHardwareManager.class.getSimpleName();
794f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
8049e559353278a56963596d652af26fe1f886a280Wonsik Kim    private final Context mContext;
814f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final Listener mListener;
82c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private final TvInputHal mHal = new TvInputHal(this);
83d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final SparseArray<Connection> mConnections = new SparseArray<>();
84d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
85546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo    private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>();
864f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    /* A map from a device ID to the matching TV input ID. */
87d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
884f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    /* A map from a HDMI logical address to the matching TV input ID. */
89546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo    private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>();
90d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>();
914f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
92d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim    private final AudioManager mAudioManager;
934f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private IHdmiControlService mHdmiControlService;
947474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    private final IHdmiHotplugEventListener mHdmiHotplugEventListener =
957474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            new HdmiHotplugEventListener();
964f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener();
971f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang    private final IHdmiSystemAudioModeChangeListener mHdmiSystemAudioModeChangeListener =
981f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            new HdmiSystemAudioModeChangeListener();
9949e559353278a56963596d652af26fe1f886a280Wonsik Kim    private final BroadcastReceiver mVolumeReceiver = new BroadcastReceiver() {
10049e559353278a56963596d652af26fe1f886a280Wonsik Kim        @Override
10149e559353278a56963596d652af26fe1f886a280Wonsik Kim        public void onReceive(Context context, Intent intent) {
10249e559353278a56963596d652af26fe1f886a280Wonsik Kim            handleVolumeChange(context, intent);
10349e559353278a56963596d652af26fe1f886a280Wonsik Kim        }
10449e559353278a56963596d652af26fe1f886a280Wonsik Kim    };
10549e559353278a56963596d652af26fe1f886a280Wonsik Kim    private int mCurrentIndex = 0;
10649e559353278a56963596d652af26fe1f886a280Wonsik Kim    private int mCurrentMaxIndex = 0;
10749e559353278a56963596d652af26fe1f886a280Wonsik Kim    private final boolean mUseMasterVolume;
108d38bf476f6a31602e92a3207a4ceb29bf965f9aaJinsuk Kim
109d922a546b94119217fb790113d0001cad0432060Wonsik Kim    // TODO: Should handle STANDBY case.
1104f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
111d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
112969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
113187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    // Calls to mListener should happen here.
114187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    private final Handler mHandler = new ListenerHandler();
115c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
116c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private final Object mLock = new Object();
117c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
118187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    public TvInputHardwareManager(Context context, Listener listener) {
11949e559353278a56963596d652af26fe1f886a280Wonsik Kim        mContext = context;
120187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        mListener = listener;
121d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
12249e559353278a56963596d652af26fe1f886a280Wonsik Kim        mUseMasterVolume = mContext.getResources().getBoolean(
12349e559353278a56963596d652af26fe1f886a280Wonsik Kim                com.android.internal.R.bool.config_useMasterVolume);
124c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        mHal.init();
125969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
126969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
127969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    public void onBootPhase(int phase) {
128969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1294f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mHdmiControlService = IHdmiControlService.Stub.asInterface(ServiceManager.getService(
1304f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Context.HDMI_CONTROL_SERVICE));
1314f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (mHdmiControlService != null) {
1327474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                try {
1334f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    mHdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
1344f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    mHdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
1351f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                    mHdmiControlService.addSystemAudioModeChangeListener(
1361f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                            mHdmiSystemAudioModeChangeListener);
137546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    mHdmiDeviceList.addAll(mHdmiControlService.getInputDevices());
1387474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                } catch (RemoteException e) {
1394f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
1407474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                }
14108f1ab02d6de42756825a2dfa7027137ff959bd8Jinsuk Kim            } else {
14208f1ab02d6de42756825a2dfa7027137ff959bd8Jinsuk Kim                Slog.w(TAG, "HdmiControlService is not available");
1437474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            }
14449e559353278a56963596d652af26fe1f886a280Wonsik Kim            if (!mUseMasterVolume) {
14549e559353278a56963596d652af26fe1f886a280Wonsik Kim                final IntentFilter filter = new IntentFilter();
14649e559353278a56963596d652af26fe1f886a280Wonsik Kim                filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
14749e559353278a56963596d652af26fe1f886a280Wonsik Kim                filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION);
14849e559353278a56963596d652af26fe1f886a280Wonsik Kim                mContext.registerReceiver(mVolumeReceiver, filter);
14949e559353278a56963596d652af26fe1f886a280Wonsik Kim            }
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            }
213c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            connection.updateConfigsLocked(configs);
2149a1036575182705bc27c6bf255dd17669821181dWonsik Kim            String inputId = mHardwareInputIdMap.get(deviceId);
2159a1036575182705bc27c6bf255dd17669821181dWonsik Kim            if (inputId != null) {
2169a1036575182705bc27c6bf255dd17669821181dWonsik Kim                mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
2175b820a8aa1a200824e44aede6bc1e381a6d69dd5Wonsik Kim                        convertConnectedToState(configs.length > 0), 0, inputId).sendToTarget();
2189a1036575182705bc27c6bf255dd17669821181dWonsik Kim            }
219017bb8523dd7c3dbfd00c055fde241de4dca9da2Dongwon Kang            ITvInputHardwareCallback callback = connection.getCallbackLocked();
220017bb8523dd7c3dbfd00c055fde241de4dca9da2Dongwon Kang            if (callback != null) {
221017bb8523dd7c3dbfd00c055fde241de4dca9da2Dongwon Kang                try {
222017bb8523dd7c3dbfd00c055fde241de4dca9da2Dongwon Kang                    callback.onStreamConfigChanged(configs);
223017bb8523dd7c3dbfd00c055fde241de4dca9da2Dongwon Kang                } catch (RemoteException e) {
224017bb8523dd7c3dbfd00c055fde241de4dca9da2Dongwon Kang                    Slog.e(TAG, "error in onStreamConfigurationChanged", e);
225017bb8523dd7c3dbfd00c055fde241de4dca9da2Dongwon Kang                }
226c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
227c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
228c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
229c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
230c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    @Override
231c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    public void onFirstFrameCaptured(int deviceId, int streamId) {
232c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        synchronized (mLock) {
233c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(deviceId);
234c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (connection == null) {
235c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Slog.e(TAG, "FirstFrameCaptured: Cannot find a connection with "
236c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        + deviceId);
237c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return;
238c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
239c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Runnable runnable = connection.getOnFirstFrameCapturedLocked();
240c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (runnable != null) {
241c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                runnable.run();
242c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                connection.setOnFirstFrameCapturedLocked(null);
243c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
244c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
245c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
246c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
247c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public List<TvInputHardwareInfo> getHardwareList() {
248c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
249d922a546b94119217fb790113d0001cad0432060Wonsik Kim            return Collections.unmodifiableList(mHardwareList);
250c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
251c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
252c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
253546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo    public List<HdmiDeviceInfo> getHdmiDeviceList() {
254d922a546b94119217fb790113d0001cad0432060Wonsik Kim        synchronized (mLock) {
255546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo            return Collections.unmodifiableList(mHdmiDeviceList);
2564f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
2574f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
2584f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
259e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim    private boolean checkUidChangedLocked(
260e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            Connection connection, int callingUid, int resolvedUserId) {
261e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        Integer connectionCallingUid = connection.getCallingUidLocked();
262e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        Integer connectionResolvedUserId = connection.getResolvedUserIdLocked();
263e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        if (connectionCallingUid == null || connectionResolvedUserId == null) {
264e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            return true;
265e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        }
266e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        if (connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId) {
267e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            return true;
268e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        }
269e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        return false;
270e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim    }
271e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim
272969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    private int convertConnectedToState(boolean connected) {
273969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        if (connected) {
274969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return INPUT_STATE_CONNECTED;
275969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        } else {
276969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return INPUT_STATE_DISCONNECTED;
277969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
278969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
279969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
2804f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    public void addHardwareTvInput(int deviceId, TvInputInfo info) {
281969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        synchronized (mLock) {
2824f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            String oldInputId = mHardwareInputIdMap.get(deviceId);
2834f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (oldInputId != null) {
284969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                Slog.w(TAG, "Trying to override previous registration: old = "
2854f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                        + mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
286969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        + info + ":" + deviceId);
287969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
2884f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mHardwareInputIdMap.put(deviceId, info.getId());
2894f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mInputMap.put(info.getId(), info);
290969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
2919a1036575182705bc27c6bf255dd17669821181dWonsik Kim            // Process pending state changes
2929a1036575182705bc27c6bf255dd17669821181dWonsik Kim
2939a1036575182705bc27c6bf255dd17669821181dWonsik Kim            // For logical HDMI devices, they have information from HDMI CEC signals.
294969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            for (int i = 0; i < mHdmiStateMap.size(); ++i) {
295d922a546b94119217fb790113d0001cad0432060Wonsik Kim                TvInputHardwareInfo hardwareInfo =
296d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i));
297d922a546b94119217fb790113d0001cad0432060Wonsik Kim                if (hardwareInfo == null) {
298d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    continue;
299d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
300d922a546b94119217fb790113d0001cad0432060Wonsik Kim                String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
301969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                if (inputId != null && inputId.equals(info.getId())) {
302187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
303969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                            convertConnectedToState(mHdmiStateMap.valueAt(i)), 0,
304969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                            inputId).sendToTarget();
3059a1036575182705bc27c6bf255dd17669821181dWonsik Kim                    return;
306969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
307969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
3089a1036575182705bc27c6bf255dd17669821181dWonsik Kim            // For the rest of the devices, we can tell by the number of available streams.
3099a1036575182705bc27c6bf255dd17669821181dWonsik Kim            Connection connection = mConnections.get(deviceId);
3109a1036575182705bc27c6bf255dd17669821181dWonsik Kim            if (connection != null) {
3119a1036575182705bc27c6bf255dd17669821181dWonsik Kim                mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
3129a1036575182705bc27c6bf255dd17669821181dWonsik Kim                        convertConnectedToState(connection.getConfigsLocked().length > 0), 0,
3139a1036575182705bc27c6bf255dd17669821181dWonsik Kim                        info.getId()).sendToTarget();
3149a1036575182705bc27c6bf255dd17669821181dWonsik Kim                return;
3159a1036575182705bc27c6bf255dd17669821181dWonsik Kim            }
316969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
317969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
318969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
31938feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim    private static <T> int indexOfEqualValue(SparseArray<T> map, T value) {
32038feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim        for (int i = 0; i < map.size(); ++i) {
32138feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            if (map.valueAt(i).equals(value)) {
32238feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim                return i;
32338feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            }
32438feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim        }
32538feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim        return -1;
32638feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim    }
32738feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim
32871dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim    private static boolean intArrayContains(int[] array, int value) {
32971dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim        for (int element : array) {
33071dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim            if (element == value) return true;
33171dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim        }
33271dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim        return false;
33371dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim    }
33471dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim
3358960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim    public void addHdmiTvInput(int id, TvInputInfo info) {
3364f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        if (info.getType() != TvInputInfo.TYPE_HDMI) {
3374f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
3384f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
3394f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        synchronized (mLock) {
3404f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            String parentId = info.getParentId();
34138feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId);
34238feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            if (parentIndex < 0) {
3434f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
3444f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
3458960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim            String oldInputId = mHdmiInputIdMap.get(id);
3464f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (oldInputId != null) {
3474f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                Slog.w(TAG, "Trying to override previous registration: old = "
3488960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim                        + mInputMap.get(oldInputId) + ":" + id + ", new = "
3498960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim                        + info + ":" + id);
3504f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
3518960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim            mHdmiInputIdMap.put(id, info.getId());
3524f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mInputMap.put(info.getId(), info);
3534f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
3544f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
3554f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
3564f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    public void removeTvInput(String inputId) {
3574f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        synchronized (mLock) {
3584f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            mInputMap.remove(inputId);
35938feae971c43700c9cb15aafbf8bd37340675a50Wonsik Kim            int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
3604f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            if (hardwareIndex >= 0) {
3614f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                mHardwareInputIdMap.removeAt(hardwareIndex);
3624f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
363546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo            int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId);
364546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo            if (deviceIndex >= 0) {
365546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                mHdmiInputIdMap.removeAt(deviceIndex);
3664f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee            }
3674f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        }
3684f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee    }
3694f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee
370c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    /**
371c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * Create a TvInputHardware object with a specific deviceId. One service at a time can access
372c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * the object, and if more than one process attempts to create hardware with the same deviceId,
373c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * the latest service will get the object and all the other hardware are released. The
374c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * release is notified via ITvInputHardwareCallback.onReleased().
375c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     */
376c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
377969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            TvInputInfo info, int callingUid, int resolvedUserId) {
378c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        if (callback == null) {
379c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            throw new NullPointerException();
380c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
381c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
382c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
383c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
384c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "Invalid deviceId : " + deviceId);
385c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return null;
386c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
387e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim            if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
388969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                TvInputHardwareImpl hardware =
389969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                        new TvInputHardwareImpl(connection.getHardwareInfoLocked());
390c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                try {
391c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    callback.asBinder().linkToDeath(connection, 0);
392c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                } catch (RemoteException e) {
393c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    hardware.release();
394c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    return null;
395c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
396969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId);
397c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
398c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return connection.getHardwareLocked();
399c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
400c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
401c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
402c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    /**
403c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     * Release the specified hardware.
404c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim     */
405c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid,
406c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            int resolvedUserId) {
407c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        synchronized (mLock) {
408c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Connection connection = mConnections.get(deviceId);
409c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection == null) {
410c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                Slog.e(TAG, "Invalid deviceId : " + deviceId);
411c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
412c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
413c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (connection.getHardwareLocked() != hardware
414e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim                    || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
415c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return;
416c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
417969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            connection.resetLocked(null, null, null, null, null);
418969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
419969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
420969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
421d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private TvInputHardwareInfo findHardwareInfoForHdmiPortLocked(int port) {
422d922a546b94119217fb790113d0001cad0432060Wonsik Kim        for (TvInputHardwareInfo hardwareInfo : mHardwareList) {
423969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
424969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    && hardwareInfo.getHdmiPortId() == port) {
425d922a546b94119217fb790113d0001cad0432060Wonsik Kim                return hardwareInfo;
426969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
427969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
428969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        return null;
429969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
430969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
431c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    private int findDeviceIdForInputIdLocked(String inputId) {
432c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        for (int i = 0; i < mConnections.size(); ++i) {
433c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(i);
434c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (connection.getInfoLocked().getId().equals(inputId)) {
435c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return i;
436c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
437c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
438c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        return -1;
439c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
440c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
441c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    /**
442c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     * Get the list of TvStreamConfig which is buffered mode.
443c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     */
444c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid,
445c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int resolvedUserId) {
446c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        List<TvStreamConfig> configsList = new ArrayList<TvStreamConfig>();
447c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        synchronized (mLock) {
448c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int deviceId = findDeviceIdForInputIdLocked(inputId);
449c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (deviceId < 0) {
450c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Slog.e(TAG, "Invalid inputId : " + inputId);
451c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return configsList;
452c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
453c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(deviceId);
454c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            for (TvStreamConfig config : connection.getConfigsLocked()) {
455c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
456c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    configsList.add(config);
457c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
458c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
459c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
460c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        return configsList;
461c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
462c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
463c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    /**
464c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     * Take a snapshot of the given TV input into the provided Surface.
465c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo     */
466c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config,
467c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int callingUid, int resolvedUserId) {
468c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        synchronized (mLock) {
469c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            int deviceId = findDeviceIdForInputIdLocked(inputId);
470c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (deviceId < 0) {
471c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Slog.e(TAG, "Invalid inputId : " + inputId);
472c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return false;
473c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
474c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            Connection connection = mConnections.get(deviceId);
475c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
476c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            if (hardwareImpl != null) {
477c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                // Stop previous capture.
478c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                Runnable runnable = connection.getOnFirstFrameCapturedLocked();
479c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (runnable != null) {
480c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    runnable.run();
481c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    connection.setOnFirstFrameCapturedLocked(null);
482c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
483c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
484c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                boolean result = hardwareImpl.startCapture(surface, config);
485c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (result) {
486c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    connection.setOnFirstFrameCapturedLocked(new Runnable() {
487c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        @Override
488c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        public void run() {
489c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                            hardwareImpl.stopCapture(config);
490c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                        }
491c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    });
492c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
493c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return result;
494c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
495c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
496c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        return false;
497c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
498c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
499d922a546b94119217fb790113d0001cad0432060Wonsik Kim    private void processPendingHdmiDeviceEventsLocked() {
500d922a546b94119217fb790113d0001cad0432060Wonsik Kim        for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) {
501d922a546b94119217fb790113d0001cad0432060Wonsik Kim            Message msg = it.next();
50261f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang            HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
503d922a546b94119217fb790113d0001cad0432060Wonsik Kim            TvInputHardwareInfo hardwareInfo =
504d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId());
505d922a546b94119217fb790113d0001cad0432060Wonsik Kim            if (hardwareInfo != null) {
506d922a546b94119217fb790113d0001cad0432060Wonsik Kim                msg.sendToTarget();
507d922a546b94119217fb790113d0001cad0432060Wonsik Kim                it.remove();
508d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
509d922a546b94119217fb790113d0001cad0432060Wonsik Kim        }
510d922a546b94119217fb790113d0001cad0432060Wonsik Kim    }
511d922a546b94119217fb790113d0001cad0432060Wonsik Kim
51249e559353278a56963596d652af26fe1f886a280Wonsik Kim    private void updateVolume() {
51349e559353278a56963596d652af26fe1f886a280Wonsik Kim        mCurrentMaxIndex = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
51449e559353278a56963596d652af26fe1f886a280Wonsik Kim        mCurrentIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
51549e559353278a56963596d652af26fe1f886a280Wonsik Kim    }
51649e559353278a56963596d652af26fe1f886a280Wonsik Kim
51749e559353278a56963596d652af26fe1f886a280Wonsik Kim    private void handleVolumeChange(Context context, Intent intent) {
51849e559353278a56963596d652af26fe1f886a280Wonsik Kim        String action = intent.getAction();
51949e559353278a56963596d652af26fe1f886a280Wonsik Kim        if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
52049e559353278a56963596d652af26fe1f886a280Wonsik Kim            int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
52149e559353278a56963596d652af26fe1f886a280Wonsik Kim            if (streamType != AudioManager.STREAM_MUSIC) {
52249e559353278a56963596d652af26fe1f886a280Wonsik Kim                return;
52349e559353278a56963596d652af26fe1f886a280Wonsik Kim            }
52449e559353278a56963596d652af26fe1f886a280Wonsik Kim            int index = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
52549e559353278a56963596d652af26fe1f886a280Wonsik Kim            if (index == mCurrentIndex) {
52649e559353278a56963596d652af26fe1f886a280Wonsik Kim                return;
52749e559353278a56963596d652af26fe1f886a280Wonsik Kim            }
52849e559353278a56963596d652af26fe1f886a280Wonsik Kim            mCurrentIndex = index;
52949e559353278a56963596d652af26fe1f886a280Wonsik Kim        } else if (action.equals(AudioManager.STREAM_MUTE_CHANGED_ACTION)) {
53049e559353278a56963596d652af26fe1f886a280Wonsik Kim            int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
53149e559353278a56963596d652af26fe1f886a280Wonsik Kim            if (streamType != AudioManager.STREAM_MUSIC) {
53249e559353278a56963596d652af26fe1f886a280Wonsik Kim                return;
53349e559353278a56963596d652af26fe1f886a280Wonsik Kim            }
53449e559353278a56963596d652af26fe1f886a280Wonsik Kim            // volume index will be updated at onMediaStreamVolumeChanged() through updateVolume().
53549e559353278a56963596d652af26fe1f886a280Wonsik Kim        } else {
53649e559353278a56963596d652af26fe1f886a280Wonsik Kim            Slog.w(TAG, "Unrecognized intent: " + intent);
53749e559353278a56963596d652af26fe1f886a280Wonsik Kim            return;
53849e559353278a56963596d652af26fe1f886a280Wonsik Kim        }
53949e559353278a56963596d652af26fe1f886a280Wonsik Kim        synchronized (mLock) {
54049e559353278a56963596d652af26fe1f886a280Wonsik Kim            for (int i = 0; i < mConnections.size(); ++i) {
54149e559353278a56963596d652af26fe1f886a280Wonsik Kim                TvInputHardwareImpl hardwareImpl = mConnections.valueAt(i).getHardwareImplLocked();
54249e559353278a56963596d652af26fe1f886a280Wonsik Kim                if (hardwareImpl != null) {
54349e559353278a56963596d652af26fe1f886a280Wonsik Kim                    hardwareImpl.onMediaStreamVolumeChanged();
54449e559353278a56963596d652af26fe1f886a280Wonsik Kim                }
54549e559353278a56963596d652af26fe1f886a280Wonsik Kim            }
54649e559353278a56963596d652af26fe1f886a280Wonsik Kim        }
54749e559353278a56963596d652af26fe1f886a280Wonsik Kim    }
54849e559353278a56963596d652af26fe1f886a280Wonsik Kim
54949e559353278a56963596d652af26fe1f886a280Wonsik Kim    private float getMediaStreamVolume() {
55049e559353278a56963596d652af26fe1f886a280Wonsik Kim        return mUseMasterVolume ? 1.0f : ((float) mCurrentIndex / (float) mCurrentMaxIndex);
55149e559353278a56963596d652af26fe1f886a280Wonsik Kim    }
55249e559353278a56963596d652af26fe1f886a280Wonsik Kim
553c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private class Connection implements IBinder.DeathRecipient {
554969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        private final TvInputHardwareInfo mHardwareInfo;
555969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        private TvInputInfo mInfo;
556c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private TvInputHardwareImpl mHardware = null;
557c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private ITvInputHardwareCallback mCallback;
558c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private TvStreamConfig[] mConfigs = null;
559c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private Integer mCallingUid = null;
560c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private Integer mResolvedUserId = null;
561c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        private Runnable mOnFirstFrameCaptured;
562c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
563969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public Connection(TvInputHardwareInfo hardwareInfo) {
564969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            mHardwareInfo = hardwareInfo;
565c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
566c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
567c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        // *Locked methods assume TvInputHardwareManager.mLock is held.
568c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
569969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
570969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                TvInputInfo info, Integer callingUid, Integer resolvedUserId) {
571c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mHardware != null) {
572c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                try {
573c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    mCallback.onReleased();
574c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                } catch (RemoteException e) {
5754f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Slog.e(TAG, "error in Connection::resetLocked", e);
576c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
577c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                mHardware.release();
578c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
579c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mHardware = hardware;
580c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mCallback = callback;
581969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            mInfo = info;
582c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mCallingUid = callingUid;
583c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mResolvedUserId = resolvedUserId;
584c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            mOnFirstFrameCaptured = null;
585c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
586c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            if (mHardware != null && mCallback != null) {
587c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                try {
588c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    mCallback.onStreamConfigChanged(getConfigsLocked());
589c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                } catch (RemoteException e) {
5904f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    Slog.e(TAG, "error in Connection::resetLocked", e);
591c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
592c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
593c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
594c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
595c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void updateConfigsLocked(TvStreamConfig[] configs) {
596c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mConfigs = configs;
597c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
598c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
599969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public TvInputHardwareInfo getHardwareInfoLocked() {
600969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            return mHardwareInfo;
601969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
602969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
603969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public TvInputInfo getInfoLocked() {
604c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mInfo;
605c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
606c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
607c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public ITvInputHardware getHardwareLocked() {
608c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mHardware;
609c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
610c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
611c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public TvInputHardwareImpl getHardwareImplLocked() {
612c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            return mHardware;
613c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
614c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
615c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public ITvInputHardwareCallback getCallbackLocked() {
616c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mCallback;
617c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
618c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
619c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public TvStreamConfig[] getConfigsLocked() {
620c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mConfigs;
621c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
622c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
623e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        public Integer getCallingUidLocked() {
624c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mCallingUid;
625c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
626c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
627e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1fWonsik Kim        public Integer getResolvedUserIdLocked() {
628c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return mResolvedUserId;
629c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
630c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
631c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public void setOnFirstFrameCapturedLocked(Runnable runnable) {
632c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            mOnFirstFrameCaptured = runnable;
633c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
634c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
635c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        public Runnable getOnFirstFrameCapturedLocked() {
636c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            return mOnFirstFrameCaptured;
637c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
638c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
639c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
640c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void binderDied() {
641c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mLock) {
642969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                resetLocked(null, null, null, null, null);
643c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
644c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
645c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
646c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
647c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private class TvInputHardwareImpl extends ITvInputHardware.Stub {
648c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private final TvInputHardwareInfo mInfo;
649c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private boolean mReleased = false;
650c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        private final Object mImplLock = new Object();
651c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
65206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private final AudioManager.OnAudioPortUpdateListener mAudioListener =
65306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                new AudioManager.OnAudioPortUpdateListener() {
65406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            @Override
65506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            public void onAudioPortListUpdate(AudioPort[] portList) {
65606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                synchronized (mImplLock) {
6577587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    updateAudioConfigLocked();
65806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
65906c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
66006c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
66106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            @Override
66206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            public void onAudioPatchListUpdate(AudioPatch[] patchList) {
66306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                // No-op
66406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
66506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
66606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            @Override
66706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            public void onServiceDied() {
66806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                synchronized (mImplLock) {
66906c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                    mAudioSource = null;
670b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    mAudioSink.clear();
67106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                    mAudioPatch = null;
67206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
67306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
67406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        };
67506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private int mOverrideAudioType = AudioManager.DEVICE_NONE;
67606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private String mOverrideAudioAddress = "";
67706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        private AudioDevicePort mAudioSource;
678b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim        private List<AudioDevicePort> mAudioSink = new ArrayList<>();
679d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim        private AudioPatch mAudioPatch = null;
680485e7e13f410072c7c79368338ec3eba92c39169Wonsik Kim        // Set to an invalid value for a volume, so that current volume can be applied at the
681485e7e13f410072c7c79368338ec3eba92c39169Wonsik Kim        // first call to updateAudioConfigLocked().
682485e7e13f410072c7c79368338ec3eba92c39169Wonsik Kim        private float mCommittedVolume = -1f;
68349e559353278a56963596d652af26fe1f886a280Wonsik Kim        private float mSourceVolume = 0.0f;
684d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim
685839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        private TvStreamConfig mActiveConfig = null;
686839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim
687ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private int mDesiredSamplingRate = 0;
688ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
689ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT;
690ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
691c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public TvInputHardwareImpl(TvInputHardwareInfo info) {
692c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            mInfo = info;
69306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            mAudioManager.registerAudioPortUpdateListener(mAudioListener);
694d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim            if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
69506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
696b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                findAudioSinkFromAudioPolicy(mAudioSink);
697ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
698ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
699ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
700b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim        private void findAudioSinkFromAudioPolicy(List<AudioDevicePort> sinks) {
701b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            sinks.clear();
702ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
703b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
704b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                return;
705b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            }
706b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
707b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            for (AudioPort port : devicePorts) {
708b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                AudioDevicePort devicePort = (AudioDevicePort) port;
709b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                if ((devicePort.type() & sinkDevice) != 0) {
710b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    sinks.add(devicePort);
711d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                }
712d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim            }
713ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
714ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
715ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        private AudioDevicePort findAudioDevicePort(int type, String address) {
716ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (type == AudioManager.DEVICE_NONE) {
717ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                return null;
718ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
719ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
720ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
721ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                return null;
722ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
723ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            for (AudioPort port : devicePorts) {
724ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                AudioDevicePort devicePort = (AudioDevicePort) port;
725ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                if (devicePort.type() == type && devicePort.address().equals(address)) {
726ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                    return devicePort;
727ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                }
728ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
729ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            return null;
730c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
731c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
732c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public void release() {
733c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
73406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mAudioManager.unregisterAudioPortUpdateListener(mAudioListener);
735d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                if (mAudioPatch != null) {
736d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                    mAudioManager.releaseAudioPatch(mAudioPatch);
737d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                    mAudioPatch = null;
738d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                }
739c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                mReleased = true;
740c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
741c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
742c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
743839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        // A TvInputHardwareImpl object holds only one active session. Therefore, if a client
744839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        // attempts to call setSurface with different TvStreamConfig objects, the last call will
745839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        // prevail.
746c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
747c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public boolean setSurface(Surface surface, TvStreamConfig config)
748c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                throws RemoteException {
749c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
750c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                if (mReleased) {
751c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    throw new IllegalStateException("Device already released.");
752c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
7537587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim
754102670f16a44d4b25d7e3a252bedee0208b68c55Wonsik Kim                int result = TvInputHal.SUCCESS;
755839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                if (surface == null) {
756b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    // The value of config is ignored when surface == null.
757b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    if (mActiveConfig != null) {
758b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
759b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        mActiveConfig = null;
760b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    } else {
761b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        // We already have no active stream.
762b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        return true;
763b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    }
764839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                } else {
765b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    // It's impossible to set a non-null surface with a null config.
766b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    if (config == null) {
767b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        return false;
768b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    }
769b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    // Remove stream only if we have an existing active configuration.
770b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    if (mActiveConfig != null && !config.equals(mActiveConfig)) {
771839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
772839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        if (result != TvInputHal.SUCCESS) {
773839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                            mActiveConfig = null;
774839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                        }
775839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    }
776b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                    // Proceed only if all previous operations succeeded.
777839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    if (result == TvInputHal.SUCCESS) {
778b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config);
779b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        if (result == TvInputHal.SUCCESS) {
780b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                            mActiveConfig = config;
781b683e6e4c70da3644427cb5ec6a8342b3a91bb23Wonsik Kim                        }
782839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                    }
783839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                }
7847587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
785839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim                return result == TvInputHal.SUCCESS;
786c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
787c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
788c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
7897587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        /**
7907587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim         * Update audio configuration (source, sink, patch) all up to current state.
7917587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim         */
7927587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        private void updateAudioConfigLocked() {
7937587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            boolean sinkUpdated = updateAudioSinkLocked();
7947587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            boolean sourceUpdated = updateAudioSourceLocked();
7957587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            // We can't do updated = updateAudioSinkLocked() || updateAudioSourceLocked() here
7967587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            // because Java won't evaluate the latter if the former is true.
7977587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim
798b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            if (mAudioSource == null || mAudioSink.isEmpty() || mActiveConfig == null) {
79906c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                if (mAudioPatch != null) {
8007587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    mAudioManager.releaseAudioPatch(mAudioPatch);
8017587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    mAudioPatch = null;
80206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
80306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                return;
80406c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
80506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
80649e559353278a56963596d652af26fe1f886a280Wonsik Kim            updateVolume();
80749e559353278a56963596d652af26fe1f886a280Wonsik Kim            float volume = mSourceVolume * getMediaStreamVolume();
8088e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            AudioGainConfig sourceGainConfig = null;
80949e559353278a56963596d652af26fe1f886a280Wonsik Kim            if (mAudioSource.gains().length > 0 && volume != mCommittedVolume) {
8108e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                AudioGain sourceGain = null;
8118e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                for (AudioGain gain : mAudioSource.gains()) {
8128e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    if ((gain.mode() & AudioGain.MODE_JOINT) != 0) {
8138e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        sourceGain = gain;
8148e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        break;
8158e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    }
8168e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                }
8178e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                // NOTE: we only change the source gain in MODE_JOINT here.
8188e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                if (sourceGain != null) {
8198e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    int steps = (sourceGain.maxValue() - sourceGain.minValue())
8208e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                            / sourceGain.stepValue();
8218e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    int gainValue = sourceGain.minValue();
82249e559353278a56963596d652af26fe1f886a280Wonsik Kim                    if (volume < 1.0f) {
82349e559353278a56963596d652af26fe1f886a280Wonsik Kim                        gainValue += sourceGain.stepValue() * (int) (volume * steps + 0.5);
8248e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    } else {
8258e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        gainValue = sourceGain.maxValue();
8268e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    }
82749e559353278a56963596d652af26fe1f886a280Wonsik Kim                    // size of gain values is 1 in MODE_JOINT
82849e559353278a56963596d652af26fe1f886a280Wonsik Kim                    int[] gainValues = new int[] { gainValue };
8298e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    sourceGainConfig = sourceGain.buildConfig(AudioGain.MODE_JOINT,
8308e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                            sourceGain.channelMask(), gainValues, 0);
8318e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                } else {
8328e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                    Slog.w(TAG, "No audio source gain with MODE_JOINT support exists.");
8338e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                }
8348e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            }
8358e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim
836ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            AudioPortConfig sourceConfig = mAudioSource.activeConfig();
837b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            List<AudioPortConfig> sinkConfigs = new ArrayList<>();
838ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
8397587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
84071dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim
841b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            for (AudioDevicePort audioSink : mAudioSink) {
842b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                AudioPortConfig sinkConfig = audioSink.activeConfig();
843b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                int sinkSamplingRate = mDesiredSamplingRate;
844b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                int sinkChannelMask = mDesiredChannelMask;
845b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                int sinkFormat = mDesiredFormat;
846b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                // If sinkConfig != null and values are set to default,
847b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                // fill in the sinkConfig values.
848b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                if (sinkConfig != null) {
849b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    if (sinkSamplingRate == 0) {
850b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                        sinkSamplingRate = sinkConfig.samplingRate();
851b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    }
852b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    if (sinkChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT) {
853b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                        sinkChannelMask = sinkConfig.channelMask();
854b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    }
855b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    if (sinkFormat == AudioFormat.ENCODING_DEFAULT) {
856b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                        sinkChannelMask = sinkConfig.format();
857b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    }
85871dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                }
85971dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim
860b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                if (sinkConfig == null
861b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                        || sinkConfig.samplingRate() != sinkSamplingRate
862b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                        || sinkConfig.channelMask() != sinkChannelMask
863b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                        || sinkConfig.format() != sinkFormat) {
864b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    // Check for compatibility and reset to default if necessary.
865b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    if (!intArrayContains(audioSink.samplingRates(), sinkSamplingRate)
866b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                            && audioSink.samplingRates().length > 0) {
867b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                        sinkSamplingRate = audioSink.samplingRates()[0];
868b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    }
869b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    if (!intArrayContains(audioSink.channelMasks(), sinkChannelMask)) {
870b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                        sinkChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
871b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    }
872b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    if (!intArrayContains(audioSink.formats(), sinkFormat)) {
873b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                        sinkFormat = AudioFormat.ENCODING_DEFAULT;
874b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    }
875b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    sinkConfig = audioSink.buildConfig(sinkSamplingRate, sinkChannelMask,
876b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                            sinkFormat, null);
877b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    shouldRecreateAudioPatch = true;
87871dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                }
879b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                sinkConfigs.add(sinkConfig);
880ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
881b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            // sinkConfigs.size() == mAudioSink.size(), and mAudioSink is guaranteed to be
882b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            // non-empty at the beginning of this method.
883b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            AudioPortConfig sinkConfig = sinkConfigs.get(0);
8848e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            if (sourceConfig == null || sourceGainConfig != null) {
88571dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                int sourceSamplingRate = 0;
88671dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                if (intArrayContains(mAudioSource.samplingRates(), sinkConfig.samplingRate())) {
88771dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                    sourceSamplingRate = sinkConfig.samplingRate();
88871dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                } else if (mAudioSource.samplingRates().length > 0) {
88971dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                    // Use any sampling rate and hope audio patch can handle resampling...
89071dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                    sourceSamplingRate = mAudioSource.samplingRates()[0];
89171dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                }
89271dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                int sourceChannelMask = AudioFormat.CHANNEL_IN_DEFAULT;
89371dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                for (int inChannelMask : mAudioSource.channelMasks()) {
89471dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                    if (AudioFormat.channelCountFromOutChannelMask(sinkConfig.channelMask())
89571dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                            == AudioFormat.channelCountFromInChannelMask(inChannelMask)) {
89671dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                        sourceChannelMask = inChannelMask;
89771dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                        break;
89871dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                    }
89971dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                }
90071dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                int sourceFormat = AudioFormat.ENCODING_DEFAULT;
90171dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                if (intArrayContains(mAudioSource.formats(), sinkConfig.format())) {
90271dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                    sourceFormat = sinkConfig.format();
90371dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                }
90471dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                sourceConfig = mAudioSource.buildConfig(sourceSamplingRate, sourceChannelMask,
90571dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                        sourceFormat, sourceGainConfig);
9068e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                shouldRecreateAudioPatch = true;
9078e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            }
9088e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim            if (shouldRecreateAudioPatch) {
90949e559353278a56963596d652af26fe1f886a280Wonsik Kim                mCommittedVolume = volume;
9108e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                mAudioManager.createAudioPatch(
9118e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        audioPatchArray,
9128e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                        new AudioPortConfig[] { sourceConfig },
913b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                        sinkConfigs.toArray(new AudioPortConfig[0]));
9148e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim                mAudioPatch = audioPatchArray[0];
91571dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                if (sourceGainConfig != null) {
91671dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                    mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig);
91771dfa96f536e2ee6fbd4c5b0e517afcc1086d1fbWonsik Kim                }
918ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
919ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
920ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
921c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
9228e45a33ca8cb6b6a0cf75edf52ffdf86ffe3dd31Wonsik Kim        public void setStreamVolume(float volume) throws RemoteException {
923c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
924c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                if (mReleased) {
925c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    throw new IllegalStateException("Device already released.");
926c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
92749e559353278a56963596d652af26fe1f886a280Wonsik Kim                mSourceVolume = volume;
9287587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
929c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
930c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
931c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
932c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        @Override
933c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
934c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            synchronized (mImplLock) {
935c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                if (mReleased) {
936c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                    throw new IllegalStateException("Device already released.");
937c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                }
938c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
939610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim            if (mInfo.getType() != TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
940c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim                return false;
941c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
942c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
943c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            return false;
944c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
945c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
946c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        private boolean startCapture(Surface surface, TvStreamConfig config) {
947c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            synchronized (mImplLock) {
948c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (mReleased) {
949c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
950c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
951c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (surface == null || config == null) {
952c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
953c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
954c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
955c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
956c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
957c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
9588f24a8b60f9afc1aedb89e7ee80ce65515439600Wonsik Kim                int result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config);
959c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return result == TvInputHal.SUCCESS;
960c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
961c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
962c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
963c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        private boolean stopCapture(TvStreamConfig config) {
964c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            synchronized (mImplLock) {
965c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (mReleased) {
966c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
967c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
968c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                if (config == null) {
969c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                    return false;
970c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                }
971c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
972c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                int result = mHal.removeStream(mInfo.getDeviceId(), config);
973c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                return result == TvInputHal.SUCCESS;
974c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
975c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        }
976ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
9777587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        private boolean updateAudioSourceLocked() {
97806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
9797587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                return false;
98006c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
9817587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            AudioDevicePort previousSource = mAudioSource;
9827587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
9837587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            return mAudioSource == null ? (previousSource != null)
9847587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                    : !mAudioSource.equals(previousSource);
9857587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        }
9867587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim
9877587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim        private boolean updateAudioSinkLocked() {
9887587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
9897587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                return false;
9907587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim            }
991b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            List<AudioDevicePort> previousSink = mAudioSink;
992b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            mAudioSink = new ArrayList<>();
99306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            if (mOverrideAudioType == AudioManager.DEVICE_NONE) {
994b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                findAudioSinkFromAudioPolicy(mAudioSink);
99506c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            } else {
99606c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                AudioDevicePort audioSink =
99706c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                        findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress);
99806c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                if (audioSink != null) {
999b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                    mAudioSink.add(audioSink);
100006c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                }
100106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim            }
1002b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim
1003b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            // Returns true if mAudioSink and previousSink differs.
1004b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            if (mAudioSink.size() != previousSink.size()) {
1005b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim                return true;
1006b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            }
1007b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            previousSink.removeAll(mAudioSink);
1008b2b85f9576645e601dd38d1af83ad2737d8463d7Wonsik Kim            return !previousSink.isEmpty();
100906c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim        }
101006c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
10111f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        private void handleAudioSinkUpdated() {
10121f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            synchronized (mImplLock) {
10137587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
10141f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            }
10151f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        }
10161f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang
1017ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        @Override
1018ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
1019ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                int channelMask, int format) {
1020ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            synchronized (mImplLock) {
102106c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mOverrideAudioType = audioType;
102206c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim                mOverrideAudioAddress = audioAddress;
102306c41a3e7ffd26894b6df3a0e4bfcfa47c48c739Wonsik Kim
1024ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mDesiredSamplingRate = samplingRate;
1025ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mDesiredChannelMask = channelMask;
1026ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim                mDesiredFormat = format;
1027ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim
10287587d298c9dccbcf425204a9879b5c9e6d2a1a7eWonsik Kim                updateAudioConfigLocked();
102949e559353278a56963596d652af26fe1f886a280Wonsik Kim            }
103049e559353278a56963596d652af26fe1f886a280Wonsik Kim        }
103149e559353278a56963596d652af26fe1f886a280Wonsik Kim
103249e559353278a56963596d652af26fe1f886a280Wonsik Kim        public void onMediaStreamVolumeChanged() {
103349e559353278a56963596d652af26fe1f886a280Wonsik Kim            synchronized (mImplLock) {
103449e559353278a56963596d652af26fe1f886a280Wonsik Kim                updateAudioConfigLocked();
1035ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim            }
1036ca17a9029dd7b1b87c155e68476eae5ee4efbedeWonsik Kim        }
1037c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
1038969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
1039187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    interface Listener {
1040187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        public void onStateChanged(String inputId, int state);
1041187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        public void onHardwareDeviceAdded(TvInputHardwareInfo info);
10424f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee        public void onHardwareDeviceRemoved(TvInputHardwareInfo info);
1043546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        public void onHdmiDeviceAdded(HdmiDeviceInfo device);
1044546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        public void onHdmiDeviceRemoved(HdmiDeviceInfo device);
1045e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim        public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo device);
1046187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    }
1047969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
1048187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    private class ListenerHandler extends Handler {
1049187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        private static final int STATE_CHANGED = 1;
1050187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        private static final int HARDWARE_DEVICE_ADDED = 2;
1051187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        private static final int HARDWARE_DEVICE_REMOVED = 3;
1052546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        private static final int HDMI_DEVICE_ADDED = 4;
1053546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo        private static final int HDMI_DEVICE_REMOVED = 5;
105461daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang        private static final int HDMI_DEVICE_UPDATED = 6;
1055969167dc05a6485a32d160895871cff46fd81884Wonsik Kim
1056969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        @Override
1057969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        public final void handleMessage(Message msg) {
1058969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            switch (msg.what) {
1059187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                case STATE_CHANGED: {
1060969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    String inputId = (String) msg.obj;
1061969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    int state = msg.arg1;
1062187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    mListener.onStateChanged(inputId, state);
1063187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
1064187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
1065187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                case HARDWARE_DEVICE_ADDED: {
1066187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
1067187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    mListener.onHardwareDeviceAdded(info);
1068187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
1069187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
1070187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                case HARDWARE_DEVICE_REMOVED: {
10714f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
10724f9f57cede3de2e2aa3045e04b485b176ab22dbdJi-Hwan Lee                    mListener.onHardwareDeviceRemoved(info);
1073187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
1074187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
1075546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                case HDMI_DEVICE_ADDED: {
107661f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                    HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
1077546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    mListener.onHdmiDeviceAdded(info);
1078187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                    break;
1079187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim                }
1080546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                case HDMI_DEVICE_REMOVED: {
108161f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                    HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
1082546c635ad9a26421fbdf54efa765b5ab0a63c191Jae Seo                    mListener.onHdmiDeviceRemoved(info);
1083969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    break;
1084969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
108561daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                case HDMI_DEVICE_UPDATED: {
1086184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
1087184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    String inputId = null;
1088184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    synchronized (mLock) {
1089184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                        inputId = mHdmiInputIdMap.get(info.getId());
1090184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    }
1091184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    if (inputId != null) {
1092184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                        mListener.onHdmiDeviceUpdated(inputId, info);
1093184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    } else {
1094184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                        Slog.w(TAG, "Could not resolve input ID matching the device info; "
1095184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                                + "ignoring.");
1096184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    }
1097184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                    break;
109861daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                }
1099969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                default: {
1100969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    Slog.w(TAG, "Unhandled message: " + msg);
1101969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                    break;
1102969167dc05a6485a32d160895871cff46fd81884Wonsik Kim                }
1103969167dc05a6485a32d160895871cff46fd81884Wonsik Kim            }
1104969167dc05a6485a32d160895871cff46fd81884Wonsik Kim        }
1105969167dc05a6485a32d160895871cff46fd81884Wonsik Kim    }
1106187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim
11077474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    // Listener implementations for HdmiControlService
11087474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim
11097474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub {
11107474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        @Override
11117474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        public void onReceived(HdmiHotplugEvent event) {
11127474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            synchronized (mLock) {
11137474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                mHdmiStateMap.put(event.getPort(), event.isConnected());
1114d922a546b94119217fb790113d0001cad0432060Wonsik Kim                TvInputHardwareInfo hardwareInfo =
1115d922a546b94119217fb790113d0001cad0432060Wonsik Kim                        findHardwareInfoForHdmiPortLocked(event.getPort());
1116d922a546b94119217fb790113d0001cad0432060Wonsik Kim                if (hardwareInfo == null) {
1117d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    return;
1118d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
1119d922a546b94119217fb790113d0001cad0432060Wonsik Kim                String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
11207474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                if (inputId == null) {
11217474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                    return;
11227474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                }
11237474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
11247474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim                        convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
11257474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim            }
11267474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim        }
11277474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim    }
11287474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim
1129187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
1130187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        @Override
113161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang        public void onStatusChanged(HdmiDeviceInfo deviceInfo, int status) {
113298d760e1c3123e6db8459f605d59a5689d56268cJinsuk Kim            if (!deviceInfo.isSourceType()) return;
1133d922a546b94119217fb790113d0001cad0432060Wonsik Kim            synchronized (mLock) {
113461daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                int messageType = 0;
1135e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                Object obj = null;
113661daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                switch (status) {
113761daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    case HdmiControlManager.DEVICE_EVENT_ADD_DEVICE: {
1138cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                        if (findHdmiDeviceInfo(deviceInfo.getId()) == null) {
113961daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            mHdmiDeviceList.add(deviceInfo);
114061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        } else {
114161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
114261daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            return;
114361daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        }
114461daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        messageType = ListenerHandler.HDMI_DEVICE_ADDED;
1145e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        obj = deviceInfo;
114661daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        break;
1147d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    }
114861daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    case HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE: {
1149cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                        HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
1150cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                        if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
115161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
115261daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            return;
115361daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        }
115461daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        messageType = ListenerHandler.HDMI_DEVICE_REMOVED;
1155e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                        obj = deviceInfo;
115661daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        break;
115761daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    }
115861daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    case HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE: {
1159cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                        HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
1160cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                        if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
116161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
116261daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                            return;
116361daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        }
116461daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        mHdmiDeviceList.add(deviceInfo);
116561daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        messageType = ListenerHandler.HDMI_DEVICE_UPDATED;
1166184a6d69b1dbab8059e4f81d5674adde8e344aa3Wonsik Kim                        obj = deviceInfo;
116761daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                        break;
1168d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    }
1169d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
117061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang
1171e92f857d50d7259a4cf7ef5b88309e098338c9c1Wonsik Kim                Message msg = mHandler.obtainMessage(messageType, 0, 0, obj);
1172d922a546b94119217fb790113d0001cad0432060Wonsik Kim                if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
1173d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    msg.sendToTarget();
1174d922a546b94119217fb790113d0001cad0432060Wonsik Kim                } else {
1175d922a546b94119217fb790113d0001cad0432060Wonsik Kim                    mPendingHdmiDeviceEvents.add(msg);
1176d922a546b94119217fb790113d0001cad0432060Wonsik Kim                }
1177d922a546b94119217fb790113d0001cad0432060Wonsik Kim            }
1178187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim        }
1179cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang
1180cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang        private HdmiDeviceInfo findHdmiDeviceInfo(int id) {
1181cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang            for (HdmiDeviceInfo info : mHdmiDeviceList) {
1182cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                if (info.getId() == id) {
1183cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                    return info;
1184cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang                }
1185cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang            }
1186cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang            return null;
1187cf445877c60396cac1e8ecef75287952d2580db6Jungshik Jang        }
1188187423c0bc4b27479bc8c23bd86969429094b296Wonsik Kim    }
11897474faca5bab2fba5749cff7ed15558231307e38Jinsuk Kim
11901f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang    private final class HdmiSystemAudioModeChangeListener extends
11911f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        IHdmiSystemAudioModeChangeListener.Stub {
11921f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        @Override
11931f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        public void onStatusChanged(boolean enabled) throws RemoteException {
11941f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            synchronized (mLock) {
11951f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                for (int i = 0; i < mConnections.size(); ++i) {
11961f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                    TvInputHardwareImpl impl = mConnections.valueAt(i).getHardwareImplLocked();
119744bb3a5bd630cfe0a4813586e840d59b9cbc9f64Jungshik Jang                    if (impl != null) {
119844bb3a5bd630cfe0a4813586e840d59b9cbc9f64Jungshik Jang                        impl.handleAudioSinkUpdated();
119944bb3a5bd630cfe0a4813586e840d59b9cbc9f64Jungshik Jang                    }
12001f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang                }
12011f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang            }
12021f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang        }
12031f819bbfc80f0ae9de120e9b81250db36629a0b1Jungshik Jang    }
1204c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim}
1205