• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.tv;
18 
19 import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
20 import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY;
21 import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
22 
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.hardware.hdmi.HdmiControlManager;
28 import android.hardware.hdmi.HdmiDeviceInfo;
29 import android.hardware.hdmi.HdmiHotplugEvent;
30 import android.hardware.hdmi.IHdmiControlService;
31 import android.hardware.hdmi.IHdmiDeviceEventListener;
32 import android.hardware.hdmi.IHdmiHotplugEventListener;
33 import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
34 import android.media.AudioDevicePort;
35 import android.media.AudioFormat;
36 import android.media.AudioGain;
37 import android.media.AudioGainConfig;
38 import android.media.AudioManager;
39 import android.media.AudioPatch;
40 import android.media.AudioPort;
41 import android.media.AudioPortConfig;
42 import android.media.AudioSystem;
43 import android.media.tv.ITvInputHardware;
44 import android.media.tv.ITvInputHardwareCallback;
45 import android.media.tv.TvInputHardwareInfo;
46 import android.media.tv.TvInputInfo;
47 import android.media.tv.TvInputService.PriorityHintUseCaseType;
48 import android.media.tv.TvStreamConfig;
49 import android.media.tv.tunerresourcemanager.ResourceClientProfile;
50 import android.media.tv.tunerresourcemanager.TunerResourceManager;
51 import android.os.Handler;
52 import android.os.IBinder;
53 import android.os.Message;
54 import android.os.RemoteException;
55 import android.os.ServiceManager;
56 import android.util.ArrayMap;
57 import android.util.Slog;
58 import android.util.SparseArray;
59 import android.util.SparseBooleanArray;
60 import android.view.Surface;
61 
62 import com.android.internal.util.DumpUtils;
63 import com.android.internal.util.IndentingPrintWriter;
64 import com.android.server.SystemService;
65 
66 import java.io.FileDescriptor;
67 import java.io.PrintWriter;
68 import java.util.ArrayList;
69 import java.util.Arrays;
70 import java.util.Collections;
71 import java.util.Iterator;
72 import java.util.LinkedList;
73 import java.util.List;
74 import java.util.Map;
75 
76 /**
77  * A helper class for TvInputManagerService to handle TV input hardware.
78  *
79  * This class does a basic connection management and forwarding calls to TvInputHal which eventually
80  * calls to tv_input HAL module.
81  *
82  * @hide
83  */
84 class TvInputHardwareManager implements TvInputHal.Callback {
85     private static final String TAG = TvInputHardwareManager.class.getSimpleName();
86 
87     private final Context mContext;
88     private final Listener mListener;
89     private final TvInputHal mHal = new TvInputHal(this);
90     private final SparseArray<Connection> mConnections = new SparseArray<>();
91     private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
92     private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>();
93     /* A map from a device ID to the matching TV input ID. */
94     private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
95     /* A map from a HDMI logical address to the matching TV input ID. */
96     private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>();
97     private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>();
98 
99     private final AudioManager mAudioManager;
100     private final IHdmiHotplugEventListener mHdmiHotplugEventListener =
101             new HdmiHotplugEventListener();
102     private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener();
103     private final IHdmiSystemAudioModeChangeListener mHdmiSystemAudioModeChangeListener =
104             new HdmiSystemAudioModeChangeListener();
105     private final BroadcastReceiver mVolumeReceiver = new BroadcastReceiver() {
106         @Override
107         public void onReceive(Context context, Intent intent) {
108             handleVolumeChange(context, intent);
109         }
110     };
111     private int mCurrentIndex = 0;
112     private int mCurrentMaxIndex = 0;
113 
114     private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
115     private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
116 
117     private final List<Message> mPendingTvinputInfoEvents = new LinkedList<>();
118 
119     // Calls to mListener should happen here.
120     private final Handler mHandler = new ListenerHandler();
121 
122     private final Object mLock = new Object();
123 
TvInputHardwareManager(Context context, Listener listener)124     public TvInputHardwareManager(Context context, Listener listener) {
125         mContext = context;
126         mListener = listener;
127         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
128         mHal.init();
129     }
130 
onBootPhase(int phase)131     public void onBootPhase(int phase) {
132         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
133             IHdmiControlService hdmiControlService = IHdmiControlService.Stub.asInterface(
134                     ServiceManager.getService(Context.HDMI_CONTROL_SERVICE));
135             if (hdmiControlService != null) {
136                 try {
137                     hdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
138                     hdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
139                     hdmiControlService.addSystemAudioModeChangeListener(
140                             mHdmiSystemAudioModeChangeListener);
141                     mHdmiDeviceList.addAll(hdmiControlService.getInputDevices());
142                 } catch (RemoteException e) {
143                     Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
144                 }
145             } else {
146                 Slog.w(TAG, "HdmiControlService is not available");
147             }
148             final IntentFilter filter = new IntentFilter();
149             filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
150             filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION);
151             mContext.registerReceiver(mVolumeReceiver, filter);
152             updateVolume();
153         }
154     }
155 
156     @Override
onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs)157     public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) {
158         synchronized (mLock) {
159             Connection connection = new Connection(info);
160             connection.updateConfigsLocked(configs);
161             connection.updateCableConnectionStatusLocked(info.getCableConnectionStatus());
162             mConnections.put(info.getDeviceId(), connection);
163             buildHardwareListLocked();
164             mHandler.obtainMessage(
165                     ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget();
166             if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
167                 processPendingHdmiDeviceEventsLocked();
168             }
169         }
170     }
171 
buildHardwareListLocked()172     private void buildHardwareListLocked() {
173         mHardwareList.clear();
174         for (int i = 0; i < mConnections.size(); ++i) {
175             mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked());
176         }
177     }
178 
179     @Override
onDeviceUnavailable(int deviceId)180     public void onDeviceUnavailable(int deviceId) {
181         synchronized (mLock) {
182             Connection connection = mConnections.get(deviceId);
183             if (connection == null) {
184                 Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
185                 return;
186             }
187             connection.resetLocked(null, null, null, null, null, null);
188             mConnections.remove(deviceId);
189             buildHardwareListLocked();
190             TvInputHardwareInfo info = connection.getHardwareInfoLocked();
191             if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
192                 // Remove HDMI devices linked with this hardware.
193                 for (Iterator<HdmiDeviceInfo> it = mHdmiDeviceList.iterator(); it.hasNext();) {
194                     HdmiDeviceInfo deviceInfo = it.next();
195                     if (deviceInfo.getPortId() == info.getHdmiPortId()) {
196                         mHandler.obtainMessage(ListenerHandler.HDMI_DEVICE_REMOVED, 0, 0,
197                                 deviceInfo).sendToTarget();
198                         it.remove();
199                     }
200                 }
201             }
202             mHandler.obtainMessage(
203                     ListenerHandler.HARDWARE_DEVICE_REMOVED, 0, 0, info).sendToTarget();
204         }
205     }
206 
207     @Override
onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs, int cableConnectionStatus)208     public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs,
209             int cableConnectionStatus) {
210         synchronized (mLock) {
211             Connection connection = mConnections.get(deviceId);
212             if (connection == null) {
213                 Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with "
214                         + deviceId);
215                 return;
216             }
217             int previousConfigsLength = connection.getConfigsLengthLocked();
218             int previousCableConnectionStatus = connection.getInputStateLocked();
219             connection.updateConfigsLocked(configs);
220             String inputId = mHardwareInputIdMap.get(deviceId);
221             if (inputId != null) {
222                 if (connection.updateCableConnectionStatusLocked(cableConnectionStatus)) {
223                     if (previousCableConnectionStatus != connection.getInputStateLocked()) {
224                         mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
225                             connection.getInputStateLocked(), 0, inputId).sendToTarget();
226                     }
227                 } else {
228                     if ((previousConfigsLength == 0)
229                             != (connection.getConfigsLengthLocked() == 0)) {
230                         mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
231                             connection.getInputStateLocked(), 0, inputId).sendToTarget();
232                     }
233                 }
234             } else {
235                 Message msg = mHandler.obtainMessage(ListenerHandler.TVINPUT_INFO_ADDED,
236                     deviceId, cableConnectionStatus, connection);
237                 for (Iterator<Message> it = mPendingTvinputInfoEvents.iterator(); it.hasNext();) {
238                     if (it.next().arg1 == deviceId) {
239                     it.remove();
240                     }
241                 }
242                 mPendingTvinputInfoEvents.add(msg);
243            }
244             ITvInputHardwareCallback callback = connection.getCallbackLocked();
245             if (callback != null) {
246                 try {
247                     callback.onStreamConfigChanged(configs);
248                 } catch (RemoteException e) {
249                     Slog.e(TAG, "error in onStreamConfigurationChanged", e);
250                 }
251             }
252         }
253     }
254 
255     @Override
onFirstFrameCaptured(int deviceId, int streamId)256     public void onFirstFrameCaptured(int deviceId, int streamId) {
257         synchronized (mLock) {
258             Connection connection = mConnections.get(deviceId);
259             if (connection == null) {
260                 Slog.e(TAG, "FirstFrameCaptured: Cannot find a connection with "
261                         + deviceId);
262                 return;
263             }
264             Runnable runnable = connection.getOnFirstFrameCapturedLocked();
265             if (runnable != null) {
266                 runnable.run();
267                 connection.setOnFirstFrameCapturedLocked(null);
268             }
269         }
270     }
271 
getHardwareList()272     public List<TvInputHardwareInfo> getHardwareList() {
273         synchronized (mLock) {
274             return Collections.unmodifiableList(mHardwareList);
275         }
276     }
277 
getHdmiDeviceList()278     public List<HdmiDeviceInfo> getHdmiDeviceList() {
279         synchronized (mLock) {
280             return Collections.unmodifiableList(mHdmiDeviceList);
281         }
282     }
283 
checkUidChangedLocked( Connection connection, int callingUid, int resolvedUserId)284     private boolean checkUidChangedLocked(
285             Connection connection, int callingUid, int resolvedUserId) {
286         Integer connectionCallingUid = connection.getCallingUidLocked();
287         Integer connectionResolvedUserId = connection.getResolvedUserIdLocked();
288         return connectionCallingUid == null || connectionResolvedUserId == null
289                 || connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId;
290     }
291 
addHardwareInput(int deviceId, TvInputInfo info)292     public void addHardwareInput(int deviceId, TvInputInfo info) {
293         synchronized (mLock) {
294             String oldInputId = mHardwareInputIdMap.get(deviceId);
295             if (oldInputId != null) {
296                 Slog.w(TAG, "Trying to override previous registration: old = "
297                         + mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
298                         + info + ":" + deviceId);
299             }
300             mHardwareInputIdMap.put(deviceId, info.getId());
301             mInputMap.put(info.getId(), info);
302             processPendingTvInputInfoEventsLocked();
303             Slog.d(TAG,"deviceId ="+ deviceId+", tvinputinfo = "+info);
304 
305             // Process pending state changes
306 
307             // For logical HDMI devices, they have information from HDMI CEC signals.
308             for (int i = 0; i < mHdmiStateMap.size(); ++i) {
309                 TvInputHardwareInfo hardwareInfo =
310                         findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i));
311                 if (hardwareInfo == null) {
312                     continue;
313                 }
314                 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
315                 if (inputId != null && inputId.equals(info.getId())) {
316                     // No HDMI hotplug does not necessarily mean disconnected, as old devices may
317                     // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to
318                     // denote unknown state.
319                     int state = mHdmiStateMap.valueAt(i)
320                             ? INPUT_STATE_CONNECTED
321                             : INPUT_STATE_CONNECTED_STANDBY;
322                     mHandler.obtainMessage(
323                         ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget();
324                     return;
325                 }
326             }
327             // For the rest of the devices, we can tell by the cable connection status.
328             Connection connection = mConnections.get(deviceId);
329             if (connection != null) {
330                 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
331                     connection.getInputStateLocked(), 0, info.getId()).sendToTarget();
332             }
333         }
334     }
335 
indexOfEqualValue(SparseArray<T> map, T value)336     private static <T> int indexOfEqualValue(SparseArray<T> map, T value) {
337         for (int i = 0; i < map.size(); ++i) {
338             if (map.valueAt(i).equals(value)) {
339                 return i;
340             }
341         }
342         return -1;
343     }
344 
intArrayContains(int[] array, int value)345     private static boolean intArrayContains(int[] array, int value) {
346         for (int element : array) {
347             if (element == value) return true;
348         }
349         return false;
350     }
351 
addHdmiInput(int id, TvInputInfo info)352     public void addHdmiInput(int id, TvInputInfo info) {
353         if (info.getType() != TvInputInfo.TYPE_HDMI) {
354             throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
355         }
356         synchronized (mLock) {
357             String parentId = info.getParentId();
358             int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId);
359             if (parentIndex < 0) {
360                 throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
361             }
362             String oldInputId = mHdmiInputIdMap.get(id);
363             if (oldInputId != null) {
364                 Slog.w(TAG, "Trying to override previous registration: old = "
365                         + mInputMap.get(oldInputId) + ":" + id + ", new = "
366                         + info + ":" + id);
367             }
368             mHdmiInputIdMap.put(id, info.getId());
369             mInputMap.put(info.getId(), info);
370         }
371     }
372 
removeHardwareInput(String inputId)373     public void removeHardwareInput(String inputId) {
374         synchronized (mLock) {
375             mInputMap.remove(inputId);
376             int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
377             if (hardwareIndex >= 0) {
378                 mHardwareInputIdMap.removeAt(hardwareIndex);
379             }
380             int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId);
381             if (deviceIndex >= 0) {
382                 mHdmiInputIdMap.removeAt(deviceIndex);
383             }
384         }
385     }
386 
387     /**
388      * Create a TvInputHardware object with a specific deviceId. One service at a time can access
389      * the object, and if more than one process attempts to create hardware with the same deviceId,
390      * the latest service will get the object and all the other hardware are released. The
391      * release is notified via ITvInputHardwareCallback.onReleased().
392      */
acquireHardware(int deviceId, ITvInputHardwareCallback callback, TvInputInfo info, int callingUid, int resolvedUserId, String tvInputSessionId, @PriorityHintUseCaseType int priorityHint)393     public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
394             TvInputInfo info, int callingUid, int resolvedUserId,
395             String tvInputSessionId, @PriorityHintUseCaseType int priorityHint) {
396         if (callback == null) {
397             throw new NullPointerException();
398         }
399         TunerResourceManager trm = (TunerResourceManager) mContext.getSystemService(
400                 Context.TV_TUNER_RESOURCE_MGR_SERVICE);
401         synchronized (mLock) {
402             Connection connection = mConnections.get(deviceId);
403             if (connection == null) {
404                 Slog.e(TAG, "Invalid deviceId : " + deviceId);
405                 return null;
406             }
407 
408             ResourceClientProfile profile = new ResourceClientProfile();
409             profile.tvInputSessionId = tvInputSessionId;
410             profile.useCase = priorityHint;
411             ResourceClientProfile holderProfile = connection.getResourceClientProfileLocked();
412             if (holderProfile != null && trm != null
413                     && !trm.isHigherPriority(profile, holderProfile)) {
414                 Slog.d(TAG, "Acquiring does not show higher priority than the current holder."
415                         + " Device id:" + deviceId);
416                 return null;
417             }
418             TvInputHardwareImpl hardware =
419                     new TvInputHardwareImpl(connection.getHardwareInfoLocked());
420             try {
421                 callback.asBinder().linkToDeath(connection, 0);
422             } catch (RemoteException e) {
423                 hardware.release();
424                 return null;
425             }
426             connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId,
427                     profile);
428             return connection.getHardwareLocked();
429         }
430     }
431 
432     /**
433      * Release the specified hardware.
434      */
releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid, int resolvedUserId)435     public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid,
436             int resolvedUserId) {
437         synchronized (mLock) {
438             Connection connection = mConnections.get(deviceId);
439             if (connection == null) {
440                 Slog.e(TAG, "Invalid deviceId : " + deviceId);
441                 return;
442             }
443             if (connection.getHardwareLocked() != hardware
444                     || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
445                 return;
446             }
447             ITvInputHardwareCallback callback = connection.getCallbackLocked();
448             if (callback != null) {
449                 callback.asBinder().unlinkToDeath(connection, 0);
450             }
451             connection.resetLocked(null, null, null, null, null, null);
452         }
453     }
454 
findHardwareInfoForHdmiPortLocked(int port)455     private TvInputHardwareInfo findHardwareInfoForHdmiPortLocked(int port) {
456         for (TvInputHardwareInfo hardwareInfo : mHardwareList) {
457             if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
458                     && hardwareInfo.getHdmiPortId() == port) {
459                 return hardwareInfo;
460             }
461         }
462         return null;
463     }
464 
findDeviceIdForInputIdLocked(String inputId)465     private int findDeviceIdForInputIdLocked(String inputId) {
466         for (int i = 0; i < mConnections.size(); ++i) {
467             Connection connection = mConnections.get(i);
468             if (connection.getInfoLocked().getId().equals(inputId)) {
469                 return i;
470             }
471         }
472         return -1;
473     }
474 
475     /**
476      * Get the list of TvStreamConfig which is buffered mode.
477      */
getAvailableTvStreamConfigList(String inputId, int callingUid, int resolvedUserId)478     public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid,
479             int resolvedUserId) {
480         List<TvStreamConfig> configsList = new ArrayList<>();
481         synchronized (mLock) {
482             int deviceId = findDeviceIdForInputIdLocked(inputId);
483             if (deviceId < 0) {
484                 Slog.e(TAG, "Invalid inputId : " + inputId);
485                 return configsList;
486             }
487             Connection connection = mConnections.get(deviceId);
488             for (TvStreamConfig config : connection.getConfigsLocked()) {
489                 if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
490                     configsList.add(config);
491                 }
492             }
493         }
494         return configsList;
495     }
496 
497     /**
498      * Take a snapshot of the given TV input into the provided Surface.
499      */
captureFrame(String inputId, Surface surface, final TvStreamConfig config, int callingUid, int resolvedUserId)500     public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config,
501             int callingUid, int resolvedUserId) {
502         synchronized (mLock) {
503             int deviceId = findDeviceIdForInputIdLocked(inputId);
504             if (deviceId < 0) {
505                 Slog.e(TAG, "Invalid inputId : " + inputId);
506                 return false;
507             }
508             Connection connection = mConnections.get(deviceId);
509             final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
510             if (hardwareImpl != null) {
511                 // Stop previous capture.
512                 Runnable runnable = connection.getOnFirstFrameCapturedLocked();
513                 if (runnable != null) {
514                     runnable.run();
515                     connection.setOnFirstFrameCapturedLocked(null);
516                 }
517 
518                 boolean result = hardwareImpl.startCapture(surface, config);
519                 if (result) {
520                     connection.setOnFirstFrameCapturedLocked(new Runnable() {
521                         @Override
522                         public void run() {
523                             hardwareImpl.stopCapture(config);
524                         }
525                     });
526                 }
527                 return result;
528             }
529         }
530         return false;
531     }
532 
processPendingHdmiDeviceEventsLocked()533     private void processPendingHdmiDeviceEventsLocked() {
534         for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) {
535             Message msg = it.next();
536             HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
537             TvInputHardwareInfo hardwareInfo =
538                     findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId());
539             if (hardwareInfo != null) {
540                 msg.sendToTarget();
541                 it.remove();
542             }
543         }
544     }
545 
546 
processPendingTvInputInfoEventsLocked()547     private void processPendingTvInputInfoEventsLocked() {
548         for (Iterator<Message> it = mPendingTvinputInfoEvents.iterator(); it.hasNext(); ) {
549             Message msg = it.next();
550             int deviceId =  msg.arg1;
551             String inputId = mHardwareInputIdMap.get(deviceId);
552             if (inputId != null) {
553                 msg.sendToTarget();
554                 it.remove();
555             }
556         }
557     }
558 
559 
updateVolume()560     private void updateVolume() {
561         mCurrentMaxIndex = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
562         mCurrentIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
563     }
564 
handleVolumeChange(Context context, Intent intent)565     private void handleVolumeChange(Context context, Intent intent) {
566         String action = intent.getAction();
567         switch (action) {
568             case AudioManager.VOLUME_CHANGED_ACTION: {
569                 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
570                 if (streamType != AudioManager.STREAM_MUSIC) {
571                     return;
572                 }
573                 int index = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
574                 if (index == mCurrentIndex) {
575                     return;
576                 }
577                 mCurrentIndex = index;
578                 break;
579             }
580             case AudioManager.STREAM_MUTE_CHANGED_ACTION: {
581                 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
582                 if (streamType != AudioManager.STREAM_MUSIC) {
583                     return;
584                 }
585                 // volume index will be updated at onMediaStreamVolumeChanged() through
586                 // updateVolume().
587                 break;
588             }
589             default:
590                 Slog.w(TAG, "Unrecognized intent: " + intent);
591                 return;
592         }
593         synchronized (mLock) {
594             for (int i = 0; i < mConnections.size(); ++i) {
595                 TvInputHardwareImpl hardwareImpl = mConnections.valueAt(i).getHardwareImplLocked();
596                 if (hardwareImpl != null) {
597                     hardwareImpl.onMediaStreamVolumeChanged();
598                 }
599             }
600         }
601     }
602 
getMediaStreamVolume()603     private float getMediaStreamVolume() {
604         return (float) mCurrentIndex / (float) mCurrentMaxIndex;
605     }
606 
dump(FileDescriptor fd, final PrintWriter writer, String[] args)607     public void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
608         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
609         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
610 
611         synchronized (mLock) {
612             pw.println("TvInputHardwareManager Info:");
613             pw.increaseIndent();
614             pw.println("mConnections: deviceId -> Connection");
615             pw.increaseIndent();
616             for (int i = 0; i < mConnections.size(); i++) {
617                 int deviceId = mConnections.keyAt(i);
618                 Connection mConnection = mConnections.valueAt(i);
619                 pw.println(deviceId + ": " + mConnection);
620 
621             }
622             pw.decreaseIndent();
623 
624             pw.println("mHardwareList:");
625             pw.increaseIndent();
626             for (TvInputHardwareInfo tvInputHardwareInfo : mHardwareList) {
627                 pw.println(tvInputHardwareInfo);
628             }
629             pw.decreaseIndent();
630 
631             pw.println("mHdmiDeviceList:");
632             pw.increaseIndent();
633             for (HdmiDeviceInfo hdmiDeviceInfo : mHdmiDeviceList) {
634                 pw.println(hdmiDeviceInfo);
635             }
636             pw.decreaseIndent();
637 
638             pw.println("mHardwareInputIdMap: deviceId -> inputId");
639             pw.increaseIndent();
640             for (int i = 0 ; i < mHardwareInputIdMap.size(); i++) {
641                 int deviceId = mHardwareInputIdMap.keyAt(i);
642                 String inputId = mHardwareInputIdMap.valueAt(i);
643                 pw.println(deviceId + ": " + inputId);
644             }
645             pw.decreaseIndent();
646 
647             pw.println("mHdmiInputIdMap: id -> inputId");
648             pw.increaseIndent();
649             for (int i = 0; i < mHdmiInputIdMap.size(); i++) {
650                 int id = mHdmiInputIdMap.keyAt(i);
651                 String inputId = mHdmiInputIdMap.valueAt(i);
652                 pw.println(id + ": " + inputId);
653             }
654             pw.decreaseIndent();
655 
656             pw.println("mInputMap: inputId -> inputInfo");
657             pw.increaseIndent();
658             for(Map.Entry<String, TvInputInfo> entry : mInputMap.entrySet()) {
659                 pw.println(entry.getKey() + ": " + entry.getValue());
660             }
661             pw.decreaseIndent();
662             pw.decreaseIndent();
663         }
664     }
665 
666     private class Connection implements IBinder.DeathRecipient {
667         private TvInputHardwareInfo mHardwareInfo;
668         private TvInputInfo mInfo;
669         private TvInputHardwareImpl mHardware = null;
670         private ITvInputHardwareCallback mCallback;
671         private TvStreamConfig[] mConfigs = null;
672         private Integer mCallingUid = null;
673         private Integer mResolvedUserId = null;
674         private Runnable mOnFirstFrameCaptured;
675         private ResourceClientProfile mResourceClientProfile = null;
676         private boolean mIsCableConnectionStatusUpdated = false;
677 
Connection(TvInputHardwareInfo hardwareInfo)678         public Connection(TvInputHardwareInfo hardwareInfo) {
679             mHardwareInfo = hardwareInfo;
680         }
681 
682         // *Locked methods assume TvInputHardwareManager.mLock is held.
683 
resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback, TvInputInfo info, Integer callingUid, Integer resolvedUserId, ResourceClientProfile profile)684         public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
685                 TvInputInfo info, Integer callingUid, Integer resolvedUserId,
686                 ResourceClientProfile profile) {
687             if (mHardware != null) {
688                 try {
689                     mCallback.onReleased();
690                 } catch (RemoteException e) {
691                     Slog.e(TAG, "error in Connection::resetLocked", e);
692                 }
693                 mHardware.release();
694             }
695             mHardware = hardware;
696             mCallback = callback;
697             mInfo = info;
698             mCallingUid = callingUid;
699             mResolvedUserId = resolvedUserId;
700             mOnFirstFrameCaptured = null;
701             mResourceClientProfile = profile;
702 
703             if (mHardware != null && mCallback != null) {
704                 try {
705                     mCallback.onStreamConfigChanged(getConfigsLocked());
706                 } catch (RemoteException e) {
707                     Slog.e(TAG, "error in Connection::resetLocked", e);
708                 }
709             }
710         }
711 
updateConfigsLocked(TvStreamConfig[] configs)712         public void updateConfigsLocked(TvStreamConfig[] configs) {
713             mConfigs = configs;
714         }
715 
getHardwareInfoLocked()716         public TvInputHardwareInfo getHardwareInfoLocked() {
717             return mHardwareInfo;
718         }
719 
getInfoLocked()720         public TvInputInfo getInfoLocked() {
721             return mInfo;
722         }
723 
getHardwareLocked()724         public ITvInputHardware getHardwareLocked() {
725             return mHardware;
726         }
727 
getHardwareImplLocked()728         public TvInputHardwareImpl getHardwareImplLocked() {
729             return mHardware;
730         }
731 
getCallbackLocked()732         public ITvInputHardwareCallback getCallbackLocked() {
733             return mCallback;
734         }
735 
getConfigsLocked()736         public TvStreamConfig[] getConfigsLocked() {
737             return mConfigs;
738         }
739 
getCallingUidLocked()740         public Integer getCallingUidLocked() {
741             return mCallingUid;
742         }
743 
getResolvedUserIdLocked()744         public Integer getResolvedUserIdLocked() {
745             return mResolvedUserId;
746         }
747 
setOnFirstFrameCapturedLocked(Runnable runnable)748         public void setOnFirstFrameCapturedLocked(Runnable runnable) {
749             mOnFirstFrameCaptured = runnable;
750         }
751 
getOnFirstFrameCapturedLocked()752         public Runnable getOnFirstFrameCapturedLocked() {
753             return mOnFirstFrameCaptured;
754         }
755 
getResourceClientProfileLocked()756         public ResourceClientProfile getResourceClientProfileLocked() {
757             return mResourceClientProfile;
758         }
759 
760         @Override
binderDied()761         public void binderDied() {
762             synchronized (mLock) {
763                 resetLocked(null, null, null, null, null, null);
764             }
765         }
766 
toString()767         public String toString() {
768             return "Connection{"
769                     + " mHardwareInfo: " + mHardwareInfo
770                     + ", mInfo: " + mInfo
771                     + ", mCallback: " + mCallback
772                     + ", mConfigs: " + Arrays.toString(mConfigs)
773                     + ", mCallingUid: " + mCallingUid
774                     + ", mResolvedUserId: " + mResolvedUserId
775                     + ", mResourceClientProfile: " + mResourceClientProfile
776                     + " }";
777         }
778 
updateCableConnectionStatusLocked(int cableConnectionStatus)779         public boolean updateCableConnectionStatusLocked(int cableConnectionStatus) {
780             // Update connection status only if it's not default value
781             if (cableConnectionStatus != TvInputHardwareInfo.CABLE_CONNECTION_STATUS_UNKNOWN
782                     || mIsCableConnectionStatusUpdated) {
783                 mIsCableConnectionStatusUpdated = true;
784                 mHardwareInfo = mHardwareInfo.toBuilder()
785                     .cableConnectionStatus(cableConnectionStatus).build();
786             }
787             return mIsCableConnectionStatusUpdated;
788         }
789 
getConfigsLengthLocked()790         private int getConfigsLengthLocked() {
791             return mConfigs == null ? 0 : mConfigs.length;
792         }
793 
getInputStateLocked()794         private int getInputStateLocked() {
795             int configsLength = getConfigsLengthLocked();
796             if (configsLength > 0) {
797                 if (!mIsCableConnectionStatusUpdated) {
798                     return INPUT_STATE_CONNECTED;
799                 }
800             }
801             switch (mHardwareInfo.getCableConnectionStatus()) {
802                 case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_CONNECTED:
803                     return INPUT_STATE_CONNECTED;
804                 case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_DISCONNECTED:
805                     return INPUT_STATE_DISCONNECTED;
806                 case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_UNKNOWN:
807                 default:
808                     return INPUT_STATE_CONNECTED_STANDBY;
809             }
810         }
811     }
812 
813     private class TvInputHardwareImpl extends ITvInputHardware.Stub {
814         private final TvInputHardwareInfo mInfo;
815         private boolean mReleased = false;
816         private final Object mImplLock = new Object();
817 
818         private final AudioManager.OnAudioPortUpdateListener mAudioListener =
819                 new AudioManager.OnAudioPortUpdateListener() {
820             @Override
821             public void onAudioPortListUpdate(AudioPort[] portList) {
822                 synchronized (mImplLock) {
823                     updateAudioConfigLocked();
824                 }
825             }
826 
827             @Override
828             public void onAudioPatchListUpdate(AudioPatch[] patchList) {
829                 // No-op
830             }
831 
832             @Override
833             public void onServiceDied() {
834                 synchronized (mImplLock) {
835                     mAudioSource = null;
836                     mAudioSink.clear();
837                     if (mAudioPatch != null) {
838                         mAudioManager.releaseAudioPatch(mAudioPatch);
839                         mAudioPatch = null;
840                     }
841                 }
842             }
843         };
844         private int mOverrideAudioType = AudioManager.DEVICE_NONE;
845         private String mOverrideAudioAddress = "";
846         private AudioDevicePort mAudioSource;
847         private List<AudioDevicePort> mAudioSink = new ArrayList<>();
848         private AudioPatch mAudioPatch = null;
849         // Set to an invalid value for a volume, so that current volume can be applied at the
850         // first call to updateAudioConfigLocked().
851         private float mCommittedVolume = -1f;
852         private float mSourceVolume = 0.0f;
853 
854         private TvStreamConfig mActiveConfig = null;
855 
856         private int mDesiredSamplingRate = 0;
857         private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
858         private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT;
859 
TvInputHardwareImpl(TvInputHardwareInfo info)860         public TvInputHardwareImpl(TvInputHardwareInfo info) {
861             mInfo = info;
862             mAudioManager.registerAudioPortUpdateListener(mAudioListener);
863             if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
864                 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
865                 findAudioSinkFromAudioPolicy(mAudioSink);
866             }
867         }
868 
findAudioSinkFromAudioPolicy(List<AudioDevicePort> sinks)869         private void findAudioSinkFromAudioPolicy(List<AudioDevicePort> sinks) {
870             sinks.clear();
871             ArrayList<AudioDevicePort> devicePorts = new ArrayList<>();
872             if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
873                 return;
874             }
875             int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
876             for (AudioDevicePort port : devicePorts) {
877                 if ((port.type() & sinkDevice) != 0 &&
878                     (port.type() & AudioSystem.DEVICE_BIT_IN) == 0) {
879                     sinks.add(port);
880                 }
881             }
882         }
883 
findAudioDevicePort(int type, String address)884         private AudioDevicePort findAudioDevicePort(int type, String address) {
885             if (type == AudioManager.DEVICE_NONE) {
886                 return null;
887             }
888             ArrayList<AudioDevicePort> devicePorts = new ArrayList<>();
889             if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
890                 return null;
891             }
892             for (AudioDevicePort port : devicePorts) {
893                 if (port.type() == type && port.address().equals(address)) {
894                     return port;
895                 }
896             }
897             return null;
898         }
899 
release()900         public void release() {
901             synchronized (mImplLock) {
902                 mAudioManager.unregisterAudioPortUpdateListener(mAudioListener);
903                 if (mAudioPatch != null) {
904                     mAudioManager.releaseAudioPatch(mAudioPatch);
905                     mAudioPatch = null;
906                 }
907                 mReleased = true;
908             }
909         }
910 
911         // A TvInputHardwareImpl object holds only one active session. Therefore, if a client
912         // attempts to call setSurface with different TvStreamConfig objects, the last call will
913         // prevail.
914         @Override
setSurface(Surface surface, TvStreamConfig config)915         public boolean setSurface(Surface surface, TvStreamConfig config)
916                 throws RemoteException {
917             synchronized (mImplLock) {
918                 if (mReleased) {
919                     throw new IllegalStateException("Device already released.");
920                 }
921 
922                 int result = TvInputHal.SUCCESS;
923                 if (surface == null) {
924                     // The value of config is ignored when surface == null.
925                     if (mActiveConfig != null) {
926                         result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
927                         mActiveConfig = null;
928                     } else {
929                         // We already have no active stream.
930                         return true;
931                     }
932                 } else {
933                     // It's impossible to set a non-null surface with a null config.
934                     if (config == null) {
935                         return false;
936                     }
937                     // Remove stream only if we have an existing active configuration.
938                     if (mActiveConfig != null && !config.equals(mActiveConfig)) {
939                         result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
940                         if (result != TvInputHal.SUCCESS) {
941                             mActiveConfig = null;
942                         }
943                     }
944                     // Proceed only if all previous operations succeeded.
945                     if (result == TvInputHal.SUCCESS) {
946                         result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config);
947                         if (result == TvInputHal.SUCCESS) {
948                             mActiveConfig = config;
949                         }
950                     }
951                 }
952                 updateAudioConfigLocked();
953                 return result == TvInputHal.SUCCESS;
954             }
955         }
956 
957         /**
958          * Update audio configuration (source, sink, patch) all up to current state.
959          */
updateAudioConfigLocked()960         private void updateAudioConfigLocked() {
961             boolean sinkUpdated = updateAudioSinkLocked();
962             boolean sourceUpdated = updateAudioSourceLocked();
963             // We can't do updated = updateAudioSinkLocked() || updateAudioSourceLocked() here
964             // because Java won't evaluate the latter if the former is true.
965 
966             if (mAudioSource == null || mAudioSink.isEmpty() || mActiveConfig == null) {
967                 if (mAudioPatch != null) {
968                     mAudioManager.releaseAudioPatch(mAudioPatch);
969                     mAudioPatch = null;
970                 }
971                 return;
972             }
973 
974             updateVolume();
975             float volume = mSourceVolume * getMediaStreamVolume();
976             AudioGainConfig sourceGainConfig = null;
977             if (mAudioSource.gains().length > 0 && volume != mCommittedVolume) {
978                 AudioGain sourceGain = null;
979                 for (AudioGain gain : mAudioSource.gains()) {
980                     if ((gain.mode() & AudioGain.MODE_JOINT) != 0) {
981                         sourceGain = gain;
982                         break;
983                     }
984                 }
985                 // NOTE: we only change the source gain in MODE_JOINT here.
986                 if (sourceGain != null) {
987                     int steps = (sourceGain.maxValue() - sourceGain.minValue())
988                             / sourceGain.stepValue();
989                     int gainValue = sourceGain.minValue();
990                     if (volume < 1.0f) {
991                         gainValue += sourceGain.stepValue() * (int) (volume * steps + 0.5);
992                     } else {
993                         gainValue = sourceGain.maxValue();
994                     }
995                     // size of gain values is 1 in MODE_JOINT
996                     int[] gainValues = new int[] { gainValue };
997                     sourceGainConfig = sourceGain.buildConfig(AudioGain.MODE_JOINT,
998                             sourceGain.channelMask(), gainValues, 0);
999                 } else {
1000                     Slog.w(TAG, "No audio source gain with MODE_JOINT support exists.");
1001                 }
1002             }
1003 
1004             AudioPortConfig sourceConfig = mAudioSource.activeConfig();
1005             List<AudioPortConfig> sinkConfigs = new ArrayList<>();
1006             AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
1007             boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated || mAudioPatch == null;
1008 
1009             for (AudioDevicePort audioSink : mAudioSink) {
1010                 AudioPortConfig sinkConfig = audioSink.activeConfig();
1011                 int sinkSamplingRate = mDesiredSamplingRate;
1012                 int sinkChannelMask = mDesiredChannelMask;
1013                 int sinkFormat = mDesiredFormat;
1014                 // If sinkConfig != null and values are set to default,
1015                 // fill in the sinkConfig values.
1016                 if (sinkConfig != null) {
1017                     if (sinkSamplingRate == 0) {
1018                         sinkSamplingRate = sinkConfig.samplingRate();
1019                     }
1020                     if (sinkChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT) {
1021                         sinkChannelMask = sinkConfig.channelMask();
1022                     }
1023                     if (sinkFormat == AudioFormat.ENCODING_DEFAULT) {
1024                         sinkFormat = sinkConfig.format();
1025                     }
1026                 }
1027 
1028                 if (sinkConfig == null
1029                         || sinkConfig.samplingRate() != sinkSamplingRate
1030                         || sinkConfig.channelMask() != sinkChannelMask
1031                         || sinkConfig.format() != sinkFormat) {
1032                     // Check for compatibility and reset to default if necessary.
1033                     if (!intArrayContains(audioSink.samplingRates(), sinkSamplingRate)
1034                             && audioSink.samplingRates().length > 0) {
1035                         sinkSamplingRate = audioSink.samplingRates()[0];
1036                     }
1037                     if (!intArrayContains(audioSink.channelMasks(), sinkChannelMask)) {
1038                         sinkChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
1039                     }
1040                     if (!intArrayContains(audioSink.formats(), sinkFormat)) {
1041                         sinkFormat = AudioFormat.ENCODING_DEFAULT;
1042                     }
1043                     sinkConfig = audioSink.buildConfig(sinkSamplingRate, sinkChannelMask,
1044                             sinkFormat, null);
1045                     shouldRecreateAudioPatch = true;
1046                 }
1047                 sinkConfigs.add(sinkConfig);
1048             }
1049             // sinkConfigs.size() == mAudioSink.size(), and mAudioSink is guaranteed to be
1050             // non-empty at the beginning of this method.
1051             AudioPortConfig sinkConfig = sinkConfigs.get(0);
1052             if (sourceConfig == null || sourceGainConfig != null) {
1053                 int sourceSamplingRate = 0;
1054                 if (intArrayContains(mAudioSource.samplingRates(), sinkConfig.samplingRate())) {
1055                     sourceSamplingRate = sinkConfig.samplingRate();
1056                 } else if (mAudioSource.samplingRates().length > 0) {
1057                     // Use any sampling rate and hope audio patch can handle resampling...
1058                     sourceSamplingRate = mAudioSource.samplingRates()[0];
1059                 }
1060                 int sourceChannelMask = AudioFormat.CHANNEL_IN_DEFAULT;
1061                 for (int inChannelMask : mAudioSource.channelMasks()) {
1062                     if (AudioFormat.channelCountFromOutChannelMask(sinkConfig.channelMask())
1063                             == AudioFormat.channelCountFromInChannelMask(inChannelMask)) {
1064                         sourceChannelMask = inChannelMask;
1065                         break;
1066                     }
1067                 }
1068                 int sourceFormat = AudioFormat.ENCODING_DEFAULT;
1069                 if (intArrayContains(mAudioSource.formats(), sinkConfig.format())) {
1070                     sourceFormat = sinkConfig.format();
1071                 }
1072                 sourceConfig = mAudioSource.buildConfig(sourceSamplingRate, sourceChannelMask,
1073                         sourceFormat, sourceGainConfig);
1074                 shouldRecreateAudioPatch = true;
1075             }
1076             if (shouldRecreateAudioPatch) {
1077                 mCommittedVolume = volume;
1078                 // only recreate if  something was updated or audioPath is null
1079                 if (mAudioPatch == null || sinkUpdated ||sourceUpdated ) {
1080                     if (mAudioPatch != null) {
1081                         mAudioManager.releaseAudioPatch(mAudioPatch);
1082                         audioPatchArray[0] = null;
1083                     }
1084                     mAudioManager.createAudioPatch(
1085                         audioPatchArray,
1086                         new AudioPortConfig[] { sourceConfig },
1087                         sinkConfigs.toArray(new AudioPortConfig[sinkConfigs.size()]));
1088                     mAudioPatch = audioPatchArray[0];
1089                 }
1090              }
1091 
1092             if (sourceGainConfig != null) {
1093                 mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig);
1094             }
1095         }
1096 
1097         @Override
setStreamVolume(float volume)1098         public void setStreamVolume(float volume) throws RemoteException {
1099             synchronized (mImplLock) {
1100                 if (mReleased) {
1101                     throw new IllegalStateException("Device already released.");
1102                 }
1103                 mSourceVolume = volume;
1104                 updateAudioConfigLocked();
1105             }
1106         }
1107 
startCapture(Surface surface, TvStreamConfig config)1108         private boolean startCapture(Surface surface, TvStreamConfig config) {
1109             synchronized (mImplLock) {
1110                 if (mReleased) {
1111                     return false;
1112                 }
1113                 if (surface == null || config == null) {
1114                     return false;
1115                 }
1116                 if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
1117                     return false;
1118                 }
1119 
1120                 int result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config);
1121                 return result == TvInputHal.SUCCESS;
1122             }
1123         }
1124 
stopCapture(TvStreamConfig config)1125         private boolean stopCapture(TvStreamConfig config) {
1126             synchronized (mImplLock) {
1127                 if (mReleased) {
1128                     return false;
1129                 }
1130                 if (config == null) {
1131                     return false;
1132                 }
1133 
1134                 int result = mHal.removeStream(mInfo.getDeviceId(), config);
1135                 return result == TvInputHal.SUCCESS;
1136             }
1137         }
1138 
updateAudioSourceLocked()1139         private boolean updateAudioSourceLocked() {
1140             if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
1141                 return false;
1142             }
1143             AudioDevicePort previousSource = mAudioSource;
1144             mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
1145             return mAudioSource == null ? (previousSource != null)
1146                     : !mAudioSource.equals(previousSource);
1147         }
1148 
updateAudioSinkLocked()1149         private boolean updateAudioSinkLocked() {
1150             if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
1151                 return false;
1152             }
1153             List<AudioDevicePort> previousSink = mAudioSink;
1154             mAudioSink = new ArrayList<>();
1155             if (mOverrideAudioType == AudioManager.DEVICE_NONE) {
1156                 findAudioSinkFromAudioPolicy(mAudioSink);
1157             } else {
1158                 AudioDevicePort audioSink =
1159                         findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress);
1160                 if (audioSink != null) {
1161                     mAudioSink.add(audioSink);
1162                 }
1163             }
1164 
1165             // Returns true if mAudioSink and previousSink differs.
1166             if (mAudioSink.size() != previousSink.size()) {
1167                 return true;
1168             }
1169             previousSink.removeAll(mAudioSink);
1170             return !previousSink.isEmpty();
1171         }
1172 
handleAudioSinkUpdated()1173         private void handleAudioSinkUpdated() {
1174             synchronized (mImplLock) {
1175                 updateAudioConfigLocked();
1176             }
1177         }
1178 
1179         @Override
overrideAudioSink(int audioType, String audioAddress, int samplingRate, int channelMask, int format)1180         public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
1181                 int channelMask, int format) {
1182             synchronized (mImplLock) {
1183                 mOverrideAudioType = audioType;
1184                 mOverrideAudioAddress = audioAddress;
1185 
1186                 mDesiredSamplingRate = samplingRate;
1187                 mDesiredChannelMask = channelMask;
1188                 mDesiredFormat = format;
1189 
1190                 updateAudioConfigLocked();
1191             }
1192         }
1193 
onMediaStreamVolumeChanged()1194         public void onMediaStreamVolumeChanged() {
1195             synchronized (mImplLock) {
1196                 updateAudioConfigLocked();
1197             }
1198         }
1199     }
1200 
1201     interface Listener {
onStateChanged(String inputId, int state)1202         void onStateChanged(String inputId, int state);
onHardwareDeviceAdded(TvInputHardwareInfo info)1203         void onHardwareDeviceAdded(TvInputHardwareInfo info);
onHardwareDeviceRemoved(TvInputHardwareInfo info)1204         void onHardwareDeviceRemoved(TvInputHardwareInfo info);
onHdmiDeviceAdded(HdmiDeviceInfo device)1205         void onHdmiDeviceAdded(HdmiDeviceInfo device);
onHdmiDeviceRemoved(HdmiDeviceInfo device)1206         void onHdmiDeviceRemoved(HdmiDeviceInfo device);
onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo device)1207         void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo device);
1208     }
1209 
1210     private class ListenerHandler extends Handler {
1211         private static final int STATE_CHANGED = 1;
1212         private static final int HARDWARE_DEVICE_ADDED = 2;
1213         private static final int HARDWARE_DEVICE_REMOVED = 3;
1214         private static final int HDMI_DEVICE_ADDED = 4;
1215         private static final int HDMI_DEVICE_REMOVED = 5;
1216         private static final int HDMI_DEVICE_UPDATED = 6;
1217         private static final int TVINPUT_INFO_ADDED = 7;
1218 
1219         @Override
handleMessage(Message msg)1220         public final void handleMessage(Message msg) {
1221             switch (msg.what) {
1222                 case STATE_CHANGED: {
1223                     String inputId = (String) msg.obj;
1224                     int state = msg.arg1;
1225                     mListener.onStateChanged(inputId, state);
1226                     break;
1227                 }
1228                 case HARDWARE_DEVICE_ADDED: {
1229                     TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
1230                     mListener.onHardwareDeviceAdded(info);
1231                     break;
1232                 }
1233                 case HARDWARE_DEVICE_REMOVED: {
1234                     TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
1235                     mListener.onHardwareDeviceRemoved(info);
1236                     break;
1237                 }
1238                 case HDMI_DEVICE_ADDED: {
1239                     HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
1240                     mListener.onHdmiDeviceAdded(info);
1241                     break;
1242                 }
1243                 case HDMI_DEVICE_REMOVED: {
1244                     HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
1245                     mListener.onHdmiDeviceRemoved(info);
1246                     break;
1247                 }
1248                 case HDMI_DEVICE_UPDATED: {
1249                     HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
1250                     String inputId;
1251                     synchronized (mLock) {
1252                         inputId = mHdmiInputIdMap.get(info.getId());
1253                     }
1254                     if (inputId != null) {
1255                         mListener.onHdmiDeviceUpdated(inputId, info);
1256                     } else {
1257                         Slog.w(TAG, "Could not resolve input ID matching the device info; "
1258                                 + "ignoring.");
1259                     }
1260                     break;
1261                 }
1262                 case TVINPUT_INFO_ADDED: {
1263                     int deviceId = msg.arg1;
1264                     int cableConnectionStatus = msg.arg2;
1265                     Connection connection =(Connection)msg.obj;
1266 
1267                     int previousConfigsLength = connection.getConfigsLengthLocked();
1268                     int previousCableConnectionStatus = connection.getInputStateLocked();
1269                     String inputId = mHardwareInputIdMap.get(deviceId);
1270 
1271                     if (inputId != null) {
1272                         if (connection.updateCableConnectionStatusLocked(cableConnectionStatus)) {
1273                             if (previousCableConnectionStatus != connection.getInputStateLocked()) {
1274                                 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
1275                                     connection.getInputStateLocked(), 0, inputId).sendToTarget();
1276                             }
1277                         } else {
1278                             if ((previousConfigsLength == 0)
1279                                     != (connection.getConfigsLengthLocked() == 0)) {
1280                                 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
1281                                     connection.getInputStateLocked(), 0, inputId).sendToTarget();
1282                             }
1283                         }
1284                     }
1285                     break;
1286                 }
1287                 default: {
1288                     Slog.w(TAG, "Unhandled message: " + msg);
1289                     break;
1290                 }
1291             }
1292         }
1293     }
1294 
1295     // Listener implementations for HdmiControlService
1296 
1297     private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub {
1298         @Override
onReceived(HdmiHotplugEvent event)1299         public void onReceived(HdmiHotplugEvent event) {
1300             synchronized (mLock) {
1301                 mHdmiStateMap.put(event.getPort(), event.isConnected());
1302                 TvInputHardwareInfo hardwareInfo =
1303                         findHardwareInfoForHdmiPortLocked(event.getPort());
1304                 if (hardwareInfo == null) {
1305                     return;
1306                 }
1307                 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
1308                 if (inputId == null) {
1309                     return;
1310                 }
1311                 // No HDMI hotplug does not necessarily mean disconnected, as old devices may
1312                 // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to
1313                 // denote unknown state.
1314                 int state = event.isConnected()
1315                         ? INPUT_STATE_CONNECTED
1316                         : INPUT_STATE_CONNECTED_STANDBY;
1317                 mHandler.obtainMessage(
1318                     ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget();
1319             }
1320         }
1321     }
1322 
1323     private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
1324         @Override
onStatusChanged(HdmiDeviceInfo deviceInfo, int status)1325         public void onStatusChanged(HdmiDeviceInfo deviceInfo, int status) {
1326             if (!deviceInfo.isSourceType()) return;
1327             synchronized (mLock) {
1328                 int messageType = 0;
1329                 Object obj = null;
1330                 switch (status) {
1331                     case HdmiControlManager.DEVICE_EVENT_ADD_DEVICE: {
1332                         if (findHdmiDeviceInfo(deviceInfo.getId()) == null) {
1333                             mHdmiDeviceList.add(deviceInfo);
1334                         } else {
1335                             Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
1336                             return;
1337                         }
1338                         messageType = ListenerHandler.HDMI_DEVICE_ADDED;
1339                         obj = deviceInfo;
1340                         break;
1341                     }
1342                     case HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE: {
1343                         HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
1344                         if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
1345                             Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
1346                             return;
1347                         }
1348                         messageType = ListenerHandler.HDMI_DEVICE_REMOVED;
1349                         obj = deviceInfo;
1350                         break;
1351                     }
1352                     case HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE: {
1353                         HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
1354                         if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
1355                             Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
1356                             return;
1357                         }
1358                         mHdmiDeviceList.add(deviceInfo);
1359                         messageType = ListenerHandler.HDMI_DEVICE_UPDATED;
1360                         obj = deviceInfo;
1361                         break;
1362                     }
1363                 }
1364 
1365                 Message msg = mHandler.obtainMessage(messageType, 0, 0, obj);
1366                 if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
1367                     msg.sendToTarget();
1368                 } else {
1369                     mPendingHdmiDeviceEvents.add(msg);
1370                 }
1371             }
1372         }
1373 
findHdmiDeviceInfo(int id)1374         private HdmiDeviceInfo findHdmiDeviceInfo(int id) {
1375             for (HdmiDeviceInfo info : mHdmiDeviceList) {
1376                 if (info.getId() == id) {
1377                     return info;
1378                 }
1379             }
1380             return null;
1381         }
1382     }
1383 
1384     private final class HdmiSystemAudioModeChangeListener extends
1385         IHdmiSystemAudioModeChangeListener.Stub {
1386         @Override
onStatusChanged(boolean enabled)1387         public void onStatusChanged(boolean enabled) throws RemoteException {
1388             synchronized (mLock) {
1389                 for (int i = 0; i < mConnections.size(); ++i) {
1390                     TvInputHardwareImpl impl = mConnections.valueAt(i).getHardwareImplLocked();
1391                     if (impl != null) {
1392                         impl.handleAudioSinkUpdated();
1393                     }
1394                 }
1395             }
1396         }
1397     }
1398 }
1399