• 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.hdmi;
18 
19 import static android.media.tv.flags.Flags.hdmiControlEnhancedBehavior;
20 import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
21 import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE;
22 import static android.hardware.hdmi.HdmiControlManager.EARC_FEATURE_DISABLED;
23 import static android.hardware.hdmi.HdmiControlManager.EARC_FEATURE_ENABLED;
24 import static android.hardware.hdmi.HdmiControlManager.HDMI_CEC_CONTROL_DISABLED;
25 import static android.hardware.hdmi.HdmiControlManager.HDMI_CEC_CONTROL_ENABLED;
26 import static android.hardware.hdmi.HdmiControlManager.POWER_CONTROL_MODE_NONE;
27 import static android.hardware.hdmi.HdmiControlManager.SOUNDBAR_MODE_DISABLED;
28 import static android.hardware.hdmi.HdmiControlManager.SOUNDBAR_MODE_ENABLED;
29 import static android.hardware.hdmi.HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED;
30 
31 import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
32 import static com.android.server.hdmi.Constants.DISABLED;
33 import static com.android.server.hdmi.Constants.ENABLED;
34 import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_ARC_PENDING;
35 import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_UNKNOWN;
36 import static com.android.server.hdmi.Constants.OPTION_MHL_ENABLE;
37 import static com.android.server.hdmi.Constants.OPTION_MHL_INPUT_SWITCHING;
38 import static com.android.server.hdmi.Constants.OPTION_MHL_POWER_CHARGE;
39 import static com.android.server.hdmi.Constants.OPTION_MHL_SERVICE_CONTROL;
40 import static com.android.server.power.ShutdownThread.SHUTDOWN_ACTION_PROPERTY;
41 
42 import android.annotation.IntDef;
43 import android.annotation.NonNull;
44 import android.annotation.Nullable;
45 import android.annotation.RequiresPermission;
46 import android.content.BroadcastReceiver;
47 import android.content.ContentResolver;
48 import android.content.Context;
49 import android.content.Intent;
50 import android.content.IntentFilter;
51 import android.database.ContentObserver;
52 import android.hardware.display.DeviceProductInfo;
53 import android.hardware.display.DisplayManager;
54 import android.hardware.hdmi.DeviceFeatures;
55 import android.hardware.hdmi.HdmiControlManager;
56 import android.hardware.hdmi.HdmiDeviceInfo;
57 import android.hardware.hdmi.HdmiHotplugEvent;
58 import android.hardware.hdmi.HdmiPortInfo;
59 import android.hardware.hdmi.IHdmiCecSettingChangeListener;
60 import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
61 import android.hardware.hdmi.IHdmiControlCallback;
62 import android.hardware.hdmi.IHdmiControlService;
63 import android.hardware.hdmi.IHdmiControlStatusChangeListener;
64 import android.hardware.hdmi.IHdmiDeviceEventListener;
65 import android.hardware.hdmi.IHdmiHotplugEventListener;
66 import android.hardware.hdmi.IHdmiInputChangeListener;
67 import android.hardware.hdmi.IHdmiMhlVendorCommandListener;
68 import android.hardware.hdmi.IHdmiRecordListener;
69 import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
70 import android.hardware.hdmi.IHdmiVendorCommandListener;
71 import android.hardware.tv.cec.V1_0.SendMessageResult;
72 import android.media.AudioAttributes;
73 import android.media.AudioDescriptor;
74 import android.media.AudioDeviceAttributes;
75 import android.media.AudioDeviceInfo;
76 import android.media.AudioDeviceVolumeManager;
77 import android.media.AudioManager;
78 import android.media.AudioProfile;
79 import android.media.VolumeInfo;
80 import android.media.session.MediaController;
81 import android.media.session.MediaSessionManager;
82 import android.media.tv.TvInputManager;
83 import android.media.tv.TvInputManager.TvInputCallback;
84 import android.net.Uri;
85 import android.os.Binder;
86 import android.os.Build;
87 import android.os.Handler;
88 import android.os.HandlerThread;
89 import android.os.IBinder;
90 import android.os.Looper;
91 import android.os.PowerManager;
92 import android.os.RemoteCallbackList;
93 import android.os.RemoteException;
94 import android.os.ResultReceiver;
95 import android.os.ShellCallback;
96 import android.os.SystemClock;
97 import android.os.SystemProperties;
98 import android.os.UserHandle;
99 import android.provider.DeviceConfig;
100 import android.provider.Settings.Global;
101 import android.stats.hdmi.HdmiStatsEnums;
102 import android.sysprop.HdmiProperties;
103 import android.text.TextUtils;
104 import android.util.ArrayMap;
105 import android.util.Slog;
106 import android.util.SparseArray;
107 import android.view.Display;
108 import android.view.KeyEvent;
109 
110 import com.android.internal.annotations.GuardedBy;
111 import com.android.internal.annotations.VisibleForTesting;
112 import com.android.internal.util.DumpUtils;
113 import com.android.internal.util.IndentingPrintWriter;
114 import com.android.server.SystemService;
115 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
116 import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
117 import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
118 import com.android.server.hdmi.HdmiCecLocalDevice.PendingActionClearedCallback;
119 import com.android.server.hdmi.HdmiCecLocalDevice.StandbyCompletedCallback;
120 
121 import libcore.util.EmptyArray;
122 
123 import java.io.FileDescriptor;
124 import java.io.PrintWriter;
125 import java.lang.annotation.Retention;
126 import java.lang.annotation.RetentionPolicy;
127 import java.util.ArrayList;
128 import java.util.Collection;
129 import java.util.Collections;
130 import java.util.HashMap;
131 import java.util.HashSet;
132 import java.util.List;
133 import java.util.Locale;
134 import java.util.Map;
135 import java.util.Objects;
136 import java.util.Set;
137 import java.util.concurrent.Executor;
138 import java.util.stream.Collectors;
139 
140 /**
141  * Provides a service for sending and processing HDMI control messages,
142  * HDMI-CEC and MHL control command, and providing the information on both standard.
143  *
144  * Additionally takes care of establishing and managing an eARC connection.
145  */
146 public class HdmiControlService extends SystemService {
147     private static final String TAG = "HdmiControlService";
148     private static final Locale HONG_KONG = new Locale("zh", "HK");
149     private static final Locale MACAU = new Locale("zh", "MO");
150     private static final String TAIWAN_HantLanguageTag = "zh-Hant-TW";
151     private static final String HONG_KONG_HantLanguageTag = "zh-Hant-HK";
152     private static final String HONG_KONG_YUE_HantLanguageTag = "yue-Hant-HK";
153     private static final String MACAU_HantLanguageTag = "zh-Hant-MO";
154 
155     private static final Map<String, String> sTerminologyToBibliographicMap =
156             createsTerminologyToBibliographicMap();
157 
createsTerminologyToBibliographicMap()158     private static Map<String, String> createsTerminologyToBibliographicMap() {
159         Map<String, String> temp = new HashMap<>();
160         // NOTE: (TERMINOLOGY_CODE, BIBLIOGRAPHIC_CODE)
161         temp.put("sqi", "alb"); // Albanian
162         temp.put("hye", "arm"); // Armenian
163         temp.put("eus", "baq"); // Basque
164         temp.put("mya", "bur"); // Burmese
165         temp.put("ces", "cze"); // Czech
166         temp.put("nld", "dut"); // Dutch
167         temp.put("kat", "geo"); // Georgian
168         temp.put("deu", "ger"); // German
169         temp.put("ell", "gre"); // Greek
170         temp.put("fra", "fre"); // French
171         temp.put("isl", "ice"); // Icelandic
172         temp.put("mkd", "mac"); // Macedonian
173         temp.put("mri", "mao"); // Maori
174         temp.put("msa", "may"); // Malay
175         temp.put("fas", "per"); // Persian
176         temp.put("ron", "rum"); // Romanian
177         temp.put("slk", "slo"); // Slovak
178         temp.put("bod", "tib"); // Tibetan
179         temp.put("cym", "wel"); // Welsh
180         return Collections.unmodifiableMap(temp);
181     }
182 
localeToMenuLanguage(Locale locale)183     @VisibleForTesting static String localeToMenuLanguage(Locale locale) {
184         if (locale.equals(Locale.TAIWAN) || locale.equals(HONG_KONG) || locale.equals(MACAU) ||
185                 locale.toLanguageTag().equals(TAIWAN_HantLanguageTag) ||
186                 locale.toLanguageTag().equals(HONG_KONG_HantLanguageTag) ||
187                 locale.toLanguageTag().equals(HONG_KONG_YUE_HantLanguageTag) ||
188                 locale.toLanguageTag().equals(MACAU_HantLanguageTag)) {
189             // Android always returns "zho" for all Chinese variants.
190             // Use "bibliographic" code defined in CEC639-2 for traditional
191             // Chinese used in Taiwan/Hong Kong/Macau.
192             return "chi";
193         } else {
194             String language = locale.getISO3Language();
195 
196             // locale.getISO3Language() returns terminology code and need to
197             // send it as bibliographic code instead since the Bibliographic
198             // codes of ISO/FDIS 639-2 shall be used.
199             // NOTE: Chinese also has terminology/bibliographic code "zho" and "chi"
200             // But, as it depends on the locale, is not handled here.
201             if (sTerminologyToBibliographicMap.containsKey(language)) {
202                 language = sTerminologyToBibliographicMap.get(language);
203             }
204 
205             return language;
206         }
207     }
208 
209     static final String PERMISSION = "android.permission.HDMI_CEC";
210 
211     // The reason code to initiate initializeCec() and initializeEarc().
212     static final int INITIATED_BY_ENABLE_CEC = 0;
213     static final int INITIATED_BY_BOOT_UP = 1;
214     static final int INITIATED_BY_SCREEN_ON = 2;
215     static final int INITIATED_BY_WAKE_UP_MESSAGE = 3;
216     static final int INITIATED_BY_HOTPLUG = 4;
217     static final int INITIATED_BY_SOUNDBAR_MODE = 5;
218     static final int INITIATED_BY_ENABLE_EARC = 6;
219 
220     // The reason code representing the intent action that drives the standby
221     // procedure. The procedure starts either by Intent.ACTION_SCREEN_OFF or
222     // Intent.ACTION_SHUTDOWN.
223     static final int STANDBY_SCREEN_OFF = 0;
224     static final int STANDBY_SHUTDOWN = 1;
225 
226     private HdmiCecNetwork mHdmiCecNetwork;
227 
228     static final int WAKE_UP_SCREEN_ON = 0;
229     static final int WAKE_UP_BOOT_UP = 1;
230 
231     // The reason code for starting the wake-up procedure. This procedure starts either by
232     // Intent.ACTION_SCREEN_ON or after boot-up.
233     @IntDef({
234             WAKE_UP_SCREEN_ON,
235             WAKE_UP_BOOT_UP
236     })
237     @Retention(RetentionPolicy.SOURCE)
238     public @interface WakeReason {
239     }
240 
241     // Timeout in millisecond for device clean up (5s).
242     // Normal actions timeout is 2s but some of them would have several sequences of timeout.
243     static final int DEVICE_CLEANUP_TIMEOUT = 5000;
244 
245     @VisibleForTesting
246     static final AudioDeviceAttributes AUDIO_OUTPUT_DEVICE_HDMI = new AudioDeviceAttributes(
247             AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_HDMI, "");
248     @VisibleForTesting
249     static final AudioDeviceAttributes AUDIO_OUTPUT_DEVICE_HDMI_ARC =
250             new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
251                     AudioDeviceInfo.TYPE_HDMI_ARC, "");
252     static final AudioDeviceAttributes AUDIO_OUTPUT_DEVICE_HDMI_EARC =
253             new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
254                     AudioDeviceInfo.TYPE_HDMI_EARC, "");
255     static final AudioDeviceAttributes AUDIO_OUTPUT_DEVICE_LINE_DIGITAL =
256             new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
257             AudioDeviceInfo.TYPE_LINE_DIGITAL, "");
258 
259     // Audio output devices used for absolute volume behavior
260     private static final List<AudioDeviceAttributes> AVB_AUDIO_OUTPUT_DEVICES =
261             List.of(AUDIO_OUTPUT_DEVICE_HDMI,
262                     AUDIO_OUTPUT_DEVICE_HDMI_ARC,
263                     AUDIO_OUTPUT_DEVICE_HDMI_EARC,
264                     AUDIO_OUTPUT_DEVICE_LINE_DIGITAL);
265 
266     // Audio output devices used for absolute volume behavior on TV panels
267     private static final List<AudioDeviceAttributes> TV_AVB_AUDIO_OUTPUT_DEVICES =
268             List.of(AUDIO_OUTPUT_DEVICE_HDMI_ARC,
269                     AUDIO_OUTPUT_DEVICE_HDMI_EARC,
270                     AUDIO_OUTPUT_DEVICE_LINE_DIGITAL);
271 
272     // Audio output devices used for absolute volume behavior on Playback devices
273     private static final List<AudioDeviceAttributes> PLAYBACK_AVB_AUDIO_OUTPUT_DEVICES =
274             List.of(AUDIO_OUTPUT_DEVICE_HDMI);
275 
276     private static final List<Integer> ABSOLUTE_VOLUME_BEHAVIORS =
277             List.of(AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
278                     AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
279 
280     private static final List<Integer> FULL_AND_ABSOLUTE_VOLUME_BEHAVIORS =
281             List.of(AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL,
282                     AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
283                     AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
284 
285     // AudioAttributes for STREAM_MUSIC
286     @VisibleForTesting
287     static final AudioAttributes STREAM_MUSIC_ATTRIBUTES =
288             new AudioAttributes.Builder().setLegacyStreamType(AudioManager.STREAM_MUSIC).build();
289 
290     private final Executor mServiceThreadExecutor = new Executor() {
291         @Override
292         public void execute(Runnable r) {
293             runOnServiceThread(r);
294         }
295     };
296 
getServiceThreadExecutor()297     Executor getServiceThreadExecutor() {
298         return mServiceThreadExecutor;
299     }
300 
301     // Logical address of the active source.
302     @GuardedBy("mLock")
303     protected final ActiveSource mActiveSource = new ActiveSource();
304 
305     // Whether System Audio Mode is activated or not.
306     @GuardedBy("mLock")
307     private boolean mSystemAudioActivated = false;
308 
309     // Whether HDMI CEC volume control is enabled or not.
310     @GuardedBy("mLock")
311     @HdmiControlManager.VolumeControl
312     private int mHdmiCecVolumeControl;
313 
314     // Caches the volume behaviors of all audio output devices in AVB_AUDIO_OUTPUT_DEVICES.
315     @GuardedBy("mLock")
316     private Map<AudioDeviceAttributes, Integer> mAudioDeviceVolumeBehaviors = new HashMap<>();
317 
318     // Maximum volume of AudioManager.STREAM_MUSIC. Set upon gaining access to system services.
319     private int mStreamMusicMaxVolume;
320 
321     // Make sure HdmiCecConfig is instantiated and the XMLs are read.
322     private HdmiCecConfig mHdmiCecConfig;
323 
324     // Timeout value for start ARC action after an established eARC connection was terminated,
325     // e.g. because eARC was disabled in Settings.
326     private static final int EARC_TRIGGER_START_ARC_ACTION_DELAY = 500;
327 
328     /**
329      * Interface to report send result.
330      */
331     interface SendMessageCallback {
332         /**
333          * Called when {@link HdmiControlService#sendCecCommand} is completed.
334          *
335          * @param error result of send request.
336          * <ul>
337          * <li>{@link SendMessageResult#SUCCESS}
338          * <li>{@link SendMessageResult#NACK}
339          * <li>{@link SendMessageResult#BUSY}
340          * <li>{@link SendMessageResult#FAIL}
341          * </ul>
342          */
onSendCompleted(int error)343         void onSendCompleted(int error);
344     }
345 
346     /**
347      * Interface to get a list of available logical devices.
348      */
349     interface DevicePollingCallback {
350         /**
351          * Called when device polling is finished.
352          *
353          * @param ackedAddress a list of logical addresses of available devices
354          */
onPollingFinished(List<Integer> ackedAddress)355         void onPollingFinished(List<Integer> ackedAddress);
356     }
357 
358     private class HdmiControlBroadcastReceiver extends BroadcastReceiver {
359         @ServiceThreadOnly
360         @Override
onReceive(Context context, Intent intent)361         public void onReceive(Context context, Intent intent) {
362             assertRunOnServiceThread();
363             boolean isReboot = SystemProperties.get(SHUTDOWN_ACTION_PROPERTY).contains("1");
364             switch (intent.getAction()) {
365                 case Intent.ACTION_SCREEN_OFF:
366                     if (isPowerOnOrTransient() && !isReboot) {
367                         onStandby(STANDBY_SCREEN_OFF);
368                     }
369                     break;
370                 case Intent.ACTION_SCREEN_ON:
371                     if (isPowerStandbyOrTransient()) {
372                         onWakeUp(WAKE_UP_SCREEN_ON);
373                     }
374                     break;
375                 case Intent.ACTION_CONFIGURATION_CHANGED:
376                     String language = HdmiControlService.localeToMenuLanguage(Locale.getDefault());
377                     if (!mMenuLanguage.equals(language)) {
378                         onLanguageChanged(language);
379                     }
380                     break;
381                 case Intent.ACTION_SHUTDOWN:
382                     if (isPowerOnOrTransient() && !isReboot) {
383                         onStandby(STANDBY_SHUTDOWN);
384                     }
385                     break;
386             }
387         }
388 
389     }
390 
391     // A thread to handle synchronous IO of CEC and MHL control service.
392     // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
393     // and sparse call it shares a thread to handle IO operations.
394     private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
395 
396     // Used to synchronize the access to the service.
397     private final Object mLock = new Object();
398 
399     // Type of CEC logical devices hosted in the system. Stored in the unmodifiable list.
400     private final List<Integer> mCecLocalDevices;
401 
402     // List of records for HDMI control status change listener for death monitoring.
403     @GuardedBy("mLock")
404     private final ArrayList<HdmiControlStatusChangeListenerRecord>
405             mHdmiControlStatusChangeListenerRecords = new ArrayList<>();
406 
407     // List of records for HDMI control volume control status change listener for death monitoring.
408     @GuardedBy("mLock")
409     private final RemoteCallbackList<IHdmiCecVolumeControlFeatureListener>
410             mHdmiCecVolumeControlFeatureListenerRecords = new RemoteCallbackList<>();
411 
412     // List of records for hotplug event listener to handle the the caller killed in action.
413     @GuardedBy("mLock")
414     private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
415             new ArrayList<>();
416 
417     // List of records for device event listener to handle the caller killed in action.
418     @GuardedBy("mLock")
419     private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
420             new ArrayList<>();
421 
422     // List of records for vendor command listener to handle the caller killed in action.
423     @GuardedBy("mLock")
424     private final ArrayList<VendorCommandListenerRecord> mVendorCommandListenerRecords =
425             new ArrayList<>();
426 
427     // List of records for CEC setting change listener to handle the caller killed in action.
428     @GuardedBy("mLock")
429     private final ArrayMap<String, RemoteCallbackList<IHdmiCecSettingChangeListener>>
430             mHdmiCecSettingChangeListenerRecords = new ArrayMap<>();
431 
432     @GuardedBy("mLock")
433     private InputChangeListenerRecord mInputChangeListenerRecord;
434 
435     @GuardedBy("mLock")
436     private HdmiRecordListenerRecord mRecordListenerRecord;
437 
438     // Set to true while HDMI control is enabled. If set to false, HDMI-CEC/MHL protocol
439     // handling will be disabled and no request will be handled.
440     @GuardedBy("mLock")
441     @HdmiControlManager.HdmiCecControl
442     private int mHdmiControlEnabled;
443 
444     // Set to true while the eARC feature is supported by the hardware on at least one port
445     // and the eARC HAL is present.
446     @GuardedBy("mLock")
447     @VisibleForTesting
448     private boolean mEarcSupported;
449 
450     // Set to true while the eARC feature is enabled.
451     @GuardedBy("mLock")
452     private boolean mEarcEnabled;
453 
454     private int mEarcPortId = -1;
455 
456     // Set to true while the service is in normal mode. While set to false, no input change is
457     // allowed. Used for situations where input change can confuse users such as channel auto-scan,
458     // system upgrade, etc., a.k.a. "prohibit mode".
459     @GuardedBy("mLock")
460     private boolean mProhibitMode;
461 
462     // List of records for system audio mode change to handle the the caller killed in action.
463     private final ArrayList<SystemAudioModeChangeListenerRecord>
464             mSystemAudioModeChangeListenerRecords = new ArrayList<>();
465 
466     // Handler used to run a task in service thread.
467     private final Handler mHandler = new Handler();
468 
469     private final SettingsObserver mSettingsObserver;
470 
471     private final HdmiControlBroadcastReceiver
472             mHdmiControlBroadcastReceiver = new HdmiControlBroadcastReceiver();
473 
474     @Nullable
475     // Save callback when the device is still under logcial address allocation
476     // Invoke once new local device is ready.
477     private IHdmiControlCallback mDisplayStatusCallback = null;
478 
479     @Nullable
480     // Save callback when the device is still under logcial address allocation
481     // Invoke once new local device is ready.
482     private IHdmiControlCallback mOtpCallbackPendingAddressAllocation = null;
483 
484     @Nullable
485     private HdmiCecController mCecController;
486 
487     @VisibleForTesting
488     protected HdmiCecPowerStatusController mPowerStatusController;
489 
490     @Nullable
491     private HdmiEarcController mEarcController;
492 
493     @Nullable
494     private HdmiEarcLocalDevice mEarcLocalDevice;
495 
496     @ServiceThreadOnly
497     private String mMenuLanguage = localeToMenuLanguage(Locale.getDefault());
498 
499     @ServiceThreadOnly
500     private boolean mStandbyMessageReceived = false;
501 
502     @ServiceThreadOnly
503     private boolean mWakeUpMessageReceived = false;
504 
505     @ServiceThreadOnly
506     private boolean mSoundbarModeFeatureFlagEnabled = false;
507 
508     @ServiceThreadOnly
509     private boolean mEarcTxFeatureFlagEnabled = false;
510 
511     @ServiceThreadOnly
512     private boolean mNumericSoundbarVolumeUiOnTvFeatureFlagEnabled = false;
513 
514     @ServiceThreadOnly
515     private boolean mTransitionFromArcToEarcTxEnabled = false;
516 
517     @ServiceThreadOnly
518     private int mActivePortId = Constants.INVALID_PORT_ID;
519 
520     // Set to true while the input change by MHL is allowed.
521     @GuardedBy("mLock")
522     private boolean mMhlInputChangeEnabled;
523 
524     // List of records for MHL Vendor command listener to handle the caller killed in action.
525     @GuardedBy("mLock")
526     private final ArrayList<HdmiMhlVendorCommandListenerRecord>
527             mMhlVendorCommandListenerRecords = new ArrayList<>();
528 
529     @GuardedBy("mLock")
530     private List<HdmiDeviceInfo> mMhlDevices;
531 
532     @Nullable
533     private HdmiMhlControllerStub mMhlController;
534 
535     @Nullable
536     private TvInputManager mTvInputManager;
537 
538     @Nullable
539     private DeviceConfigWrapper mDeviceConfig;
540 
541     @Nullable
542     private WakeLockWrapper mWakeLock;
543 
544     @Nullable
545     private PowerManagerWrapper mPowerManager;
546 
547     @Nullable
548     private PowerManagerInternalWrapper mPowerManagerInternal;
549 
550     @Nullable
551     private AudioManagerWrapper mAudioManager;
552 
553     @Nullable
554     private AudioDeviceVolumeManagerWrapper mAudioDeviceVolumeManager;
555 
556     @Nullable
557     private Looper mIoLooper;
558 
559     @Nullable
560     private DisplayManager mDisplayManager;
561 
562     @HdmiControlManager.HdmiCecVersion
563     private int mCecVersion;
564 
565     // Last input port before switching to the MHL port. Should switch back to this port
566     // when the mobile device sends the request one touch play with off.
567     // Gets invalidated if we go to other port/input.
568     @ServiceThreadOnly
569     private int mLastInputMhl = Constants.INVALID_PORT_ID;
570 
571     // Set to true if the logical address allocation is completed.
572     private boolean mAddressAllocated = false;
573 
574     // Whether a CEC-enabled sink is connected to the playback device
575     private boolean mIsCecAvailable = false;
576 
577     // Object that handles logging statsd atoms.
578     // Use getAtomWriter() instead of accessing directly, to allow dependency injection for testing.
579     private HdmiCecAtomWriter mAtomWriter = new HdmiCecAtomWriter();
580 
581     private CecMessageBuffer mCecMessageBuffer;
582 
583     private final SelectRequestBuffer mSelectRequestBuffer = new SelectRequestBuffer();
584 
585     /**
586      * Constructor for testing.
587      *
588      * Takes fakes for AudioManager and AudioDeviceVolumeManager.
589      *
590      * This is especially important for AudioDeviceVolumeManager because a normally instantiated
591      * AudioDeviceVolumeManager can access the "real" AudioService on the DUT.
592      */
HdmiControlService(Context context, List<Integer> deviceTypes, AudioManagerWrapper audioManager, AudioDeviceVolumeManagerWrapper audioDeviceVolumeManager)593     @VisibleForTesting HdmiControlService(Context context, List<Integer> deviceTypes,
594             AudioManagerWrapper audioManager,
595             AudioDeviceVolumeManagerWrapper audioDeviceVolumeManager) {
596         super(context);
597         mCecLocalDevices = deviceTypes;
598         mSettingsObserver = new SettingsObserver(mHandler);
599         mHdmiCecConfig = new HdmiCecConfig(context);
600         mDeviceConfig = new DeviceConfigWrapper();
601         mAudioManager = audioManager;
602         mAudioDeviceVolumeManager = audioDeviceVolumeManager;
603     }
604 
HdmiControlService(Context context)605     public HdmiControlService(Context context) {
606         super(context);
607         mCecLocalDevices = readDeviceTypes();
608         mSettingsObserver = new SettingsObserver(mHandler);
609         mHdmiCecConfig = new HdmiCecConfig(context);
610         mDeviceConfig = new DeviceConfigWrapper();
611     }
612 
613     @VisibleForTesting
getCecDeviceTypes()614     protected List<HdmiProperties.cec_device_types_values> getCecDeviceTypes() {
615         return HdmiProperties.cec_device_types();
616     }
617 
618     @VisibleForTesting
getDeviceTypes()619     protected List<Integer> getDeviceTypes() {
620         return HdmiProperties.device_type();
621     }
622 
623     /**
624      * Extracts a list of integer device types from the sysprop ro.hdmi.cec_device_types.
625      * If ro.hdmi.cec_device_types is not set, reads from ro.hdmi.device.type instead.
626      * @return the list of integer device types
627      */
628     @VisibleForTesting
readDeviceTypes()629     protected List<Integer> readDeviceTypes() {
630         List<HdmiProperties.cec_device_types_values> cecDeviceTypes = getCecDeviceTypes();
631         if (!cecDeviceTypes.isEmpty()) {
632             if (cecDeviceTypes.contains(null)) {
633                 Slog.w(TAG, "Error parsing ro.hdmi.cec_device_types: " + SystemProperties.get(
634                         "ro.hdmi.cec_device_types"));
635             }
636             return cecDeviceTypes.stream()
637                     .map(HdmiControlService::enumToIntDeviceType)
638                     .filter(Objects::nonNull)
639                     .collect(Collectors.toList());
640         } else {
641             // If ro.hdmi.cec_device_types isn't set, fall back to reading ro.hdmi.device_type
642             List<Integer> deviceTypes = getDeviceTypes();
643             if (deviceTypes.contains(null)) {
644                 Slog.w(TAG, "Error parsing ro.hdmi.device_type: " + SystemProperties.get(
645                         "ro.hdmi.device_type"));
646             }
647             return deviceTypes.stream()
648                     .filter(Objects::nonNull)
649                     .collect(Collectors.toList());
650         }
651     }
652 
653     /**
654      * Converts an enum representing a value in ro.hdmi.cec_device_types to an integer device type.
655      * Returns null if the input is null or an unrecognized device type.
656      */
657     @Nullable
enumToIntDeviceType( @ullable HdmiProperties.cec_device_types_values cecDeviceType)658     private static Integer enumToIntDeviceType(
659             @Nullable HdmiProperties.cec_device_types_values cecDeviceType) {
660         if (cecDeviceType == null) {
661             return null;
662         }
663         switch (cecDeviceType) {
664             case TV:
665                 return HdmiDeviceInfo.DEVICE_TV;
666             case RECORDING_DEVICE:
667                 return HdmiDeviceInfo.DEVICE_RECORDER;
668             case RESERVED:
669                 return HdmiDeviceInfo.DEVICE_RESERVED;
670             case TUNER:
671                 return HdmiDeviceInfo.DEVICE_TUNER;
672             case PLAYBACK_DEVICE:
673                 return HdmiDeviceInfo.DEVICE_PLAYBACK;
674             case AUDIO_SYSTEM:
675                 return HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
676             case PURE_CEC_SWITCH:
677                 return HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH;
678             case VIDEO_PROCESSOR:
679                 return HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR;
680             default:
681                 Slog.w(TAG, "Unrecognized device type in ro.hdmi.cec_device_types: "
682                         + cecDeviceType.getPropValue());
683                 return null;
684         }
685     }
686 
getIntList(String string)687     protected static List<Integer> getIntList(String string) {
688         ArrayList<Integer> list = new ArrayList<>();
689         TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
690         splitter.setString(string);
691         for (String item : splitter) {
692             try {
693                 list.add(Integer.parseInt(item));
694             } catch (NumberFormatException e) {
695                 Slog.w(TAG, "Can't parseInt: " + item);
696             }
697         }
698         return Collections.unmodifiableList(list);
699     }
700 
701     @Override
onStart()702     public void onStart() {
703         initService();
704         publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
705 
706         if (mCecController != null) {
707             // Register broadcast receiver for power state change.
708             IntentFilter filter = new IntentFilter();
709             filter.addAction(Intent.ACTION_SCREEN_OFF);
710             filter.addAction(Intent.ACTION_SCREEN_ON);
711             filter.addAction(Intent.ACTION_SHUTDOWN);
712             filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
713             getContext().registerReceiver(mHdmiControlBroadcastReceiver, filter);
714 
715             // Register ContentObserver to monitor the settings change.
716             registerContentObserver();
717         }
718         if (mMhlController != null) {
719             mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, ENABLED);
720         }
721     }
722 
723     @VisibleForTesting
initService()724     void initService() {
725         if (mIoLooper == null) {
726             mIoThread.start();
727             mIoLooper = mIoThread.getLooper();
728         }
729 
730         if (mPowerStatusController == null) {
731             mPowerStatusController = new HdmiCecPowerStatusController(this);
732         }
733         mPowerStatusController.setPowerStatus(getInitialPowerStatus());
734         setProhibitMode(false);
735         if (isTvDevice() && getWasCecDisabledOnStandbyByLowEnergyMode()) {
736             Slog.w(TAG, "Re-enable CEC on boot-up since it was disabled due to low energy "
737                     + " mode.");
738             mHdmiCecConfig.setIntValue(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
739                     HDMI_CEC_CONTROL_ENABLED);
740             setWasCecDisabledOnStandbyByLowEnergyMode(false);
741         }
742         mHdmiControlEnabled = mHdmiCecConfig.getIntValue(
743                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED);
744 
745         mSoundbarModeFeatureFlagEnabled = mDeviceConfig.getBoolean(
746                 Constants.DEVICE_CONFIG_FEATURE_FLAG_SOUNDBAR_MODE, true);
747         mEarcTxFeatureFlagEnabled = mDeviceConfig.getBoolean(
748                 Constants.DEVICE_CONFIG_FEATURE_FLAG_ENABLE_EARC_TX, true);
749         mTransitionFromArcToEarcTxEnabled = mDeviceConfig.getBoolean(
750                 Constants.DEVICE_CONFIG_FEATURE_FLAG_TRANSITION_ARC_TO_EARC_TX, true);
751         mNumericSoundbarVolumeUiOnTvFeatureFlagEnabled = mDeviceConfig.getBoolean(
752                 Constants.DEVICE_CONFIG_FEATURE_FLAG_TV_NUMERIC_SOUNDBAR_VOLUME_UI, true);
753 
754         synchronized (mLock) {
755             mEarcEnabled = (mHdmiCecConfig.getIntValue(
756                     HdmiControlManager.SETTING_NAME_EARC_ENABLED) == EARC_FEATURE_ENABLED);
757             if (isTvDevice()) {
758                 mEarcEnabled &= mEarcTxFeatureFlagEnabled;
759             }
760         }
761         setHdmiCecVolumeControlEnabledInternal(getHdmiCecConfig().getIntValue(
762                 HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE));
763         mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true);
764 
765         if (mCecMessageBuffer == null) {
766             mCecMessageBuffer = new CecMessageBuffer(this);
767         }
768         if (mCecController == null) {
769             mCecController = HdmiCecController.create(this, getAtomWriter());
770         }
771         if (mCecController == null) {
772             Slog.i(TAG, "Device does not support HDMI-CEC.");
773             return;
774         }
775         if (mMhlController == null) {
776             mMhlController = HdmiMhlControllerStub.create(this);
777         }
778         if (!mMhlController.isReady()) {
779             Slog.i(TAG, "Device does not support MHL-control.");
780         }
781         if (mEarcController == null) {
782             mEarcController = HdmiEarcController.create(this);
783         }
784         if (mEarcController == null) {
785             Slog.i(TAG, "Device does not support eARC.");
786         }
787         mHdmiCecNetwork = new HdmiCecNetwork(this, mCecController, mMhlController);
788         if (isCecControlEnabled()) {
789             initializeCec(INITIATED_BY_BOOT_UP);
790         } else {
791             mCecController.enableCec(false);
792         }
793 
794         synchronized (mLock) {
795             mMhlDevices = Collections.emptyList();
796         }
797 
798         mHdmiCecNetwork.initPortInfo();
799         List<HdmiPortInfo> ports = getPortInfo();
800         synchronized (mLock) {
801             mEarcSupported = false;
802             for (HdmiPortInfo port : ports) {
803                 boolean earcSupportedOnPort = port.isEarcSupported();
804                 if (earcSupportedOnPort && mEarcSupported) {
805                     // This means that more than 1 port supports eARC.
806                     // The HDMI specification only allows 1 active eARC connection.
807                     // Android does not support devices with multiple eARC-enabled ports.
808                     // Consider eARC not supported in this case.
809                     Slog.e(TAG, "HDMI eARC supported on more than 1 port.");
810                     mEarcSupported = false;
811                     mEarcPortId = -1;
812                     break;
813                 } else if (earcSupportedOnPort) {
814                     mEarcPortId = port.getId();
815                     mEarcSupported = earcSupportedOnPort;
816                 }
817             }
818             mEarcSupported &= (mEarcController != null);
819         }
820         if (isEarcSupported()) {
821             if (isEarcEnabled()) {
822                 initializeEarc(INITIATED_BY_BOOT_UP);
823             } else {
824                 setEarcEnabledInHal(false, false);
825             }
826         }
827 
828         mHdmiCecConfig.registerChangeListener(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
829                 new HdmiCecConfig.SettingChangeListener() {
830                     @Override
831                     public void onChange(String setting) {
832                         @HdmiControlManager.HdmiCecControl int enabled = mHdmiCecConfig.getIntValue(
833                                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED);
834                         setCecEnabled(enabled);
835                     }
836                 }, mServiceThreadExecutor);
837         mHdmiCecConfig.registerChangeListener(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
838                 new HdmiCecConfig.SettingChangeListener() {
839                     @Override
840                     public void onChange(String setting) {
841                         initializeCec(INITIATED_BY_ENABLE_CEC);
842                     }
843                 }, mServiceThreadExecutor);
844         mHdmiCecConfig.registerChangeListener(HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
845                 new HdmiCecConfig.SettingChangeListener() {
846                     @Override
847                     public void onChange(String setting) {
848                         boolean enabled = mHdmiCecConfig.getIntValue(
849                                 HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL)
850                                     == HdmiControlManager.ROUTING_CONTROL_ENABLED;
851                         if (isAudioSystemDevice()) {
852                             if (audioSystem() == null) {
853                                 Slog.w(TAG, "Switch device has not registered yet."
854                                         + " Can't turn routing on.");
855                             } else {
856                                 audioSystem().setRoutingControlFeatureEnabled(enabled);
857                             }
858                         }
859                     }
860                 }, mServiceThreadExecutor);
861         mHdmiCecConfig.registerChangeListener(
862                 HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
863                 new HdmiCecConfig.SettingChangeListener() {
864                     @Override
865                     public void onChange(String setting) {
866                         boolean enabled = mHdmiCecConfig.getIntValue(
867                                 HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL)
868                                     == HdmiControlManager.SYSTEM_AUDIO_CONTROL_ENABLED;
869                         if (isTvDeviceEnabled()) {
870                             tv().setSystemAudioControlFeatureEnabled(enabled);
871                         }
872                         if (isAudioSystemDevice()) {
873                             if (audioSystem() == null) {
874                                 Slog.e(TAG, "Audio System device has not registered yet."
875                                         + " Can't turn system audio mode on.");
876                             } else {
877                                 audioSystem().onSystemAudioControlFeatureSupportChanged(enabled);
878                             }
879                         }
880                     }
881                 }, mServiceThreadExecutor);
882         mHdmiCecConfig.registerChangeListener(
883                 HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
884                 new HdmiCecConfig.SettingChangeListener() {
885                     @Override
886                     public void onChange(String setting) {
887                         setHdmiCecVolumeControlEnabledInternal(getHdmiCecConfig().getIntValue(
888                                 HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE));
889                     }
890                 }, mServiceThreadExecutor);
891         mHdmiCecConfig.registerChangeListener(
892                 HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
893                 new HdmiCecConfig.SettingChangeListener() {
894                     @Override
895                     public void onChange(String setting) {
896                         if (isTvDeviceEnabled()) {
897                             mCecController.enableWakeupByOtp(tv().getAutoWakeup());
898                         }
899                     }
900                 }, mServiceThreadExecutor);
901         mHdmiCecConfig.registerChangeListener(HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
902                 new HdmiCecConfig.SettingChangeListener() {
903                     @Override
904                     public void onChange(String setting) {
905                         reportFeatures(true);
906                     }
907                 },
908                 mServiceThreadExecutor);
909         mHdmiCecConfig.registerChangeListener(
910                 HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
911                 new HdmiCecConfig.SettingChangeListener() {
912                     @Override
913                     public void onChange(String setting) {
914                         reportFeatures(false);
915                     }
916                 },
917                 mServiceThreadExecutor);
918         mHdmiCecConfig.registerChangeListener(
919                 HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
920                 new HdmiCecConfig.SettingChangeListener() {
921                     @Override
922                     public void onChange(String setting) {
923                         reportFeatures(false);
924                     }
925                 },
926                 mServiceThreadExecutor);
927         mHdmiCecConfig.registerChangeListener(
928                 HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
929                 new HdmiCecConfig.SettingChangeListener() {
930                     @Override
931                     public void onChange(String setting) {
932                         reportFeatures(false);
933                     }
934                 },
935                 mServiceThreadExecutor);
936         mHdmiCecConfig.registerChangeListener(
937                 HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
938                 new HdmiCecConfig.SettingChangeListener() {
939                     @Override
940                     public void onChange(String setting) {
941                         reportFeatures(false);
942                     }
943                 },
944                 mServiceThreadExecutor);
945         mHdmiCecConfig.registerChangeListener(
946                 HdmiControlManager
947                         .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU,
948                 new HdmiCecConfig.SettingChangeListener() {
949                     @Override
950                     public void onChange(String setting) {
951                         reportFeatures(false);
952                     }
953                 },
954                 mServiceThreadExecutor);
955 
956         if (isTvDevice()) {
957             mDeviceConfig.addOnPropertiesChangedListener(getContext().getMainExecutor(),
958                     new DeviceConfig.OnPropertiesChangedListener() {
959                         @Override
960                         public void onPropertiesChanged(DeviceConfig.Properties properties) {
961                             mEarcTxFeatureFlagEnabled = properties.getBoolean(
962                                     Constants.DEVICE_CONFIG_FEATURE_FLAG_ENABLE_EARC_TX,
963                                     true);
964                             boolean earcEnabledSetting = mHdmiCecConfig.getIntValue(
965                                     HdmiControlManager.SETTING_NAME_EARC_ENABLED)
966                                     == EARC_FEATURE_ENABLED;
967                             setEarcEnabled(earcEnabledSetting && mEarcTxFeatureFlagEnabled
968                                     ? EARC_FEATURE_ENABLED : EARC_FEATURE_DISABLED);
969                         }
970                     });
971         }
972 
973         mHdmiCecConfig.registerChangeListener(HdmiControlManager.SETTING_NAME_EARC_ENABLED,
974                 new HdmiCecConfig.SettingChangeListener() {
975                     @Override
976                     public void onChange(String setting) {
977                         if (isTvDevice()) {
978                             boolean earcEnabledSetting = mHdmiCecConfig.getIntValue(
979                                     HdmiControlManager.SETTING_NAME_EARC_ENABLED)
980                                     == EARC_FEATURE_ENABLED;
981                             setEarcEnabled(earcEnabledSetting && mEarcTxFeatureFlagEnabled
982                                     ? EARC_FEATURE_ENABLED : EARC_FEATURE_DISABLED);
983                         } else {
984                             setEarcEnabled(mHdmiCecConfig.getIntValue(
985                                     HdmiControlManager.SETTING_NAME_EARC_ENABLED));
986                         }
987                     }
988                 },
989                 mServiceThreadExecutor);
990 
991         mDeviceConfig.addOnPropertiesChangedListener(getContext().getMainExecutor(),
992                 new DeviceConfig.OnPropertiesChangedListener() {
993                     @Override
994                     public void onPropertiesChanged(DeviceConfig.Properties properties) {
995                         mSoundbarModeFeatureFlagEnabled = properties.getBoolean(
996                                 Constants.DEVICE_CONFIG_FEATURE_FLAG_SOUNDBAR_MODE,
997                                 true);
998                         boolean soundbarModeSetting = mHdmiCecConfig.getIntValue(
999                                 HdmiControlManager.CEC_SETTING_NAME_SOUNDBAR_MODE)
1000                                 == SOUNDBAR_MODE_ENABLED;
1001                         setSoundbarMode(soundbarModeSetting && mSoundbarModeFeatureFlagEnabled
1002                                 ? SOUNDBAR_MODE_ENABLED : SOUNDBAR_MODE_DISABLED);
1003                     }
1004                 });
1005         mHdmiCecConfig.registerChangeListener(HdmiControlManager.CEC_SETTING_NAME_SOUNDBAR_MODE,
1006                 new HdmiCecConfig.SettingChangeListener() {
1007                     @Override
1008                     public void onChange(String setting) {
1009                         boolean soundbarModeSetting = mHdmiCecConfig.getIntValue(
1010                                 HdmiControlManager.CEC_SETTING_NAME_SOUNDBAR_MODE)
1011                                 == SOUNDBAR_MODE_ENABLED;
1012                         setSoundbarMode(soundbarModeSetting && mSoundbarModeFeatureFlagEnabled
1013                                 ? SOUNDBAR_MODE_ENABLED : SOUNDBAR_MODE_DISABLED);
1014                     }
1015                 }, mServiceThreadExecutor);
1016 
1017         if (isPlaybackDevice()) {
1018             mHdmiCecConfig.registerChangeListener(
1019                     HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
1020                     new HdmiCecConfig.SettingChangeListener() {
1021                         @Override
1022                         public void onChange(String setting) {
1023                             boolean goToStandbyOnActiveSourceLost =
1024                                     mHdmiCecConfig.getStringValue(
1025                                             HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST)
1026                                             .equals(HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
1027                             writePowerStateChangeOnActiveSourceLostAtom(goToStandbyOnActiveSourceLost);
1028                         }
1029                     }, mServiceThreadExecutor);
1030         }
1031 
1032         mDeviceConfig.addOnPropertiesChangedListener(getContext().getMainExecutor(),
1033                 new DeviceConfig.OnPropertiesChangedListener() {
1034                     @Override
1035                     public void onPropertiesChanged(DeviceConfig.Properties properties) {
1036                         mTransitionFromArcToEarcTxEnabled = properties.getBoolean(
1037                                 Constants.DEVICE_CONFIG_FEATURE_FLAG_TRANSITION_ARC_TO_EARC_TX,
1038                                 true);
1039                     }
1040                 });
1041 
1042         mDeviceConfig.addOnPropertiesChangedListener(getContext().getMainExecutor(),
1043                 new DeviceConfig.OnPropertiesChangedListener() {
1044                     @Override
1045                     public void onPropertiesChanged(DeviceConfig.Properties properties) {
1046                         mNumericSoundbarVolumeUiOnTvFeatureFlagEnabled = properties.getBoolean(
1047                                 Constants.DEVICE_CONFIG_FEATURE_FLAG_TV_NUMERIC_SOUNDBAR_VOLUME_UI,
1048                                 true);
1049                         checkAndUpdateAbsoluteVolumeBehavior();
1050                     }
1051                 });
1052     }
1053     /** Returns true if the device screen is off */
isScreenOff()1054     boolean isScreenOff() {
1055         return mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_OFF;
1056     }
1057 
bootCompleted()1058     private void bootCompleted() {
1059         // on boot, if device is interactive, set HDMI CEC state as powered on as well
1060         if (mPowerManager.isInteractive() && isPowerStandbyOrTransient()) {
1061             mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
1062             // Start all actions that were queued because the device was in standby
1063             if (mAddressAllocated) {
1064                 for (HdmiCecLocalDevice localDevice : getAllCecLocalDevices()) {
1065                     localDevice.startQueuedActions();
1066                 }
1067             }
1068         }
1069     }
1070 
1071     /** Helper method for sending feature discovery command */
reportFeatures(boolean isTvDeviceSetting)1072     private void reportFeatures(boolean isTvDeviceSetting) {
1073         // <Report Features> should only be sent for HDMI 2.0
1074         if (getCecVersion() < HdmiControlManager.HDMI_CEC_VERSION_2_0) {
1075             return;
1076         }
1077         // check if tv device is enabled for tv device specific RC profile setting
1078         if (isTvDeviceSetting) {
1079             if (isTvDeviceEnabled()) {
1080                 tv().reportFeatures();
1081             }
1082         } else { // check for source device setting
1083             HdmiCecLocalDeviceSource source = isAudioSystemDevice() ? audioSystem() : playback();
1084             if (source != null) {
1085                 source.reportFeatures();
1086             }
1087         }
1088     }
1089 
1090     /**
1091      * Returns the initial power status used when the HdmiControlService starts.
1092      */
1093     @VisibleForTesting
getInitialPowerStatus()1094     int getInitialPowerStatus() {
1095         // The initial power status is POWER_STATUS_TRANSIENT_TO_STANDBY.
1096         // Once boot completes the service transitions to POWER_STATUS_ON if the device is
1097         // interactive.
1098         // Quiescent boot is a special boot mode, in which the screen stays off during boot
1099         // and the device goes to sleep after boot has finished.
1100         // We don't transition to POWER_STATUS_ON initially, as we might be booting in quiescent
1101         // mode, during which we don't want to appear powered on to avoid being made active source.
1102         return HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
1103     }
1104 
1105     @VisibleForTesting
setCecController(HdmiCecController cecController)1106     void setCecController(HdmiCecController cecController) {
1107         mCecController = cecController;
1108     }
1109 
1110     @VisibleForTesting
setEarcController(HdmiEarcController earcController)1111     void setEarcController(HdmiEarcController earcController) {
1112         mEarcController = earcController;
1113     }
1114 
1115     @VisibleForTesting
setHdmiCecNetwork(HdmiCecNetwork hdmiCecNetwork)1116     void setHdmiCecNetwork(HdmiCecNetwork hdmiCecNetwork) {
1117         mHdmiCecNetwork = hdmiCecNetwork;
1118     }
1119 
1120     @VisibleForTesting
setHdmiCecConfig(HdmiCecConfig hdmiCecConfig)1121     void setHdmiCecConfig(HdmiCecConfig hdmiCecConfig) {
1122         mHdmiCecConfig = hdmiCecConfig;
1123     }
1124 
getHdmiCecNetwork()1125     public HdmiCecNetwork getHdmiCecNetwork() {
1126         return mHdmiCecNetwork;
1127     }
1128 
1129     @VisibleForTesting
setHdmiMhlController(HdmiMhlControllerStub hdmiMhlController)1130     void setHdmiMhlController(HdmiMhlControllerStub hdmiMhlController) {
1131         mMhlController = hdmiMhlController;
1132     }
1133 
1134     @Override
onBootPhase(int phase)1135     public void onBootPhase(int phase) {
1136         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
1137             mDisplayManager = getContext().getSystemService(DisplayManager.class);
1138             mTvInputManager = (TvInputManager) getContext().getSystemService(
1139                     Context.TV_INPUT_SERVICE);
1140             mPowerManager = new PowerManagerWrapper(getContext());
1141             mPowerManagerInternal = new PowerManagerInternalWrapper();
1142             if (mAudioManager == null) {
1143                 mAudioManager = new DefaultAudioManagerWrapper(getContext());
1144             }
1145             mStreamMusicMaxVolume = getAudioManager().getStreamMaxVolume(AudioManager.STREAM_MUSIC);
1146             if (mAudioDeviceVolumeManager == null) {
1147                 mAudioDeviceVolumeManager =
1148                         new DefaultAudioDeviceVolumeManagerWrapper(getContext());
1149             }
1150             getAudioDeviceVolumeManager().addOnDeviceVolumeBehaviorChangedListener(
1151                     mServiceThreadExecutor, this::onDeviceVolumeBehaviorChanged);
1152         } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
1153             runOnServiceThread(this::bootCompleted);
1154         }
1155     }
1156 
getTvInputManager()1157     TvInputManager getTvInputManager() {
1158         return mTvInputManager;
1159     }
1160 
registerTvInputCallback(TvInputCallback callback)1161     void registerTvInputCallback(TvInputCallback callback) {
1162         if (mTvInputManager == null) return;
1163         mTvInputManager.registerCallback(callback, mHandler);
1164     }
1165 
unregisterTvInputCallback(TvInputCallback callback)1166     void unregisterTvInputCallback(TvInputCallback callback) {
1167         if (mTvInputManager == null) return;
1168         mTvInputManager.unregisterCallback(callback);
1169     }
1170 
1171     @VisibleForTesting
setDeviceConfig(DeviceConfigWrapper deviceConfig)1172     void setDeviceConfig(DeviceConfigWrapper deviceConfig) {
1173         mDeviceConfig = deviceConfig;
1174     }
1175 
1176     @VisibleForTesting
setPowerManager(PowerManagerWrapper powerManager)1177     void setPowerManager(PowerManagerWrapper powerManager) {
1178         mPowerManager = powerManager;
1179     }
1180 
1181     @VisibleForTesting
setPowerManagerInternal(PowerManagerInternalWrapper powerManagerInternal)1182     void setPowerManagerInternal(PowerManagerInternalWrapper powerManagerInternal) {
1183         mPowerManagerInternal = powerManagerInternal;
1184     }
1185 
getDeviceConfig()1186     DeviceConfigWrapper getDeviceConfig() {
1187         return mDeviceConfig;
1188     }
1189 
getPowerManager()1190     PowerManagerWrapper getPowerManager() {
1191         return mPowerManager;
1192     }
1193 
getPowerManagerInternal()1194     PowerManagerInternalWrapper getPowerManagerInternal() {
1195         return mPowerManagerInternal;
1196     }
1197 
1198     /**
1199      * Triggers the address allocation that states the presence of a local device audio system in
1200      * the network.
1201      */
1202     @VisibleForTesting
setSoundbarMode(final int settingValue)1203     public void setSoundbarMode(final int settingValue) {
1204         boolean isArcSupported = isArcSupported();
1205         HdmiCecLocalDevicePlayback playback = playback();
1206         HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
1207         getAtomWriter().dsmStatusChanged(isArcSupported,
1208                 settingValue == SOUNDBAR_MODE_ENABLED,
1209                 HdmiStatsEnums.LOG_REASON_DSM_SETTING_TOGGLED);
1210 
1211         if (playback == null) {
1212             Slog.w(TAG, "Device type not compatible to change soundbar mode.");
1213             return;
1214         }
1215         if (!isArcSupported) {
1216             Slog.w(TAG, "Device type doesn't support ARC.");
1217             return;
1218         }
1219         boolean isArcEnabled = false;
1220         if (settingValue == SOUNDBAR_MODE_DISABLED && audioSystem != null) {
1221             isArcEnabled = audioSystem.isArcEnabled();
1222             if (isSystemAudioActivated()) {
1223                 audioSystem.terminateSystemAudioMode();
1224             }
1225             if (isArcEnabled) {
1226                 audioSystem.addAndStartAction(new ArcTerminationActionFromAvr(audioSystem,
1227                         new IHdmiControlCallback.Stub() {
1228                             @Override
1229                             public void onComplete(int result) {
1230                                 mAddressAllocated = false;
1231                                 initializeCecLocalDevices(INITIATED_BY_SOUNDBAR_MODE);
1232                             }
1233                         }), true);
1234             }
1235         }
1236         if (!isArcEnabled) {
1237             mAddressAllocated = false;
1238             initializeCecLocalDevices(INITIATED_BY_SOUNDBAR_MODE);
1239         }
1240     }
1241 
1242     /**
1243      * Checks if the Device Discovery is handled by the local device playback.
1244      * See {@link HdmiCecLocalDeviceAudioSystem#launchDeviceDiscovery}.
1245      */
isDeviceDiscoveryHandledByPlayback()1246     public boolean isDeviceDiscoveryHandledByPlayback() {
1247         HdmiCecLocalDevicePlayback playback = playback();
1248         if (playback != null && (playback.hasAction(DeviceDiscoveryAction.class)
1249                 || playback.hasAction(HotplugDetectionAction.class))) {
1250             return true;
1251         }
1252         return false;
1253     }
1254 
1255     /**
1256      * Called when the initialization of local devices is complete.
1257      */
onInitializeCecComplete(int initiatedBy)1258     private void onInitializeCecComplete(int initiatedBy) {
1259         updatePowerStatusOnInitializeCecComplete();
1260         mWakeUpMessageReceived = false;
1261 
1262         if (isTvDeviceEnabled()) {
1263             mCecController.enableWakeupByOtp(tv().getAutoWakeup());
1264         }
1265         int reason = -1;
1266         switch (initiatedBy) {
1267             case INITIATED_BY_BOOT_UP:
1268                 reason = HdmiControlManager.CONTROL_STATE_CHANGED_REASON_START;
1269                 break;
1270             case INITIATED_BY_ENABLE_CEC:
1271                 reason = HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING;
1272                 break;
1273             case INITIATED_BY_SCREEN_ON:
1274                 reason = HdmiControlManager.CONTROL_STATE_CHANGED_REASON_WAKEUP;
1275                 final List<HdmiCecLocalDevice> devices = getAllCecLocalDevices();
1276                 for (HdmiCecLocalDevice device : devices) {
1277                     device.onInitializeCecComplete(initiatedBy);
1278                 }
1279                 break;
1280             case INITIATED_BY_WAKE_UP_MESSAGE:
1281                 reason = HdmiControlManager.CONTROL_STATE_CHANGED_REASON_WAKEUP;
1282                 break;
1283         }
1284         if (reason != -1) {
1285             invokeVendorCommandListenersOnControlStateChanged(true, reason);
1286             announceHdmiControlStatusChange(HDMI_CEC_CONTROL_ENABLED);
1287         }
1288     }
1289 
1290     /**
1291      * Updates the power status once the initialization of local devices is complete.
1292      */
updatePowerStatusOnInitializeCecComplete()1293     private void updatePowerStatusOnInitializeCecComplete() {
1294         if (mPowerStatusController.isPowerStatusTransientToOn()) {
1295             mHandler.post(() -> mPowerStatusController.setPowerStatus(
1296                     HdmiControlManager.POWER_STATUS_ON));
1297         } else if (mPowerStatusController.isPowerStatusTransientToStandby()) {
1298             mHandler.post(() -> mPowerStatusController.setPowerStatus(
1299                     HdmiControlManager.POWER_STATUS_STANDBY));
1300         }
1301     }
1302 
registerContentObserver()1303     private void registerContentObserver() {
1304         ContentResolver resolver = getContext().getContentResolver();
1305         String[] settings = new String[] {
1306                 Global.MHL_INPUT_SWITCHING_ENABLED,
1307                 Global.MHL_POWER_CHARGE_ENABLED,
1308                 Global.DEVICE_NAME
1309         };
1310         for (String s : settings) {
1311             resolver.registerContentObserver(Global.getUriFor(s), false, mSettingsObserver,
1312                     UserHandle.USER_ALL);
1313         }
1314     }
1315 
1316     private class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler)1317         public SettingsObserver(Handler handler) {
1318             super(handler);
1319         }
1320 
1321         // onChange is set up to run in service thread.
1322         @Override
onChange(boolean selfChange, Uri uri)1323         public void onChange(boolean selfChange, Uri uri) {
1324             String option = uri.getLastPathSegment();
1325             boolean enabled = readBooleanSetting(option, true);
1326             switch (option) {
1327                 case Global.MHL_INPUT_SWITCHING_ENABLED:
1328                     setMhlInputChangeEnabled(enabled);
1329                     break;
1330                 case Global.MHL_POWER_CHARGE_ENABLED:
1331                     mMhlController.setOption(OPTION_MHL_POWER_CHARGE, toInt(enabled));
1332                     break;
1333                 case Global.DEVICE_NAME:
1334                     String deviceName = readStringSetting(option, Build.MODEL);
1335                     setDisplayName(deviceName);
1336                     break;
1337             }
1338         }
1339     }
1340 
toInt(boolean enabled)1341     private static int toInt(boolean enabled) {
1342         return enabled ? ENABLED : DISABLED;
1343     }
1344 
1345     @VisibleForTesting
readBooleanSetting(String key, boolean defVal)1346     boolean readBooleanSetting(String key, boolean defVal) {
1347         ContentResolver cr = getContext().getContentResolver();
1348         return Global.getInt(cr, key, toInt(defVal)) == ENABLED;
1349     }
1350 
1351     @VisibleForTesting
readIntSetting(String key, int defVal)1352     int readIntSetting(String key, int defVal) {
1353         ContentResolver cr = getContext().getContentResolver();
1354         return Global.getInt(cr, key, defVal);
1355     }
1356 
writeBooleanSetting(String key, boolean value)1357     void writeBooleanSetting(String key, boolean value) {
1358         ContentResolver cr = getContext().getContentResolver();
1359         Global.putInt(cr, key, toInt(value));
1360     }
1361 
1362     @VisibleForTesting
writeStringSystemProperty(String key, String value)1363     protected void writeStringSystemProperty(String key, String value) {
1364         SystemProperties.set(key, value);
1365     }
1366 
1367     @VisibleForTesting
readBooleanSystemProperty(String key, boolean defVal)1368     boolean readBooleanSystemProperty(String key, boolean defVal) {
1369         return SystemProperties.getBoolean(key, defVal);
1370     }
1371 
readStringSetting(String key, String defVal)1372     String readStringSetting(String key, String defVal) {
1373         ContentResolver cr = getContext().getContentResolver();
1374         String content = Global.getString(cr, key);
1375         if (TextUtils.isEmpty(content)) {
1376             return defVal;
1377         }
1378         return content;
1379     }
1380 
writeStringSetting(String key, String value)1381     void writeStringSetting(String key, String value) {
1382         ContentResolver cr = getContext().getContentResolver();
1383         Global.putString(cr, key, value);
1384     }
1385 
initializeCec(int initiatedBy)1386     private void initializeCec(int initiatedBy) {
1387         mAddressAllocated = false;
1388         int settingsCecVersion = getHdmiCecConfig().getIntValue(
1389                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION);
1390         int supportedCecVersion = mCecController.getVersion();
1391 
1392         // Limit the used CEC version to the highest supported version by HAL and selected
1393         // version in settings (but at least v1.4b).
1394         mCecVersion = Math.max(HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
1395                 Math.min(settingsCecVersion, supportedCecVersion));
1396 
1397         mCecController.enableSystemCecControl(true);
1398         mCecController.setLanguage(mMenuLanguage);
1399         initializeCecLocalDevices(initiatedBy);
1400     }
1401 
1402     /**
1403      * If the Soundbar mode is turned on, adds the local device type audio system in the list of
1404      * local devices types. This method is called when the local devices are initialized such that
1405      * the list of local devices is in sync with the Soundbar mode setting.
1406      * @return the list of integer device types
1407      */
1408     @ServiceThreadOnly
getCecLocalDeviceTypes()1409     private List<Integer> getCecLocalDeviceTypes() {
1410         ArrayList<Integer> allLocalDeviceTypes = new ArrayList<>(mCecLocalDevices);
1411         if (!isTvDevice() && isDsmEnabled()
1412                 && !allLocalDeviceTypes.contains(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM)
1413                 && isArcSupported() && mSoundbarModeFeatureFlagEnabled) {
1414             allLocalDeviceTypes.add(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
1415         }
1416         return allLocalDeviceTypes;
1417     }
1418 
1419     @ServiceThreadOnly
1420     @VisibleForTesting
initializeCecLocalDevices(final int initiatedBy)1421     protected void initializeCecLocalDevices(final int initiatedBy) {
1422         assertRunOnServiceThread();
1423         // A container for [Device type, Local device info].
1424         ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
1425         for (int type : getCecLocalDeviceTypes()) {
1426             HdmiCecLocalDevice localDevice = mHdmiCecNetwork.getLocalDevice(type);
1427             if (localDevice == null) {
1428                 localDevice = HdmiCecLocalDevice.create(this, type);
1429             }
1430             localDevice.init();
1431             localDevices.add(localDevice);
1432         }
1433         mHdmiCecNetwork.clearDeviceList();
1434         allocateLogicalAddress(localDevices, initiatedBy);
1435     }
1436 
1437     @ServiceThreadOnly
1438     @VisibleForTesting
allocateLogicalAddress(final ArrayList<HdmiCecLocalDevice> allocatingDevices, final int initiatedBy)1439     protected void allocateLogicalAddress(final ArrayList<HdmiCecLocalDevice> allocatingDevices,
1440             final int initiatedBy) {
1441         assertRunOnServiceThread();
1442         mCecController.clearLogicalAddress();
1443         final ArrayList<HdmiCecLocalDevice> allocatedDevices = new ArrayList<>();
1444         final int[] finished = new int[1];
1445         mAddressAllocated = allocatingDevices.isEmpty();
1446 
1447         // For TV device, select request can be invoked while address allocation or device
1448         // discovery is in progress. Initialize the request here at the start of allocation,
1449         // and process the collected requests later when the allocation and device discovery
1450         // is all completed.
1451         mSelectRequestBuffer.clear();
1452 
1453         for (final HdmiCecLocalDevice localDevice : allocatingDevices) {
1454             mCecController.allocateLogicalAddress(localDevice.getType(),
1455                     localDevice.getPreferredAddress(), new AllocateAddressCallback() {
1456                         @Override
1457                         public void onAllocated(int deviceType, int logicalAddress) {
1458                             if (logicalAddress == Constants.ADDR_UNREGISTERED) {
1459                                 Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType
1460                                         + "]");
1461                                 mHdmiCecNetwork.removeLocalDeviceWithType(deviceType);
1462                             } else {
1463                                 // Set POWER_STATUS_ON to all local devices because they share
1464                                 // lifetime
1465                                 // with system.
1466                                 HdmiDeviceInfo deviceInfo = createDeviceInfo(logicalAddress,
1467                                         deviceType,
1468                                         HdmiControlManager.POWER_STATUS_ON, getCecVersion());
1469                                 localDevice.setDeviceInfo(deviceInfo);
1470                                 // If a local device of the same type already exists, it will be
1471                                 // replaced.
1472                                 mHdmiCecNetwork.addLocalDevice(deviceType, localDevice);
1473                                 mHdmiCecNetwork.addCecDevice(localDevice.getDeviceInfo());
1474                                 mCecController.addLogicalAddress(logicalAddress);
1475                                 allocatedDevices.add(localDevice);
1476                             }
1477 
1478                             // Address allocation completed for all devices. Notify each device.
1479                             if (allocatingDevices.size() == ++finished[0]) {
1480                                 if (initiatedBy != INITIATED_BY_HOTPLUG
1481                                         && initiatedBy != INITIATED_BY_SOUNDBAR_MODE) {
1482                                     // In case of the hotplug or soundbar mode setting toggle
1483                                     // we don't call onInitializeCecComplete()
1484                                     // since we reallocate the logical address only.
1485                                     onInitializeCecComplete(initiatedBy);
1486                                 } else if (initiatedBy == INITIATED_BY_HOTPLUG
1487                                         && mDisplayStatusCallback == null) {
1488                                     // Force to update display status for hotplug event.
1489                                     synchronized (mLock) {
1490                                         announceHdmiControlStatusChange(mHdmiControlEnabled);
1491                                     }
1492                                 }
1493                                 // We remove local devices here, instead of before the start of
1494                                 // address allocation, to prevent multiple local devices of the
1495                                 // same type from existing simultaneously.
1496                                 mHdmiCecNetwork.removeUnusedLocalDevices(allocatedDevices);
1497                                 mAddressAllocated = true;
1498                                 notifyAddressAllocated(allocatedDevices, initiatedBy);
1499                                 // Reinvoke the saved display status callback once the local
1500                                 // device is ready.
1501                                 if (mDisplayStatusCallback != null) {
1502                                     queryDisplayStatus(mDisplayStatusCallback);
1503                                     mDisplayStatusCallback = null;
1504                                 }
1505                                 if (mOtpCallbackPendingAddressAllocation != null) {
1506                                     oneTouchPlay(mOtpCallbackPendingAddressAllocation);
1507                                     mOtpCallbackPendingAddressAllocation = null;
1508                                 }
1509                                 mCecMessageBuffer.processMessages();
1510                             }
1511                         }
1512                     });
1513         }
1514     }
1515 
1516     /**
1517      * Notifies local devices that address allocation finished.
1518      * @param devices - list of local devices allocated.
1519      * @param initiatedBy - reason for the address allocation.
1520      */
1521     @VisibleForTesting
1522     @ServiceThreadOnly
notifyAddressAllocated(ArrayList<HdmiCecLocalDevice> devices, int initiatedBy)1523     public void notifyAddressAllocated(ArrayList<HdmiCecLocalDevice> devices, int initiatedBy) {
1524         assertRunOnServiceThread();
1525         if (devices == null || devices.isEmpty()) {
1526             Slog.w(TAG, "No local device to notify.");
1527             return;
1528         }
1529         List<HdmiCecMessage> bufferedMessages = mCecMessageBuffer.getBuffer();
1530         for (HdmiCecLocalDevice device : devices) {
1531             int address = device.getDeviceInfo().getLogicalAddress();
1532             device.handleAddressAllocated(address, bufferedMessages, initiatedBy);
1533         }
1534         if (isTvDeviceEnabled()) {
1535             tv().setSelectRequestBuffer(mSelectRequestBuffer);
1536         }
1537     }
1538 
isAddressAllocated()1539     boolean isAddressAllocated() {
1540         return mAddressAllocated;
1541     }
1542 
getPortInfo()1543     List<HdmiPortInfo> getPortInfo() {
1544         synchronized (mLock) {
1545             return mHdmiCecNetwork.getPortInfo();
1546         }
1547     }
1548 
getPortInfo(int portId)1549     HdmiPortInfo getPortInfo(int portId) {
1550         return mHdmiCecNetwork.getPortInfo(portId);
1551     }
1552 
1553     /**
1554      * Returns the routing path (physical address) of the HDMI port for the given
1555      * port id.
1556      */
portIdToPath(int portId)1557     int portIdToPath(int portId) {
1558         return mHdmiCecNetwork.portIdToPath(portId);
1559     }
1560 
1561     /**
1562      * Returns the id of HDMI port located at the current device that runs this method.
1563      *
1564      * For TV with physical address 0x0000, target device 0x1120, we want port physical address
1565      * 0x1000 to get the correct port id from {@link #mPortIdMap}. For device with Physical Address
1566      * 0x2000, target device 0x2420, we want port address 0x24000 to get the port id.
1567      *
1568      * <p>Return {@link Constants#INVALID_PORT_ID} if target device does not connect to.
1569      *
1570      * @param path the target device's physical address.
1571      * @return the id of the port that the target device eventually connects to
1572      * on the current device.
1573      */
pathToPortId(int path)1574     int pathToPortId(int path) {
1575         return mHdmiCecNetwork.physicalAddressToPortId(path);
1576     }
1577 
isValidPortId(int portId)1578     boolean isValidPortId(int portId) {
1579         return mHdmiCecNetwork.getPortInfo(portId) != null;
1580     }
1581 
1582     /**
1583      * Returns {@link Looper} for IO operation.
1584      */
1585     @Nullable
1586     @VisibleForTesting
getIoLooper()1587     protected Looper getIoLooper() {
1588         return mIoLooper;
1589     }
1590 
1591     @VisibleForTesting
setIoLooper(Looper ioLooper)1592     void setIoLooper(Looper ioLooper) {
1593         mIoLooper = ioLooper;
1594     }
1595 
1596     @VisibleForTesting
setCecMessageBuffer(CecMessageBuffer cecMessageBuffer)1597     void setCecMessageBuffer(CecMessageBuffer cecMessageBuffer) {
1598         this.mCecMessageBuffer = cecMessageBuffer;
1599     }
1600 
getCecMessageWithOpcode(int opcode)1601     List<HdmiCecMessage> getCecMessageWithOpcode(int opcode) {
1602         List<HdmiCecMessage> cecMessagesWithOpcode = new ArrayList<>();
1603         List<HdmiCecMessage> cecMessages = mCecMessageBuffer.getBuffer();
1604         for (HdmiCecMessage message: cecMessages) {
1605             if (message.getOpcode() == opcode) {
1606                 cecMessagesWithOpcode.add(message);
1607             }
1608         }
1609         return cecMessagesWithOpcode;
1610     }
1611 
1612     /**
1613      * Returns {@link Looper} of main thread. Use this {@link Looper} instance
1614      * for tasks that are running on main service thread.
1615      */
getServiceLooper()1616     protected Looper getServiceLooper() {
1617         return mHandler.getLooper();
1618     }
1619 
1620     /**
1621      * Returns physical address of the device.
1622      */
getPhysicalAddress()1623     int getPhysicalAddress() {
1624         return mHdmiCecNetwork.getPhysicalAddress();
1625     }
1626 
1627     /**
1628      * Returns vendor id of CEC service.
1629      */
getVendorId()1630     int getVendorId() {
1631         return mCecController.getVendorId();
1632     }
1633 
1634     @Nullable
1635     @ServiceThreadOnly
getDeviceInfo(int logicalAddress)1636     HdmiDeviceInfo getDeviceInfo(int logicalAddress) {
1637         assertRunOnServiceThread();
1638         return mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
1639     }
1640 
1641     @ServiceThreadOnly
getDeviceInfoByPort(int port)1642     HdmiDeviceInfo getDeviceInfoByPort(int port) {
1643         assertRunOnServiceThread();
1644         HdmiMhlLocalDeviceStub info = mMhlController.getLocalDevice(port);
1645         if (info != null) {
1646             return info.getInfo();
1647         }
1648         return null;
1649     }
1650 
1651     /**
1652      * Returns version of CEC.
1653      */
1654     @VisibleForTesting
1655     @HdmiControlManager.HdmiCecVersion
getCecVersion()1656     protected int getCecVersion() {
1657         return mCecVersion;
1658     }
1659 
1660     /**
1661      * Whether a device of the specified physical address is connected to ARC enabled port.
1662      */
isConnectedToArcPort(int physicalAddress)1663     boolean isConnectedToArcPort(int physicalAddress) {
1664         return mHdmiCecNetwork.isConnectedToArcPort(physicalAddress);
1665     }
1666 
1667     @ServiceThreadOnly
isConnected(int portId)1668     boolean isConnected(int portId) {
1669         assertRunOnServiceThread();
1670         return mCecController.isConnected(portId);
1671     }
1672 
1673     /**
1674      * Executes a Runnable on the service thread.
1675      * During execution, sets the work source UID to the parent's work source UID.
1676      *
1677      * @param runnable The runnable to execute on the service thread
1678      */
runOnServiceThread(Runnable runnable)1679     void runOnServiceThread(Runnable runnable) {
1680         mHandler.post(new WorkSourceUidPreservingRunnable(runnable));
1681     }
1682 
runOnServiceThreadDelayed(Runnable runnable, long delay)1683     void runOnServiceThreadDelayed(Runnable runnable, long delay) {
1684         mHandler.postDelayed(new WorkSourceUidPreservingRunnable(runnable), delay);
1685     }
1686 
assertRunOnServiceThread()1687     private void assertRunOnServiceThread() {
1688         if (Looper.myLooper() != mHandler.getLooper()) {
1689             throw new IllegalStateException("Should run on service thread.");
1690         }
1691     }
1692 
1693     @ServiceThreadOnly
sendCecCommand(HdmiCecMessage command)1694     void sendCecCommand(HdmiCecMessage command) {
1695         sendCecCommand(command, null);
1696     }
1697 
1698     @ServiceThreadOnly
sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback)1699     void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
1700         switch (command.getOpcode()) {
1701             case Constants.MESSAGE_ACTIVE_SOURCE:
1702             case Constants.MESSAGE_IMAGE_VIEW_ON:
1703             case Constants.MESSAGE_INACTIVE_SOURCE:
1704             case Constants.MESSAGE_ROUTING_CHANGE:
1705             case Constants.MESSAGE_SET_STREAM_PATH:
1706             case Constants.MESSAGE_TEXT_VIEW_ON:
1707                 // RequestActiveSourceAction is started after the TV finished logical address
1708                 // allocation. This action is used by the TV to get the active source from the CEC
1709                 // network. If the TV sent a source changing CEC message, this action does not have
1710                 // to continue anymore.
1711                 if (isTvDeviceEnabled()) {
1712                     tv().removeAction(RequestActiveSourceAction.class);
1713                 }
1714                 sendCecCommandWithRetries(command, callback);
1715                 break;
1716             default:
1717                 sendCecCommandWithoutRetries(command, callback);
1718         }
1719     }
1720 
1721     /**
1722      * Create a {@link ResendCecCommandAction} that will retry sending the CEC message if it fails.
1723      * @param command  command to be sent on the CEC bus.
1724      * @param callback callback for handling the result of sending the command.
1725      */
1726     @ServiceThreadOnly
sendCecCommandWithRetries(HdmiCecMessage command, @Nullable SendMessageCallback callback)1727     private void sendCecCommandWithRetries(HdmiCecMessage command,
1728             @Nullable SendMessageCallback callback) {
1729         assertRunOnServiceThread();
1730         List<HdmiCecLocalDevice> devices = getAllCecLocalDevices();
1731         if (devices.isEmpty()) {
1732             return;
1733         }
1734         HdmiCecLocalDevice localDevice = devices.get(0);
1735         if (localDevice != null) {
1736             sendCecCommandWithoutRetries(command, new SendMessageCallback() {
1737                 @Override
1738                 public void onSendCompleted(int result) {
1739                     if (result != SendMessageResult.SUCCESS) {
1740                         localDevice.addAndStartAction(new
1741                                 ResendCecCommandAction(localDevice, command, callback));
1742                     } else if (callback != null) {
1743                         callback.onSendCompleted(result);
1744                     }
1745                 }
1746             });
1747         }
1748     }
1749 
1750 
1751     /**
1752      * Transmit a CEC command to CEC bus.
1753      *
1754      * @param command CEC command to send out
1755      * @param callback interface used to the result of send command
1756      */
1757     @ServiceThreadOnly
sendCecCommandWithoutRetries(HdmiCecMessage command, @Nullable SendMessageCallback callback)1758     void sendCecCommandWithoutRetries(HdmiCecMessage command,
1759             @Nullable SendMessageCallback callback) {
1760         assertRunOnServiceThread();
1761         if (command.getValidationResult() == HdmiCecMessageValidator.OK
1762                 && verifyPhysicalAddresses(command)) {
1763             mCecController.sendCommand(command, callback);
1764         } else {
1765             HdmiLogger.error("Invalid message type:" + command);
1766             if (callback != null) {
1767                 callback.onSendCompleted(SendMessageResult.FAIL);
1768             }
1769         }
1770     }
1771 
1772     /**
1773      * Send <Feature Abort> command on the given CEC message if possible.
1774      * If the aborted message is invalid, then it wont send the message.
1775      * @param command original command to be aborted
1776      * @param reason reason of feature abort
1777      */
1778     @ServiceThreadOnly
maySendFeatureAbortCommand(HdmiCecMessage command, int reason)1779     void maySendFeatureAbortCommand(HdmiCecMessage command, int reason) {
1780         assertRunOnServiceThread();
1781         mCecController.maySendFeatureAbortCommand(command, reason);
1782     }
1783 
1784     /**
1785      * Returns whether all of the physical addresses in a message could exist in this CEC network.
1786      */
verifyPhysicalAddresses(HdmiCecMessage message)1787     boolean verifyPhysicalAddresses(HdmiCecMessage message) {
1788         byte[] params = message.getParams();
1789         switch (message.getOpcode()) {
1790             case Constants.MESSAGE_ROUTING_CHANGE:
1791                 return verifyPhysicalAddress(params, 0)
1792                         && verifyPhysicalAddress(params, 2);
1793             case Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST:
1794                 return params.length == 0 || verifyPhysicalAddress(params, 0);
1795             case Constants.MESSAGE_ACTIVE_SOURCE:
1796             case Constants.MESSAGE_INACTIVE_SOURCE:
1797             case Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS:
1798             case Constants.MESSAGE_ROUTING_INFORMATION:
1799             case Constants.MESSAGE_SET_STREAM_PATH:
1800                 return verifyPhysicalAddress(params, 0);
1801             case Constants.MESSAGE_CLEAR_EXTERNAL_TIMER:
1802             case Constants.MESSAGE_SET_EXTERNAL_TIMER:
1803                 return verifyExternalSourcePhysicalAddress(params, 7);
1804             default:
1805                 return true;
1806         }
1807     }
1808 
1809     /**
1810      * Returns whether a given physical address could exist in this CEC network.
1811      * For a TV, the physical address must either be the address of the TV itself,
1812      * or the address of a device connected to one of its ports (possibly indirectly).
1813      */
verifyPhysicalAddress(byte[] params, int offset)1814     private boolean verifyPhysicalAddress(byte[] params, int offset) {
1815         if (!isTvDevice()) {
1816             // If the device is not TV, we can't convert path to port-id, so stop here.
1817             return true;
1818         }
1819         // Invalidate the physical address if parameters length is too short.
1820         if (params.length < offset + 2) {
1821             return false;
1822         }
1823         int path = HdmiUtils.twoBytesToInt(params, offset);
1824         if (path != Constants.INVALID_PHYSICAL_ADDRESS && path == getPhysicalAddress()) {
1825             return true;
1826         }
1827         int portId = pathToPortId(path);
1828         if (portId == Constants.INVALID_PORT_ID) {
1829             return false;
1830         }
1831         return true;
1832     }
1833 
1834     /**
1835      * Returns whether the physical address of an external source could exist in this network.
1836      */
verifyExternalSourcePhysicalAddress(byte[] params, int offset)1837     private boolean verifyExternalSourcePhysicalAddress(byte[] params, int offset) {
1838         int externalSourceSpecifier = params[offset];
1839         offset = offset + 1;
1840         if (externalSourceSpecifier == 0x05) {
1841             if (params.length - offset >= 2) {
1842                 return verifyPhysicalAddress(params, offset);
1843             }
1844         }
1845         return true;
1846     }
1847 
1848     /**
1849      * Returns whether the source address of a message is a local logical address.
1850      */
sourceAddressIsLocal(HdmiCecMessage message)1851     private boolean sourceAddressIsLocal(HdmiCecMessage message) {
1852         for (HdmiCecLocalDevice device : getAllCecLocalDevices()) {
1853             if (message.getSource() == device.getDeviceInfo().getLogicalAddress()
1854                     && message.getSource() != Constants.ADDR_UNREGISTERED) {
1855                 HdmiLogger.warning(
1856                         "Unexpected source: message sent from device itself, " + message);
1857                 return true;
1858             }
1859         }
1860         return false;
1861     }
1862 
1863     @ServiceThreadOnly
1864     @VisibleForTesting
1865     @Constants.HandleMessageResult
handleCecCommand(HdmiCecMessage message)1866     protected int handleCecCommand(HdmiCecMessage message) {
1867         assertRunOnServiceThread();
1868 
1869         @HdmiCecMessageValidator.ValidationResult
1870         int validationResult = message.getValidationResult();
1871         if (validationResult == HdmiCecMessageValidator.ERROR_PARAMETER
1872                 || validationResult == HdmiCecMessageValidator.ERROR_PARAMETER_LONG
1873                 || !verifyPhysicalAddresses(message)) {
1874             return Constants.ABORT_INVALID_OPERAND;
1875         } else if (validationResult != HdmiCecMessageValidator.OK
1876                 || sourceAddressIsLocal(message)) {
1877             return Constants.HANDLED;
1878         }
1879 
1880         getHdmiCecNetwork().handleCecMessage(message);
1881 
1882         @Constants.HandleMessageResult int handleMessageResult =
1883                 dispatchMessageToLocalDevice(message);
1884         // mAddressAllocated is false during address allocation, meaning there is no device to
1885         // handle the message, so it should be buffered, if possible.
1886         if (!mAddressAllocated
1887                 && mCecMessageBuffer.bufferMessage(message)) {
1888             return Constants.HANDLED;
1889         }
1890 
1891         return handleMessageResult;
1892     }
1893 
enableAudioReturnChannel(int portId, boolean enabled)1894     void enableAudioReturnChannel(int portId, boolean enabled) {
1895         if (!mTransitionFromArcToEarcTxEnabled && enabled && mEarcController != null) {
1896             // If the feature flag is set to false, prevent eARC from establishing if ARC is already
1897             // established.
1898             setEarcEnabledInHal(false, false);
1899         }
1900         mCecController.enableAudioReturnChannel(portId, enabled);
1901     }
1902 
1903     @ServiceThreadOnly
1904     @VisibleForTesting
1905     @Constants.HandleMessageResult
dispatchMessageToLocalDevice(HdmiCecMessage message)1906     protected int dispatchMessageToLocalDevice(HdmiCecMessage message) {
1907         assertRunOnServiceThread();
1908         for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
1909             @Constants.HandleMessageResult int messageResult = device.dispatchMessage(message);
1910             if (messageResult != Constants.NOT_HANDLED
1911                     && message.getDestination() != Constants.ADDR_BROADCAST) {
1912                 return messageResult;
1913             }
1914         }
1915 
1916         // We should never respond <Feature Abort> to a broadcast message
1917         if (message.getDestination() == Constants.ADDR_BROADCAST) {
1918             return Constants.HANDLED;
1919         } else {
1920             HdmiLogger.warning("Unhandled cec command:" + message);
1921             return Constants.NOT_HANDLED;
1922         }
1923     }
1924 
1925     /**
1926      * Called when a new hotplug event is issued.
1927      *
1928      * @param portId hdmi port number where hot plug event issued.
1929      * @param connected whether to be plugged in or not
1930      */
1931     @ServiceThreadOnly
onHotplug(int portId, boolean connected)1932     void onHotplug(int portId, boolean connected) {
1933         assertRunOnServiceThread();
1934         // initPortInfo at hotplug event.
1935         mHdmiCecNetwork.initPortInfo();
1936 
1937         HdmiPortInfo portInfo = getPortInfo(portId);
1938         if (connected && !isTvDevice()
1939                 && portInfo != null && portInfo.getType() == HdmiPortInfo.PORT_OUTPUT) {
1940             ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
1941             for (int type : getCecLocalDeviceTypes()) {
1942                 HdmiCecLocalDevice localDevice = mHdmiCecNetwork.getLocalDevice(type);
1943                 if (localDevice == null) {
1944                     localDevice = HdmiCecLocalDevice.create(this, type);
1945                     localDevice.init();
1946                 }
1947                 localDevices.add(localDevice);
1948             }
1949             allocateLogicalAddress(localDevices, INITIATED_BY_HOTPLUG);
1950         }
1951 
1952         for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
1953             device.onHotplug(portId, connected);
1954         }
1955 
1956         announceHotplugEvent(portId, connected);
1957     }
1958 
1959     /**
1960      * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
1961      * devices.
1962      *
1963      * @param callback an interface used to get a list of all remote devices' address
1964      * @param sourceAddress a logical address of source device where sends polling message
1965      * @param pickStrategy strategy how to pick polling candidates
1966      * @param retryCount the number of retry used to send polling message to remote devices
1967      * @throws IllegalArgumentException if {@code pickStrategy} is invalid value
1968      */
1969     @ServiceThreadOnly
pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy, int retryCount, long pollingMessageInterval)1970     void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
1971             int retryCount, long pollingMessageInterval) {
1972         assertRunOnServiceThread();
1973         mCecController.pollDevices(callback, sourceAddress, checkPollStrategy(pickStrategy),
1974                 retryCount, pollingMessageInterval);
1975     }
1976 
checkPollStrategy(int pickStrategy)1977     private int checkPollStrategy(int pickStrategy) {
1978         int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
1979         if (strategy == 0) {
1980             throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
1981         }
1982         int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
1983         if (iterationStrategy == 0) {
1984             throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
1985         }
1986         return strategy | iterationStrategy;
1987     }
1988 
getAllCecLocalDevices()1989     List<HdmiCecLocalDevice> getAllCecLocalDevices() {
1990         assertRunOnServiceThread();
1991         return mHdmiCecNetwork.getLocalDeviceList();
1992     }
1993 
1994     /**
1995      * Check if a logical address is conflict with the current device's. Reallocate the logical
1996      * address of the current device if there is conflict.
1997      *
1998      * Android HDMI CEC 1.4 is handling logical address allocation in the framework side. This could
1999      * introduce delay between the logical address allocation and notifying the driver that the
2000      * address is occupied. Adding this check to avoid such case.
2001      *
2002      * @param logicalAddress logical address of the remote device that might have the same logical
2003      * address as the current device.
2004      * @param physicalAddress physical address of the given device.
2005      */
checkLogicalAddressConflictAndReallocate(int logicalAddress, int physicalAddress)2006     protected void checkLogicalAddressConflictAndReallocate(int logicalAddress,
2007             int physicalAddress) {
2008         // The given device is a local device. No logical address conflict.
2009         if (physicalAddress == getPhysicalAddress()) {
2010             return;
2011         }
2012         for (HdmiCecLocalDevice device : getAllCecLocalDevices()) {
2013             if (device.getDeviceInfo().getLogicalAddress() == logicalAddress) {
2014                 HdmiLogger.debug("allocate logical address for " + device.getDeviceInfo());
2015                 ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
2016                 localDevices.add(device);
2017                 allocateLogicalAddress(localDevices, HdmiControlService.INITIATED_BY_HOTPLUG);
2018                 return;
2019             }
2020         }
2021     }
2022 
getServiceLock()2023     Object getServiceLock() {
2024         return mLock;
2025     }
2026 
setAudioStatus(boolean mute, int volume)2027     void setAudioStatus(boolean mute, int volume) {
2028         if (!isTvDeviceEnabled()
2029                 || !tv().isSystemAudioActivated()
2030                 || getHdmiCecVolumeControl()
2031                 == HdmiControlManager.VOLUME_CONTROL_DISABLED) {
2032             return;
2033         }
2034         AudioManagerWrapper audioManager = getAudioManager();
2035         boolean muted = audioManager.isStreamMute(AudioManager.STREAM_MUSIC);
2036         if (mute) {
2037             if (!muted) {
2038                 audioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
2039             }
2040         } else {
2041             if (muted) {
2042                 audioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
2043             }
2044             // FLAG_HDMI_SYSTEM_AUDIO_VOLUME prevents audio manager from announcing
2045             // volume change notification back to hdmi control service.
2046             int flag = AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME;
2047             if (0 <= volume && volume <= 100) {
2048                 Slog.i(TAG, "volume: " + volume);
2049                 flag |= AudioManager.FLAG_SHOW_UI;
2050                 audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, flag);
2051             }
2052         }
2053     }
2054 
announceSystemAudioModeChange(boolean enabled)2055     void announceSystemAudioModeChange(boolean enabled) {
2056         synchronized (mLock) {
2057             for (SystemAudioModeChangeListenerRecord record :
2058                     mSystemAudioModeChangeListenerRecords) {
2059                 invokeSystemAudioModeChangeLocked(record.mListener, enabled);
2060             }
2061         }
2062     }
2063 
createDeviceInfo(int logicalAddress, int deviceType, int powerStatus, int cecVersion)2064     private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType, int powerStatus,
2065             int cecVersion) {
2066         String displayName = readStringSetting(Global.DEVICE_NAME, Build.MODEL);
2067         return HdmiDeviceInfo.cecDeviceBuilder()
2068                 .setLogicalAddress(logicalAddress)
2069                 .setPhysicalAddress(getPhysicalAddress())
2070                 .setPortId(pathToPortId(getPhysicalAddress()))
2071                 .setDeviceType(deviceType)
2072                 .setVendorId(getVendorId())
2073                 .setDisplayName(displayName)
2074                 .setDevicePowerStatus(powerStatus)
2075                 .setCecVersion(cecVersion)
2076                 .build();
2077     }
2078 
2079     // Set the display name in HdmiDeviceInfo of the current devices to content provided by
2080     // Global.DEVICE_NAME. Only set and broadcast if the new name is different.
setDisplayName(String newDisplayName)2081     private void setDisplayName(String newDisplayName) {
2082         for (HdmiCecLocalDevice device : getAllCecLocalDevices()) {
2083             HdmiDeviceInfo deviceInfo = device.getDeviceInfo();
2084             if (deviceInfo.getDisplayName().equals(newDisplayName)) {
2085                 continue;
2086             }
2087             device.setDeviceInfo(deviceInfo.toBuilder().setDisplayName(newDisplayName).build());
2088             sendCecCommand(
2089                     HdmiCecMessageBuilder.buildSetOsdNameCommand(
2090                             deviceInfo.getLogicalAddress(), Constants.ADDR_TV, newDisplayName));
2091         }
2092     }
2093 
2094     @ServiceThreadOnly
handleMhlHotplugEvent(int portId, boolean connected)2095     void handleMhlHotplugEvent(int portId, boolean connected) {
2096         assertRunOnServiceThread();
2097         // Hotplug event is used to add/remove MHL devices as TV input.
2098         if (connected) {
2099             HdmiMhlLocalDeviceStub newDevice = new HdmiMhlLocalDeviceStub(this, portId);
2100             HdmiMhlLocalDeviceStub oldDevice = mMhlController.addLocalDevice(newDevice);
2101             if (oldDevice != null) {
2102                 oldDevice.onDeviceRemoved();
2103                 Slog.i(TAG, "Old device of port " + portId + " is removed");
2104             }
2105             invokeDeviceEventListeners(newDevice.getInfo(), DEVICE_EVENT_ADD_DEVICE);
2106             updateSafeMhlInput();
2107         } else {
2108             HdmiMhlLocalDeviceStub device = mMhlController.removeLocalDevice(portId);
2109             if (device != null) {
2110                 device.onDeviceRemoved();
2111                 invokeDeviceEventListeners(device.getInfo(), DEVICE_EVENT_REMOVE_DEVICE);
2112                 updateSafeMhlInput();
2113             } else {
2114                 Slog.w(TAG, "No device to remove:[portId=" + portId);
2115             }
2116         }
2117         announceHotplugEvent(portId, connected);
2118     }
2119 
2120     @ServiceThreadOnly
handleMhlBusModeChanged(int portId, int busmode)2121     void handleMhlBusModeChanged(int portId, int busmode) {
2122         assertRunOnServiceThread();
2123         HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
2124         if (device != null) {
2125             device.setBusMode(busmode);
2126         } else {
2127             Slog.w(TAG, "No mhl device exists for bus mode change[portId:" + portId +
2128                     ", busmode:" + busmode + "]");
2129         }
2130     }
2131 
2132     @ServiceThreadOnly
handleMhlBusOvercurrent(int portId, boolean on)2133     void handleMhlBusOvercurrent(int portId, boolean on) {
2134         assertRunOnServiceThread();
2135         HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
2136         if (device != null) {
2137             device.onBusOvercurrentDetected(on);
2138         } else {
2139             Slog.w(TAG, "No mhl device exists for bus overcurrent event[portId:" + portId + "]");
2140         }
2141     }
2142 
2143     @ServiceThreadOnly
handleMhlDeviceStatusChanged(int portId, int adopterId, int deviceId)2144     void handleMhlDeviceStatusChanged(int portId, int adopterId, int deviceId) {
2145         assertRunOnServiceThread();
2146         HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
2147 
2148         if (device != null) {
2149             device.setDeviceStatusChange(adopterId, deviceId);
2150         } else {
2151             Slog.w(TAG, "No mhl device exists for device status event[portId:"
2152                     + portId + ", adopterId:" + adopterId + ", deviceId:" + deviceId + "]");
2153         }
2154     }
2155 
2156     @ServiceThreadOnly
updateSafeMhlInput()2157     private void updateSafeMhlInput() {
2158         assertRunOnServiceThread();
2159         List<HdmiDeviceInfo> inputs = Collections.emptyList();
2160         SparseArray<HdmiMhlLocalDeviceStub> devices = mMhlController.getAllLocalDevices();
2161         for (int i = 0; i < devices.size(); ++i) {
2162             HdmiMhlLocalDeviceStub device = devices.valueAt(i);
2163             HdmiDeviceInfo info = device.getInfo();
2164             if (info != null) {
2165                 if (inputs.isEmpty()) {
2166                     inputs = new ArrayList<>();
2167                 }
2168                 inputs.add(device.getInfo());
2169             }
2170         }
2171         synchronized (mLock) {
2172             mMhlDevices = inputs;
2173         }
2174     }
2175 
2176     @GuardedBy("mLock")
getMhlDevicesLocked()2177     private List<HdmiDeviceInfo> getMhlDevicesLocked() {
2178         return mMhlDevices;
2179     }
2180 
2181     private class HdmiMhlVendorCommandListenerRecord implements IBinder.DeathRecipient {
2182         private final IHdmiMhlVendorCommandListener mListener;
2183 
HdmiMhlVendorCommandListenerRecord(IHdmiMhlVendorCommandListener listener)2184         public HdmiMhlVendorCommandListenerRecord(IHdmiMhlVendorCommandListener listener) {
2185             mListener = listener;
2186         }
2187 
2188         @Override
binderDied()2189         public void binderDied() {
2190             mMhlVendorCommandListenerRecords.remove(this);
2191         }
2192     }
2193 
2194     // Record class that monitors the event of the caller of being killed. Used to clean up
2195     // the listener list and record list accordingly.
2196     private final class HdmiControlStatusChangeListenerRecord implements IBinder.DeathRecipient {
2197         private final IHdmiControlStatusChangeListener mListener;
2198 
HdmiControlStatusChangeListenerRecord(IHdmiControlStatusChangeListener listener)2199         HdmiControlStatusChangeListenerRecord(IHdmiControlStatusChangeListener listener) {
2200             mListener = listener;
2201         }
2202 
2203         @Override
binderDied()2204         public void binderDied() {
2205             synchronized (mLock) {
2206                 mHdmiControlStatusChangeListenerRecords.remove(this);
2207             }
2208         }
2209 
2210         @Override
equals(Object obj)2211         public boolean equals(Object obj) {
2212             if (!(obj instanceof HdmiControlStatusChangeListenerRecord)) return false;
2213             if (obj == this) return true;
2214             HdmiControlStatusChangeListenerRecord other =
2215                     (HdmiControlStatusChangeListenerRecord) obj;
2216             return other.mListener == this.mListener;
2217         }
2218 
2219         @Override
hashCode()2220         public int hashCode() {
2221             return mListener.hashCode();
2222         }
2223     }
2224 
2225     // Record class that monitors the event of the caller of being killed. Used to clean up
2226     // the listener list and record list accordingly.
2227     private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
2228         private final IHdmiHotplugEventListener mListener;
2229 
HotplugEventListenerRecord(IHdmiHotplugEventListener listener)2230         public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) {
2231             mListener = listener;
2232         }
2233 
2234         @Override
binderDied()2235         public void binderDied() {
2236             synchronized (mLock) {
2237                 mHotplugEventListenerRecords.remove(this);
2238             }
2239         }
2240 
2241         @Override
equals(Object obj)2242         public boolean equals(Object obj) {
2243             if (!(obj instanceof HotplugEventListenerRecord)) return false;
2244             if (obj == this) return true;
2245             HotplugEventListenerRecord other = (HotplugEventListenerRecord) obj;
2246             return other.mListener == this.mListener;
2247         }
2248 
2249         @Override
hashCode()2250         public int hashCode() {
2251             return mListener.hashCode();
2252         }
2253     }
2254 
2255     private final class DeviceEventListenerRecord implements IBinder.DeathRecipient {
2256         private final IHdmiDeviceEventListener mListener;
2257 
DeviceEventListenerRecord(IHdmiDeviceEventListener listener)2258         public DeviceEventListenerRecord(IHdmiDeviceEventListener listener) {
2259             mListener = listener;
2260         }
2261 
2262         @Override
binderDied()2263         public void binderDied() {
2264             synchronized (mLock) {
2265                 mDeviceEventListenerRecords.remove(this);
2266             }
2267         }
2268     }
2269 
2270     private final class SystemAudioModeChangeListenerRecord implements IBinder.DeathRecipient {
2271         private final IHdmiSystemAudioModeChangeListener mListener;
2272 
SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener)2273         public SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener) {
2274             mListener = listener;
2275         }
2276 
2277         @Override
binderDied()2278         public void binderDied() {
2279             synchronized (mLock) {
2280                 mSystemAudioModeChangeListenerRecords.remove(this);
2281             }
2282         }
2283     }
2284 
2285     class VendorCommandListenerRecord implements IBinder.DeathRecipient {
2286         private final IHdmiVendorCommandListener mListener;
2287         private final int mVendorId;
2288 
VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int vendorId)2289         VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int vendorId) {
2290             mListener = listener;
2291             mVendorId = vendorId;
2292         }
2293 
2294         @Override
binderDied()2295         public void binderDied() {
2296             synchronized (mLock) {
2297                 mVendorCommandListenerRecords.remove(this);
2298             }
2299         }
2300     }
2301 
2302     private class HdmiRecordListenerRecord implements IBinder.DeathRecipient {
2303         private final IHdmiRecordListener mListener;
2304 
HdmiRecordListenerRecord(IHdmiRecordListener listener)2305         public HdmiRecordListenerRecord(IHdmiRecordListener listener) {
2306             mListener = listener;
2307         }
2308 
2309         @Override
binderDied()2310         public void binderDied() {
2311             synchronized (mLock) {
2312                 if (mRecordListenerRecord == this) {
2313                     mRecordListenerRecord = null;
2314                 }
2315             }
2316         }
2317     }
2318 
2319     /**
2320      * Sets the work source UID to the Binder calling UID.
2321      * Work source UID allows access to the original calling UID of a Binder call in the Runnables
2322      * that it spawns.
2323      * This is necessary because Runnables that are executed on the service thread
2324      * take on the calling UID of the service thread.
2325      */
setWorkSourceUidToCallingUid()2326     private void setWorkSourceUidToCallingUid() {
2327         Binder.setCallingWorkSourceUid(Binder.getCallingUid());
2328     }
2329 
enforceAccessPermission()2330     private void enforceAccessPermission() {
2331         getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
2332     }
2333 
initBinderCall()2334     private void initBinderCall() {
2335         enforceAccessPermission();
2336         setWorkSourceUidToCallingUid();
2337     }
2338 
2339     private final class BinderService extends IHdmiControlService.Stub {
2340         @Override
getSupportedTypes()2341         public int[] getSupportedTypes() {
2342             initBinderCall();
2343             // mCecLocalDevices is an unmodifiable list - no lock necessary.
2344             int[] localDevices = new int[mCecLocalDevices.size()];
2345             for (int i = 0; i < localDevices.length; ++i) {
2346                 localDevices[i] = mCecLocalDevices.get(i);
2347             }
2348             return localDevices;
2349         }
2350 
2351         @Override
2352         @Nullable
getActiveSource()2353         public HdmiDeviceInfo getActiveSource() {
2354             initBinderCall();
2355 
2356             return HdmiControlService.this.getActiveSource();
2357         }
2358 
2359         @Override
deviceSelect(final int deviceId, final IHdmiControlCallback callback)2360         public void deviceSelect(final int deviceId, final IHdmiControlCallback callback) {
2361             initBinderCall();
2362             runOnServiceThread(new Runnable() {
2363                 @Override
2364                 public void run() {
2365                     if (callback == null) {
2366                         Slog.e(TAG, "Callback cannot be null");
2367                         return;
2368                     }
2369                     HdmiCecLocalDeviceTv tv = tv();
2370                     HdmiCecLocalDevicePlayback playback = playback();
2371                     if (tv == null && playback == null) {
2372                         if (!mAddressAllocated) {
2373                             mSelectRequestBuffer.set(SelectRequestBuffer.newDeviceSelect(
2374                                     HdmiControlService.this, deviceId, callback));
2375                             return;
2376                         }
2377                         if (isTvDevice()) {
2378                             Slog.e(TAG, "Local tv device not available");
2379                             return;
2380                         }
2381                         invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
2382                         return;
2383                     }
2384                     if (tv != null) {
2385                         HdmiMhlLocalDeviceStub device = mMhlController.getLocalDeviceById(deviceId);
2386                         if (device != null) {
2387                             if (device.getPortId() == tv.getActivePortId()) {
2388                                 invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
2389                                 return;
2390                             }
2391                             // Upon selecting MHL device, we send RAP[Content On] to wake up
2392                             // the connected mobile device, start routing control to switch ports.
2393                             // callback is handled by MHL action.
2394                             device.turnOn(callback);
2395                             tv.doManualPortSwitching(device.getPortId(), null);
2396                             return;
2397                         }
2398                         tv.deviceSelect(deviceId, callback);
2399                         return;
2400                     }
2401                     playback.deviceSelect(deviceId, callback);
2402                 }
2403             });
2404         }
2405 
2406         @Override
portSelect(final int portId, final IHdmiControlCallback callback)2407         public void portSelect(final int portId, final IHdmiControlCallback callback) {
2408             initBinderCall();
2409             runOnServiceThread(new Runnable() {
2410                 @Override
2411                 public void run() {
2412                     if (callback == null) {
2413                         Slog.e(TAG, "Callback cannot be null");
2414                         return;
2415                     }
2416                     HdmiCecLocalDeviceTv tv = tv();
2417                     if (tv != null) {
2418                         tv.doManualPortSwitching(portId, callback);
2419                         return;
2420                     }
2421                     HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
2422                     if (audioSystem != null) {
2423                         audioSystem.doManualPortSwitching(portId, callback);
2424                         return;
2425                     }
2426 
2427                     if (!mAddressAllocated) {
2428                         mSelectRequestBuffer.set(SelectRequestBuffer.newPortSelect(
2429                                 HdmiControlService.this, portId, callback));
2430                         return;
2431                     }
2432                     Slog.w(TAG, "Local device not available");
2433                     invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
2434                     return;
2435                 }
2436             });
2437         }
2438 
2439         @Override
sendKeyEvent(final int deviceType, final int keyCode, final boolean isPressed)2440         public void sendKeyEvent(final int deviceType, final int keyCode, final boolean isPressed) {
2441             initBinderCall();
2442             runOnServiceThread(new Runnable() {
2443                 @Override
2444                 public void run() {
2445                     HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(mActivePortId);
2446                     if (device != null) {
2447                         device.sendKeyEvent(keyCode, isPressed);
2448                         return;
2449                     }
2450                     if (mCecController != null) {
2451                         HdmiCecLocalDevice localDevice = mHdmiCecNetwork.getLocalDevice(deviceType);
2452                         if (localDevice == null) {
2453                             Slog.w(TAG, "Local device not available to send key event.");
2454                             return;
2455                         }
2456                         localDevice.sendKeyEvent(keyCode, isPressed);
2457                     }
2458                 }
2459             });
2460         }
2461 
2462         @Override
sendVolumeKeyEvent( final int deviceType, final int keyCode, final boolean isPressed)2463         public void sendVolumeKeyEvent(
2464             final int deviceType, final int keyCode, final boolean isPressed) {
2465             initBinderCall();
2466             runOnServiceThread(new Runnable() {
2467                 @Override
2468                 public void run() {
2469                     if (mCecController == null) {
2470                         Slog.w(TAG, "CEC controller not available to send volume key event.");
2471                         return;
2472                     }
2473                     HdmiCecLocalDevice localDevice = mHdmiCecNetwork.getLocalDevice(deviceType);
2474                     if (localDevice == null) {
2475                         Slog.w(TAG, "Local device " + deviceType
2476                               + " not available to send volume key event.");
2477                         return;
2478                     }
2479                     localDevice.sendVolumeKeyEvent(keyCode, isPressed);
2480                 }
2481             });
2482         }
2483 
2484         @Override
oneTouchPlay(final IHdmiControlCallback callback)2485         public void oneTouchPlay(final IHdmiControlCallback callback) {
2486             initBinderCall();
2487             int pid = Binder.getCallingPid();
2488             Slog.d(TAG, "Process pid: " + pid + " is calling oneTouchPlay.");
2489             runOnServiceThread(new Runnable() {
2490                 @Override
2491                 public void run() {
2492                     HdmiControlService.this.oneTouchPlay(callback);
2493                 }
2494             });
2495         }
2496 
2497         @Override
toggleAndFollowTvPower()2498         public void toggleAndFollowTvPower() {
2499             initBinderCall();
2500             int pid = Binder.getCallingPid();
2501             Slog.d(TAG, "Process pid: " + pid + " is calling toggleAndFollowTvPower.");
2502             runOnServiceThread(new Runnable() {
2503                 @Override
2504                 public void run() {
2505                     HdmiControlService.this.toggleAndFollowTvPower();
2506                 }
2507             });
2508         }
2509 
2510         @Override
shouldHandleTvPowerKey()2511         public boolean shouldHandleTvPowerKey() {
2512             initBinderCall();
2513             return HdmiControlService.this.shouldHandleTvPowerKey();
2514         }
2515 
2516         @Override
queryDisplayStatus(final IHdmiControlCallback callback)2517         public void queryDisplayStatus(final IHdmiControlCallback callback) {
2518             initBinderCall();
2519             runOnServiceThread(new Runnable() {
2520                 @Override
2521                 public void run() {
2522                     HdmiControlService.this.queryDisplayStatus(callback);
2523                 }
2524             });
2525         }
2526 
2527         @Override
addHdmiControlStatusChangeListener( final IHdmiControlStatusChangeListener listener)2528         public void addHdmiControlStatusChangeListener(
2529                 final IHdmiControlStatusChangeListener listener) {
2530             initBinderCall();
2531             HdmiControlService.this.addHdmiControlStatusChangeListener(listener);
2532         }
2533 
2534         @Override
removeHdmiControlStatusChangeListener( final IHdmiControlStatusChangeListener listener)2535         public void removeHdmiControlStatusChangeListener(
2536                 final IHdmiControlStatusChangeListener listener) {
2537             initBinderCall();
2538             HdmiControlService.this.removeHdmiControlStatusChangeListener(listener);
2539         }
2540 
2541         @Override
addHdmiCecVolumeControlFeatureListener( final IHdmiCecVolumeControlFeatureListener listener)2542         public void addHdmiCecVolumeControlFeatureListener(
2543                 final IHdmiCecVolumeControlFeatureListener listener) {
2544             initBinderCall();
2545             HdmiControlService.this.addHdmiCecVolumeControlFeatureListener(listener);
2546         }
2547 
2548         @Override
removeHdmiCecVolumeControlFeatureListener( final IHdmiCecVolumeControlFeatureListener listener)2549         public void removeHdmiCecVolumeControlFeatureListener(
2550                 final IHdmiCecVolumeControlFeatureListener listener) {
2551             initBinderCall();
2552             HdmiControlService.this.removeHdmiControlVolumeControlStatusChangeListener(listener);
2553         }
2554 
2555 
2556         @Override
addHotplugEventListener(final IHdmiHotplugEventListener listener)2557         public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
2558             initBinderCall();
2559             HdmiControlService.this.addHotplugEventListener(listener);
2560         }
2561 
2562         @Override
removeHotplugEventListener(final IHdmiHotplugEventListener listener)2563         public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
2564             initBinderCall();
2565             HdmiControlService.this.removeHotplugEventListener(listener);
2566         }
2567 
2568         @Override
addDeviceEventListener(final IHdmiDeviceEventListener listener)2569         public void addDeviceEventListener(final IHdmiDeviceEventListener listener) {
2570             initBinderCall();
2571             HdmiControlService.this.addDeviceEventListener(listener);
2572         }
2573 
2574         @Override
getPortInfo()2575         public List<HdmiPortInfo> getPortInfo() {
2576             initBinderCall();
2577             return HdmiControlService.this.getPortInfo() == null
2578                 ? Collections.<HdmiPortInfo>emptyList()
2579                 : HdmiControlService.this.getPortInfo();
2580         }
2581 
2582         @Override
canChangeSystemAudioMode()2583         public boolean canChangeSystemAudioMode() {
2584             initBinderCall();
2585             HdmiCecLocalDeviceTv tv = tv();
2586             if (tv == null) {
2587                 return false;
2588             }
2589             return tv.hasSystemAudioDevice();
2590         }
2591 
2592         @Override
getSystemAudioMode()2593         public boolean getSystemAudioMode() {
2594             // TODO(shubang): handle getSystemAudioMode() for all device types
2595             initBinderCall();
2596             HdmiCecLocalDeviceTv tv = tv();
2597             HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
2598             return (tv != null && tv.isSystemAudioActivated())
2599                     || (audioSystem != null && audioSystem.isSystemAudioActivated());
2600         }
2601 
2602         @Override
getPhysicalAddress()2603         public int getPhysicalAddress() {
2604             initBinderCall();
2605             synchronized (mLock) {
2606                 return mHdmiCecNetwork.getPhysicalAddress();
2607             }
2608         }
2609 
2610         @Override
setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback)2611         public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
2612             initBinderCall();
2613             runOnServiceThread(new Runnable() {
2614                 @Override
2615                 public void run() {
2616                     HdmiCecLocalDeviceTv tv = tv();
2617                     if (tv == null) {
2618                         Slog.w(TAG, "Local tv device not available");
2619                         invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
2620                         return;
2621                     }
2622                     tv.changeSystemAudioMode(enabled, callback);
2623                 }
2624             });
2625         }
2626 
2627         @Override
addSystemAudioModeChangeListener( final IHdmiSystemAudioModeChangeListener listener)2628         public void addSystemAudioModeChangeListener(
2629                 final IHdmiSystemAudioModeChangeListener listener) {
2630             initBinderCall();
2631             HdmiControlService.this.addSystemAudioModeChangeListner(listener);
2632         }
2633 
2634         @Override
removeSystemAudioModeChangeListener( final IHdmiSystemAudioModeChangeListener listener)2635         public void removeSystemAudioModeChangeListener(
2636                 final IHdmiSystemAudioModeChangeListener listener) {
2637             initBinderCall();
2638             HdmiControlService.this.removeSystemAudioModeChangeListener(listener);
2639         }
2640 
2641         @Override
setInputChangeListener(final IHdmiInputChangeListener listener)2642         public void setInputChangeListener(final IHdmiInputChangeListener listener) {
2643             initBinderCall();
2644             HdmiControlService.this.setInputChangeListener(listener);
2645         }
2646 
2647         @Override
getInputDevices()2648         public List<HdmiDeviceInfo> getInputDevices() {
2649             initBinderCall();
2650             // No need to hold the lock for obtaining TV device as the local device instance
2651             // is preserved while the HDMI control is enabled.
2652             return HdmiUtils.mergeToUnmodifiableList(mHdmiCecNetwork.getSafeExternalInputsLocked(),
2653                     getMhlDevicesLocked());
2654         }
2655 
2656         // Returns all the CEC devices on the bus including system audio, switch,
2657         // even those of reserved type.
2658         @Override
getDeviceList()2659         public List<HdmiDeviceInfo> getDeviceList() {
2660             initBinderCall();
2661             return mHdmiCecNetwork.getSafeCecDevicesLocked();
2662         }
2663 
2664         @Override
powerOffRemoteDevice(int logicalAddress, int powerStatus)2665         public void powerOffRemoteDevice(int logicalAddress, int powerStatus) {
2666             initBinderCall();
2667             runOnServiceThread(new Runnable() {
2668                 @Override
2669                 public void run() {
2670                     Slog.w(TAG, "Device "
2671                             + logicalAddress + " power status is " + powerStatus
2672                             + " before standby command sent out");
2673                     sendCecCommand(HdmiCecMessageBuilder.buildStandby(
2674                             getRemoteControlSourceAddress(), logicalAddress));
2675                 }
2676             });
2677         }
2678 
2679         @Override
powerOnRemoteDevice(int logicalAddress, int powerStatus)2680         public void powerOnRemoteDevice(int logicalAddress, int powerStatus) {
2681             initBinderCall();
2682             runOnServiceThread(new Runnable() {
2683                 @Override
2684                 public void run() {
2685                     Slog.i(TAG, "Device "
2686                             + logicalAddress + " power status is " + powerStatus
2687                             + " before power on command sent out");
2688                     if (getSwitchDevice() != null) {
2689                         getSwitchDevice().sendUserControlPressedAndReleased(
2690                                 logicalAddress, HdmiCecKeycode.CEC_KEYCODE_POWER_ON_FUNCTION);
2691                     } else {
2692                         Slog.e(TAG, "Can't get the correct local device to handle routing.");
2693                     }
2694                 }
2695             });
2696         }
2697 
2698         @Override
2699         // TODO(b/128427908): add a result callback
askRemoteDeviceToBecomeActiveSource(int physicalAddress)2700         public void askRemoteDeviceToBecomeActiveSource(int physicalAddress) {
2701             initBinderCall();
2702             runOnServiceThread(new Runnable() {
2703                 @Override
2704                 public void run() {
2705                     HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(
2706                             getRemoteControlSourceAddress(), physicalAddress);
2707                     if (pathToPortId(physicalAddress) != Constants.INVALID_PORT_ID) {
2708                         if (getSwitchDevice() != null) {
2709                             getSwitchDevice().handleSetStreamPath(setStreamPath);
2710                         } else {
2711                             Slog.e(TAG, "Can't get the correct local device to handle routing.");
2712                         }
2713                     }
2714                     sendCecCommand(setStreamPath);
2715                 }
2716             });
2717         }
2718 
2719         @Override
setSystemAudioVolume(final int oldIndex, final int newIndex, final int maxIndex)2720         public void setSystemAudioVolume(final int oldIndex, final int newIndex,
2721                 final int maxIndex) {
2722             initBinderCall();
2723             runOnServiceThread(new Runnable() {
2724                 @Override
2725                 public void run() {
2726                     HdmiCecLocalDeviceTv tv = tv();
2727                     if (tv == null) {
2728                         Slog.w(TAG, "Local tv device not available");
2729                         return;
2730                     }
2731                     tv.changeVolume(oldIndex, newIndex - oldIndex, maxIndex);
2732                 }
2733             });
2734         }
2735 
2736         @Override
setSystemAudioMute(final boolean mute)2737         public void setSystemAudioMute(final boolean mute) {
2738             initBinderCall();
2739             runOnServiceThread(new Runnable() {
2740                 @Override
2741                 public void run() {
2742                     HdmiCecLocalDeviceTv tv = tv();
2743                     if (tv == null) {
2744                         Slog.w(TAG, "Local tv device not available");
2745                         return;
2746                     }
2747                     tv.changeMute(mute);
2748                 }
2749             });
2750         }
2751 
2752         @Override
setArcMode(final boolean enabled)2753         public void setArcMode(final boolean enabled) {
2754             initBinderCall();
2755             runOnServiceThread(new Runnable() {
2756                 @Override
2757                 public void run() {
2758                     HdmiCecLocalDeviceTv tv = tv();
2759                     if (tv == null) {
2760                         Slog.w(TAG, "Local tv device not available to change arc mode.");
2761                         return;
2762                     }
2763                     tv.startArcAction(enabled);
2764                 }
2765             });
2766         }
2767 
2768         @Override
setProhibitMode(final boolean enabled)2769         public void setProhibitMode(final boolean enabled) {
2770             initBinderCall();
2771             if (!isTvDevice()) {
2772                 return;
2773             }
2774             HdmiControlService.this.setProhibitMode(enabled);
2775         }
2776 
2777         @Override
addVendorCommandListener( final IHdmiVendorCommandListener listener, final int vendorId)2778         public void addVendorCommandListener(
2779                 final IHdmiVendorCommandListener listener, final int vendorId) {
2780             initBinderCall();
2781             HdmiControlService.this.addVendorCommandListener(listener, vendorId);
2782         }
2783 
2784         @Override
sendVendorCommand(final int deviceType, final int targetAddress, final byte[] params, final boolean hasVendorId)2785         public void sendVendorCommand(final int deviceType, final int targetAddress,
2786                 final byte[] params, final boolean hasVendorId) {
2787             initBinderCall();
2788             runOnServiceThread(new Runnable() {
2789                 @Override
2790                 public void run() {
2791                     HdmiCecLocalDevice device = mHdmiCecNetwork.getLocalDevice(deviceType);
2792                     if (device == null) {
2793                         Slog.w(TAG, "Local device not available");
2794                         return;
2795                     }
2796                     if (hasVendorId) {
2797                         sendCecCommand(HdmiCecMessageBuilder.buildVendorCommandWithId(
2798                                 device.getDeviceInfo().getLogicalAddress(), targetAddress,
2799                                 getVendorId(), params));
2800                     } else {
2801                         sendCecCommand(HdmiCecMessageBuilder.buildVendorCommand(
2802                                 device.getDeviceInfo().getLogicalAddress(), targetAddress, params));
2803                     }
2804                 }
2805             });
2806         }
2807 
2808         @Override
sendStandby(final int deviceType, final int deviceId)2809         public void sendStandby(final int deviceType, final int deviceId) {
2810             initBinderCall();
2811             runOnServiceThread(new Runnable() {
2812                 @Override
2813                 public void run() {
2814                     HdmiMhlLocalDeviceStub mhlDevice = mMhlController.getLocalDeviceById(deviceId);
2815                     if (mhlDevice != null) {
2816                         mhlDevice.sendStandby();
2817                         return;
2818                     }
2819                     HdmiCecLocalDevice device = mHdmiCecNetwork.getLocalDevice(deviceType);
2820                     if (device == null) {
2821                         device = audioSystem();
2822                     }
2823                     if (device == null) {
2824                         Slog.w(TAG, "Local device not available");
2825                         return;
2826                     }
2827                     device.sendStandby(deviceId);
2828                 }
2829             });
2830         }
2831 
2832         @Override
setHdmiRecordListener(IHdmiRecordListener listener)2833         public void setHdmiRecordListener(IHdmiRecordListener listener) {
2834             initBinderCall();
2835             HdmiControlService.this.setHdmiRecordListener(listener);
2836         }
2837 
2838         @Override
startOneTouchRecord(final int recorderAddress, final byte[] recordSource)2839         public void startOneTouchRecord(final int recorderAddress, final byte[] recordSource) {
2840             initBinderCall();
2841             runOnServiceThread(new Runnable() {
2842                 @Override
2843                 public void run() {
2844                     if (!isTvDeviceEnabled()) {
2845                         Slog.w(TAG, "TV device is not enabled.");
2846                         return;
2847                     }
2848                     tv().startOneTouchRecord(recorderAddress, recordSource);
2849                 }
2850             });
2851         }
2852 
2853         @Override
stopOneTouchRecord(final int recorderAddress)2854         public void stopOneTouchRecord(final int recorderAddress) {
2855             initBinderCall();
2856             runOnServiceThread(new Runnable() {
2857                 @Override
2858                 public void run() {
2859                     if (!isTvDeviceEnabled()) {
2860                         Slog.w(TAG, "TV device is not enabled.");
2861                         return;
2862                     }
2863                     tv().stopOneTouchRecord(recorderAddress);
2864                 }
2865             });
2866         }
2867 
2868         @Override
startTimerRecording(final int recorderAddress, final int sourceType, final byte[] recordSource)2869         public void startTimerRecording(final int recorderAddress, final int sourceType,
2870                 final byte[] recordSource) {
2871             initBinderCall();
2872             runOnServiceThread(new Runnable() {
2873                 @Override
2874                 public void run() {
2875                     if (!isTvDeviceEnabled()) {
2876                         Slog.w(TAG, "TV device is not enabled.");
2877                         return;
2878                     }
2879                     tv().startTimerRecording(recorderAddress, sourceType, recordSource);
2880                 }
2881             });
2882         }
2883 
2884         @Override
clearTimerRecording(final int recorderAddress, final int sourceType, final byte[] recordSource)2885         public void clearTimerRecording(final int recorderAddress, final int sourceType,
2886                 final byte[] recordSource) {
2887             initBinderCall();
2888             runOnServiceThread(new Runnable() {
2889                 @Override
2890                 public void run() {
2891                     if (!isTvDeviceEnabled()) {
2892                         Slog.w(TAG, "TV device is not enabled.");
2893                         return;
2894                     }
2895                     tv().clearTimerRecording(recorderAddress, sourceType, recordSource);
2896                 }
2897             });
2898         }
2899 
2900         @Override
sendMhlVendorCommand(final int portId, final int offset, final int length, final byte[] data)2901         public void sendMhlVendorCommand(final int portId, final int offset, final int length,
2902                 final byte[] data) {
2903             initBinderCall();
2904             runOnServiceThread(new Runnable() {
2905                 @Override
2906                 public void run() {
2907                     if (!isCecControlEnabled()) {
2908                         Slog.w(TAG, "Hdmi control is disabled.");
2909                         return ;
2910                     }
2911                     HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
2912                     if (device == null) {
2913                         Slog.w(TAG, "Invalid port id:" + portId);
2914                         return;
2915                     }
2916                     mMhlController.sendVendorCommand(portId, offset, length, data);
2917                 }
2918             });
2919         }
2920 
2921         @Override
addHdmiMhlVendorCommandListener( IHdmiMhlVendorCommandListener listener)2922         public void addHdmiMhlVendorCommandListener(
2923                 IHdmiMhlVendorCommandListener listener) {
2924             initBinderCall();
2925             HdmiControlService.this.addHdmiMhlVendorCommandListener(listener);
2926         }
2927 
2928         @Override
setStandbyMode(final boolean isStandbyModeOn)2929         public void setStandbyMode(final boolean isStandbyModeOn) {
2930             initBinderCall();
2931             runOnServiceThread(new Runnable() {
2932                 @Override
2933                 public void run() {
2934                     HdmiControlService.this.setStandbyMode(isStandbyModeOn);
2935                 }
2936             });
2937         }
2938 
2939         @Override
reportAudioStatus(final int deviceType, final int volume, final int maxVolume, final boolean isMute)2940         public void reportAudioStatus(final int deviceType, final int volume, final int maxVolume,
2941                 final boolean isMute) {
2942             initBinderCall();
2943             runOnServiceThread(new Runnable() {
2944                 @Override
2945                 public void run() {
2946                     HdmiCecLocalDevice device = mHdmiCecNetwork.getLocalDevice(deviceType);
2947                     if (device == null) {
2948                         Slog.w(TAG, "Local device not available");
2949                         return;
2950                     }
2951                     if (audioSystem() == null) {
2952                         Slog.w(TAG, "audio system is not available");
2953                         return;
2954                     }
2955                     if (!audioSystem().isSystemAudioActivated()) {
2956                         Slog.w(TAG, "audio system is not in system audio mode");
2957                         return;
2958                     }
2959                     audioSystem().reportAudioStatus(Constants.ADDR_TV);
2960                 }
2961             });
2962         }
2963 
2964         @Override
setSystemAudioModeOnForAudioOnlySource()2965         public void setSystemAudioModeOnForAudioOnlySource() {
2966             initBinderCall();
2967             runOnServiceThread(new Runnable() {
2968                 @Override
2969                 public void run() {
2970                     if (!isAudioSystemDevice()) {
2971                         Slog.e(TAG, "Not an audio system device. Won't set system audio mode on");
2972                         return;
2973                     }
2974                     if (audioSystem() == null) {
2975                         Slog.e(TAG, "Audio System local device is not registered");
2976                         return;
2977                     }
2978                     if (!audioSystem().checkSupportAndSetSystemAudioMode(true)) {
2979                         Slog.e(TAG, "System Audio Mode is not supported.");
2980                         return;
2981                     }
2982                     sendCecCommand(
2983                             HdmiCecMessageBuilder.buildSetSystemAudioMode(
2984                                     audioSystem().getDeviceInfo().getLogicalAddress(),
2985                                     Constants.ADDR_BROADCAST,
2986                                     true));
2987                 }
2988             });
2989         }
2990 
2991         @Override
onShellCommand(@ullable FileDescriptor in, @Nullable FileDescriptor out, @Nullable FileDescriptor err, String[] args, @Nullable ShellCallback callback, ResultReceiver resultReceiver)2992         public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
2993                 @Nullable FileDescriptor err, String[] args,
2994                 @Nullable ShellCallback callback, ResultReceiver resultReceiver)
2995                 throws RemoteException {
2996             initBinderCall();
2997             new HdmiControlShellCommand(this)
2998                     .exec(this, in, out, err, args, callback, resultReceiver);
2999         }
3000 
3001         @Override
dump(FileDescriptor fd, final PrintWriter writer, String[] args)3002         protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
3003             if (!DumpUtils.checkDumpPermission(getContext(), TAG, writer)) return;
3004             final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
3005 
3006             synchronized (mLock) {
3007                 pw.println("mProhibitMode: " + mProhibitMode);
3008             }
3009             pw.println("mPowerStatus: " + mPowerStatusController.getPowerStatus());
3010             pw.println("mIsCecAvailable: " + mIsCecAvailable);
3011             pw.println("mCecVersion: " + mCecVersion);
3012             pw.println("mIsAbsoluteVolumeBehaviorEnabled: " + isAbsoluteVolumeBehaviorEnabled());
3013 
3014             // System settings
3015             pw.println("System_settings:");
3016             pw.increaseIndent();
3017             pw.println("mMhlInputChangeEnabled: " + isMhlInputChangeEnabled());
3018             pw.println("mSystemAudioActivated: " + isSystemAudioActivated());
3019             pw.println("mHdmiCecVolumeControlEnabled: " + getHdmiCecVolumeControl());
3020             pw.decreaseIndent();
3021 
3022             // CEC settings
3023             pw.println("CEC settings:");
3024             pw.increaseIndent();
3025             HdmiCecConfig hdmiCecConfig = HdmiControlService.this.getHdmiCecConfig();
3026             List<String> allSettings = hdmiCecConfig.getAllSettings();
3027             Set<String> userSettings = new HashSet<>(hdmiCecConfig.getUserSettings());
3028             for (String setting : allSettings) {
3029                 if (hdmiCecConfig.isStringValueType(setting)) {
3030                     pw.println(setting + " (string): " + hdmiCecConfig.getStringValue(setting)
3031                             + " (default: " + hdmiCecConfig.getDefaultStringValue(setting) + ")"
3032                             + (userSettings.contains(setting) ? " [modifiable]" : ""));
3033                 } else if (hdmiCecConfig.isIntValueType(setting)) {
3034                     pw.println(setting + " (int): " + hdmiCecConfig.getIntValue(setting)
3035                             + " (default: " + hdmiCecConfig.getDefaultIntValue(setting) + ")"
3036                             + (userSettings.contains(setting) ? " [modifiable]" : ""));
3037                 }
3038             }
3039             pw.decreaseIndent();
3040 
3041             pw.println("mMhlController: ");
3042             pw.increaseIndent();
3043             mMhlController.dump(pw);
3044             pw.decreaseIndent();
3045             pw.print("eARC local device: ");
3046             pw.increaseIndent();
3047             if (mEarcLocalDevice == null) {
3048                 pw.println("None. eARC is either disabled or not available.");
3049             } else {
3050                 mEarcLocalDevice.dump(pw);
3051             }
3052             pw.decreaseIndent();
3053             mHdmiCecNetwork.dump(pw);
3054             if (mCecController != null) {
3055                 pw.println("mCecController: ");
3056                 pw.increaseIndent();
3057                 mCecController.dump(pw);
3058                 pw.decreaseIndent();
3059             }
3060         }
3061 
3062         @Override
setMessageHistorySize(int newSize)3063         public boolean setMessageHistorySize(int newSize) {
3064             enforceAccessPermission();
3065             if (mCecController == null) {
3066                 return false;
3067             }
3068             return mCecController.setMessageHistorySize(newSize);
3069         }
3070 
3071         @Override
getMessageHistorySize()3072         public int getMessageHistorySize() {
3073             enforceAccessPermission();
3074             if (mCecController != null) {
3075                 return mCecController.getMessageHistorySize();
3076             } else {
3077                 return 0;
3078             }
3079         }
3080 
3081         @Override
addCecSettingChangeListener(String name, final IHdmiCecSettingChangeListener listener)3082         public void addCecSettingChangeListener(String name,
3083                 final IHdmiCecSettingChangeListener listener) {
3084             enforceAccessPermission();
3085             HdmiControlService.this.addCecSettingChangeListener(name, listener);
3086         }
3087 
3088         @Override
removeCecSettingChangeListener(String name, final IHdmiCecSettingChangeListener listener)3089         public void removeCecSettingChangeListener(String name,
3090                 final IHdmiCecSettingChangeListener listener) {
3091             enforceAccessPermission();
3092             HdmiControlService.this.removeCecSettingChangeListener(name, listener);
3093         }
3094 
3095         @Override
getUserCecSettings()3096         public List<String> getUserCecSettings() {
3097             initBinderCall();
3098             final long token = Binder.clearCallingIdentity();
3099             try {
3100                 return HdmiControlService.this.getHdmiCecConfig().getUserSettings();
3101             } finally {
3102                 Binder.restoreCallingIdentity(token);
3103             }
3104         }
3105 
3106         @Override
getAllowedCecSettingStringValues(String name)3107         public List<String> getAllowedCecSettingStringValues(String name) {
3108             initBinderCall();
3109             final long token = Binder.clearCallingIdentity();
3110             try {
3111                 return HdmiControlService.this.getHdmiCecConfig().getAllowedStringValues(name);
3112             } finally {
3113                 Binder.restoreCallingIdentity(token);
3114             }
3115         }
3116 
3117         @Override
getAllowedCecSettingIntValues(String name)3118         public int[] getAllowedCecSettingIntValues(String name) {
3119             initBinderCall();
3120             final long token = Binder.clearCallingIdentity();
3121             try {
3122                 List<Integer> allowedValues =
3123                         HdmiControlService.this.getHdmiCecConfig().getAllowedIntValues(name);
3124                 return allowedValues.stream().mapToInt(i->i).toArray();
3125             } finally {
3126                 Binder.restoreCallingIdentity(token);
3127             }
3128         }
3129 
3130         @Override
getCecSettingStringValue(String name)3131         public String getCecSettingStringValue(String name) {
3132             initBinderCall();
3133             final long token = Binder.clearCallingIdentity();
3134             try {
3135                 return HdmiControlService.this.getHdmiCecConfig().getStringValue(name);
3136             } finally {
3137                 Binder.restoreCallingIdentity(token);
3138             }
3139         }
3140 
3141         @Override
setCecSettingStringValue(String name, String value)3142         public void setCecSettingStringValue(String name, String value) {
3143             initBinderCall();
3144             final long token = Binder.clearCallingIdentity();
3145             try {
3146                 HdmiControlService.this.getHdmiCecConfig().setStringValue(name, value);
3147             } finally {
3148                 Binder.restoreCallingIdentity(token);
3149             }
3150         }
3151 
3152         @Override
getCecSettingIntValue(String name)3153         public int getCecSettingIntValue(String name) {
3154             initBinderCall();
3155             final long token = Binder.clearCallingIdentity();
3156             try {
3157                 return HdmiControlService.this.getHdmiCecConfig().getIntValue(name);
3158             } finally {
3159                 Binder.restoreCallingIdentity(token);
3160             }
3161         }
3162 
3163         @Override
setCecSettingIntValue(String name, int value)3164         public void setCecSettingIntValue(String name, int value) {
3165             initBinderCall();
3166             final long token = Binder.clearCallingIdentity();
3167             try {
3168                 HdmiControlService.this.getHdmiCecConfig().setIntValue(name, value);
3169             } finally {
3170                 Binder.restoreCallingIdentity(token);
3171             }
3172         }
3173     }
3174 
3175     @VisibleForTesting
setHdmiCecVolumeControlEnabledInternal( @dmiControlManager.VolumeControl int hdmiCecVolumeControl)3176     void setHdmiCecVolumeControlEnabledInternal(
3177             @HdmiControlManager.VolumeControl int hdmiCecVolumeControl) {
3178         mHdmiCecVolumeControl = hdmiCecVolumeControl;
3179         announceHdmiCecVolumeControlFeatureChange(hdmiCecVolumeControl);
3180         runOnServiceThread(this::checkAndUpdateAbsoluteVolumeBehavior);
3181     }
3182 
3183     // Get the source address to send out commands to devices connected to the current device
3184     // when other services interact with HdmiControlService.
getRemoteControlSourceAddress()3185     private int getRemoteControlSourceAddress() {
3186         if (isAudioSystemDevice()) {
3187             return audioSystem().getDeviceInfo().getLogicalAddress();
3188         } else if (isPlaybackDevice()) {
3189             return playback().getDeviceInfo().getLogicalAddress();
3190         }
3191         return ADDR_UNREGISTERED;
3192     }
3193 
3194     // Get the switch device to do CEC routing control
3195     @Nullable
getSwitchDevice()3196     private HdmiCecLocalDeviceSource getSwitchDevice() {
3197         if (isAudioSystemDevice()) {
3198             return audioSystem();
3199         }
3200         if (isPlaybackDevice()) {
3201             return playback();
3202         }
3203         return null;
3204     }
3205 
3206     @ServiceThreadOnly
3207     @VisibleForTesting
oneTouchPlay(final IHdmiControlCallback callback)3208     protected void oneTouchPlay(final IHdmiControlCallback callback) {
3209         assertRunOnServiceThread();
3210         if (!mAddressAllocated) {
3211             mOtpCallbackPendingAddressAllocation = callback;
3212             Slog.d(TAG, "Local device is under address allocation. "
3213                         + "Save OTP callback for later process.");
3214             return;
3215         }
3216 
3217         HdmiCecLocalDeviceSource source = playback();
3218         if (source == null) {
3219             source = audioSystem();
3220         } else {
3221             // Cancel an existing timer to send the device to sleep since OTP was triggered.
3222             playback().mDelayedStandbyOnActiveSourceLostHandler
3223                     .removeCallbacksAndMessages(null);
3224             playback().setIsActiveSourceLostPopupLaunched(false);
3225         }
3226 
3227         if (source == null) {
3228             Slog.w(TAG, "Local source device not available");
3229             invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
3230             return;
3231         }
3232         source.oneTouchPlay(callback);
3233     }
3234 
3235     @ServiceThreadOnly
3236     @VisibleForTesting
toggleAndFollowTvPower()3237     protected void toggleAndFollowTvPower() {
3238         assertRunOnServiceThread();
3239         HdmiCecLocalDeviceSource source = playback();
3240         if (source == null) {
3241             source = audioSystem();
3242         }
3243 
3244         if (source == null) {
3245             Slog.w(TAG, "Local source device not available");
3246             return;
3247         }
3248         source.toggleAndFollowTvPower();
3249     }
3250 
3251     @VisibleForTesting
shouldHandleTvPowerKey()3252     protected boolean shouldHandleTvPowerKey() {
3253         if (isTvDevice()) {
3254             return false;
3255         }
3256         String powerControlMode = getHdmiCecConfig().getStringValue(
3257                 HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
3258         if (powerControlMode.equals(POWER_CONTROL_MODE_NONE)) {
3259             return false;
3260         }
3261         int hdmiCecEnabled = getHdmiCecConfig().getIntValue(
3262                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED);
3263         if (hdmiCecEnabled != HdmiControlManager.HDMI_CEC_CONTROL_ENABLED) {
3264             return false;
3265         }
3266         return mIsCecAvailable;
3267     }
3268 
3269     /**
3270      * Queries the display status of the TV and calls {@code callback} upon completion.
3271      *
3272      * If this is a non-source device, or if the query fails for any reason, the callback will
3273      * be called with {@link HdmiControlManager.POWER_STATUS_UNKNOWN}.
3274      *
3275      * If the query succeeds, the callback will be called with one of the other power status
3276      * constants.
3277      */
3278     @ServiceThreadOnly
queryDisplayStatus(final IHdmiControlCallback callback)3279     protected void queryDisplayStatus(final IHdmiControlCallback callback) {
3280         assertRunOnServiceThread();
3281         if (!mAddressAllocated) {
3282             mDisplayStatusCallback = callback;
3283             Slog.d(TAG, "Local device is under address allocation. "
3284                         + "Queue display callback for later process.");
3285             return;
3286         }
3287 
3288         HdmiCecLocalDeviceSource source = playback();
3289         if (source == null) {
3290             source = audioSystem();
3291         }
3292 
3293         if (source == null) {
3294             Slog.w(TAG, "Local source device not available");
3295             invokeCallback(callback, HdmiControlManager.POWER_STATUS_UNKNOWN);
3296             return;
3297         }
3298         source.queryDisplayStatus(callback);
3299     }
3300 
getActiveSource()3301     protected HdmiDeviceInfo getActiveSource() {
3302         // If a the device is a playback device that is the current active source, return the
3303         // local device info
3304         if (playback() != null && playback().isActiveSource()) {
3305             return playback().getDeviceInfo();
3306         }
3307 
3308         // Otherwise get the active source and look for it from the device list
3309         ActiveSource activeSource = getLocalActiveSource();
3310 
3311         if (activeSource.isValid()) {
3312             HdmiDeviceInfo activeSourceInfo = mHdmiCecNetwork.getSafeCecDeviceInfo(
3313                     activeSource.logicalAddress);
3314             if (activeSourceInfo != null) {
3315                 return activeSourceInfo;
3316             }
3317 
3318             return HdmiDeviceInfo.hardwarePort(activeSource.physicalAddress,
3319                     pathToPortId(activeSource.physicalAddress));
3320         }
3321 
3322         if (tv() != null) {
3323             int activePath = tv().getActivePath();
3324             if (activePath != HdmiDeviceInfo.PATH_INVALID) {
3325                 HdmiDeviceInfo info = mHdmiCecNetwork.getSafeDeviceInfoByPath(activePath);
3326                 return (info != null) ? info : HdmiDeviceInfo.hardwarePort(activePath,
3327                         tv().getActivePortId());
3328             }
3329         }
3330 
3331         return null;
3332     }
3333 
3334     @VisibleForTesting
addHdmiControlStatusChangeListener( final IHdmiControlStatusChangeListener listener)3335     void addHdmiControlStatusChangeListener(
3336             final IHdmiControlStatusChangeListener listener) {
3337         final HdmiControlStatusChangeListenerRecord record =
3338                 new HdmiControlStatusChangeListenerRecord(listener);
3339         try {
3340             listener.asBinder().linkToDeath(record, 0);
3341         } catch (RemoteException e) {
3342             Slog.w(TAG, "Listener already died");
3343             return;
3344         }
3345         synchronized (mLock) {
3346             mHdmiControlStatusChangeListenerRecords.add(record);
3347         }
3348 
3349         // Inform the listener of the initial state of each HDMI port by generating
3350         // hotplug events.
3351         runOnServiceThread(new Runnable() {
3352             @Override
3353             public void run() {
3354                 synchronized (mLock) {
3355                     if (!mHdmiControlStatusChangeListenerRecords.contains(record)) return;
3356                 }
3357 
3358                 // Return the current status of mHdmiControlEnabled;
3359                 synchronized (mLock) {
3360                     invokeHdmiControlStatusChangeListenerLocked(listener, mHdmiControlEnabled);
3361                 }
3362             }
3363         });
3364     }
3365 
removeHdmiControlStatusChangeListener( final IHdmiControlStatusChangeListener listener)3366     private void removeHdmiControlStatusChangeListener(
3367             final IHdmiControlStatusChangeListener listener) {
3368         synchronized (mLock) {
3369             for (HdmiControlStatusChangeListenerRecord record :
3370                     mHdmiControlStatusChangeListenerRecords) {
3371                 if (record.mListener.asBinder() == listener.asBinder()) {
3372                     listener.asBinder().unlinkToDeath(record, 0);
3373                     mHdmiControlStatusChangeListenerRecords.remove(record);
3374                     break;
3375                 }
3376             }
3377         }
3378     }
3379 
3380     @VisibleForTesting
addHdmiCecVolumeControlFeatureListener( final IHdmiCecVolumeControlFeatureListener listener)3381     void addHdmiCecVolumeControlFeatureListener(
3382             final IHdmiCecVolumeControlFeatureListener listener) {
3383         mHdmiCecVolumeControlFeatureListenerRecords.register(listener);
3384 
3385         runOnServiceThread(new Runnable() {
3386             @Override
3387             public void run() {
3388                 // Return the current status of mHdmiCecVolumeControlEnabled;
3389                 synchronized (mLock) {
3390                     try {
3391                         listener.onHdmiCecVolumeControlFeature(mHdmiCecVolumeControl);
3392                     } catch (RemoteException e) {
3393                         Slog.e(TAG, "Failed to report HdmiControlVolumeControlStatusChange: "
3394                                 + mHdmiCecVolumeControl, e);
3395                     }
3396                 }
3397             }
3398         });
3399     }
3400 
3401     @VisibleForTesting
removeHdmiControlVolumeControlStatusChangeListener( final IHdmiCecVolumeControlFeatureListener listener)3402     void removeHdmiControlVolumeControlStatusChangeListener(
3403             final IHdmiCecVolumeControlFeatureListener listener) {
3404         mHdmiCecVolumeControlFeatureListenerRecords.unregister(listener);
3405     }
3406 
addHotplugEventListener(final IHdmiHotplugEventListener listener)3407     private void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
3408         final HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
3409         try {
3410             listener.asBinder().linkToDeath(record, 0);
3411         } catch (RemoteException e) {
3412             Slog.w(TAG, "Listener already died");
3413             return;
3414         }
3415         synchronized (mLock) {
3416             mHotplugEventListenerRecords.add(record);
3417         }
3418 
3419         // Inform the listener of the initial state of each HDMI port by generating
3420         // hotplug events.
3421         runOnServiceThread(new Runnable() {
3422             @Override
3423             public void run() {
3424                 synchronized (mLock) {
3425                     if (!mHotplugEventListenerRecords.contains(record)) return;
3426                 }
3427                 for (HdmiPortInfo port : getPortInfo()) {
3428                     HdmiHotplugEvent event = new HdmiHotplugEvent(port.getId(),
3429                             mCecController.isConnected(port.getId()));
3430                     synchronized (mLock) {
3431                         invokeHotplugEventListenerLocked(listener, event);
3432                     }
3433                 }
3434             }
3435         });
3436     }
3437 
removeHotplugEventListener(IHdmiHotplugEventListener listener)3438     private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
3439         synchronized (mLock) {
3440             for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
3441                 if (record.mListener.asBinder() == listener.asBinder()) {
3442                     listener.asBinder().unlinkToDeath(record, 0);
3443                     mHotplugEventListenerRecords.remove(record);
3444                     break;
3445                 }
3446             }
3447         }
3448     }
3449 
addDeviceEventListener(IHdmiDeviceEventListener listener)3450     private void addDeviceEventListener(IHdmiDeviceEventListener listener) {
3451         DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener);
3452         try {
3453             listener.asBinder().linkToDeath(record, 0);
3454         } catch (RemoteException e) {
3455             Slog.w(TAG, "Listener already died");
3456             return;
3457         }
3458         synchronized (mLock) {
3459             mDeviceEventListenerRecords.add(record);
3460         }
3461     }
3462 
invokeDeviceEventListeners(HdmiDeviceInfo device, int status)3463     void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
3464         synchronized (mLock) {
3465             for (DeviceEventListenerRecord record : mDeviceEventListenerRecords) {
3466                 try {
3467                     record.mListener.onStatusChanged(device, status);
3468                 } catch (RemoteException e) {
3469                     Slog.e(TAG, "Failed to report device event:" + e);
3470                 }
3471             }
3472         }
3473     }
3474 
addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener)3475     private void addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener) {
3476         SystemAudioModeChangeListenerRecord record = new SystemAudioModeChangeListenerRecord(
3477                 listener);
3478         try {
3479             listener.asBinder().linkToDeath(record, 0);
3480         } catch (RemoteException e) {
3481             Slog.w(TAG, "Listener already died");
3482             return;
3483         }
3484         synchronized (mLock) {
3485             mSystemAudioModeChangeListenerRecords.add(record);
3486         }
3487     }
3488 
removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener)3489     private void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) {
3490         synchronized (mLock) {
3491             for (SystemAudioModeChangeListenerRecord record :
3492                     mSystemAudioModeChangeListenerRecords) {
3493                 if (record.mListener.asBinder() == listener) {
3494                     listener.asBinder().unlinkToDeath(record, 0);
3495                     mSystemAudioModeChangeListenerRecords.remove(record);
3496                     break;
3497                 }
3498             }
3499         }
3500     }
3501 
3502     private final class InputChangeListenerRecord implements IBinder.DeathRecipient {
3503         private final IHdmiInputChangeListener mListener;
3504 
InputChangeListenerRecord(IHdmiInputChangeListener listener)3505         public InputChangeListenerRecord(IHdmiInputChangeListener listener) {
3506             mListener = listener;
3507         }
3508 
3509         @Override
binderDied()3510         public void binderDied() {
3511             synchronized (mLock) {
3512                 if (mInputChangeListenerRecord == this) {
3513                     mInputChangeListenerRecord = null;
3514                 }
3515             }
3516         }
3517     }
3518 
setInputChangeListener(IHdmiInputChangeListener listener)3519     private void setInputChangeListener(IHdmiInputChangeListener listener) {
3520         synchronized (mLock) {
3521             mInputChangeListenerRecord = new InputChangeListenerRecord(listener);
3522             try {
3523                 listener.asBinder().linkToDeath(mInputChangeListenerRecord, 0);
3524             } catch (RemoteException e) {
3525                 Slog.w(TAG, "Listener already died");
3526                 return;
3527             }
3528         }
3529     }
3530 
invokeInputChangeListener(HdmiDeviceInfo info)3531     void invokeInputChangeListener(HdmiDeviceInfo info) {
3532         synchronized (mLock) {
3533             if (mInputChangeListenerRecord != null) {
3534                 try {
3535                     mInputChangeListenerRecord.mListener.onChanged(info);
3536                 } catch (RemoteException e) {
3537                     Slog.w(TAG, "Exception thrown by IHdmiInputChangeListener: " + e);
3538                 }
3539             }
3540         }
3541     }
3542 
setHdmiRecordListener(IHdmiRecordListener listener)3543     private void setHdmiRecordListener(IHdmiRecordListener listener) {
3544         synchronized (mLock) {
3545             mRecordListenerRecord = new HdmiRecordListenerRecord(listener);
3546             try {
3547                 listener.asBinder().linkToDeath(mRecordListenerRecord, 0);
3548             } catch (RemoteException e) {
3549                 Slog.w(TAG, "Listener already died.", e);
3550             }
3551         }
3552     }
3553 
invokeRecordRequestListener(int recorderAddress)3554     byte[] invokeRecordRequestListener(int recorderAddress) {
3555         synchronized (mLock) {
3556             if (mRecordListenerRecord != null) {
3557                 try {
3558                     return mRecordListenerRecord.mListener.getOneTouchRecordSource(recorderAddress);
3559                 } catch (RemoteException e) {
3560                     Slog.w(TAG, "Failed to start record.", e);
3561                 }
3562             }
3563             return EmptyArray.BYTE;
3564         }
3565     }
3566 
invokeOneTouchRecordResult(int recorderAddress, int result)3567     void invokeOneTouchRecordResult(int recorderAddress, int result) {
3568         synchronized (mLock) {
3569             if (mRecordListenerRecord != null) {
3570                 try {
3571                     mRecordListenerRecord.mListener.onOneTouchRecordResult(recorderAddress, result);
3572                 } catch (RemoteException e) {
3573                     Slog.w(TAG, "Failed to call onOneTouchRecordResult.", e);
3574                 }
3575             }
3576         }
3577     }
3578 
invokeTimerRecordingResult(int recorderAddress, int result)3579     void invokeTimerRecordingResult(int recorderAddress, int result) {
3580         synchronized (mLock) {
3581             if (mRecordListenerRecord != null) {
3582                 try {
3583                     mRecordListenerRecord.mListener.onTimerRecordingResult(recorderAddress, result);
3584                 } catch (RemoteException e) {
3585                     Slog.w(TAG, "Failed to call onTimerRecordingResult.", e);
3586                 }
3587             }
3588         }
3589     }
3590 
invokeClearTimerRecordingResult(int recorderAddress, int result)3591     void invokeClearTimerRecordingResult(int recorderAddress, int result) {
3592         synchronized (mLock) {
3593             if (mRecordListenerRecord != null) {
3594                 try {
3595                     mRecordListenerRecord.mListener.onClearTimerRecordingResult(recorderAddress,
3596                             result);
3597                 } catch (RemoteException e) {
3598                     Slog.w(TAG, "Failed to call onClearTimerRecordingResult.", e);
3599                 }
3600             }
3601         }
3602     }
3603 
invokeCallback(IHdmiControlCallback callback, int result)3604     private void invokeCallback(IHdmiControlCallback callback, int result) {
3605         if (callback == null) {
3606             return;
3607         }
3608         try {
3609             callback.onComplete(result);
3610         } catch (RemoteException e) {
3611             Slog.e(TAG, "Invoking callback failed:" + e);
3612         }
3613     }
3614 
invokeSystemAudioModeChangeLocked(IHdmiSystemAudioModeChangeListener listener, boolean enabled)3615     private void invokeSystemAudioModeChangeLocked(IHdmiSystemAudioModeChangeListener listener,
3616             boolean enabled) {
3617         try {
3618             listener.onStatusChanged(enabled);
3619         } catch (RemoteException e) {
3620             Slog.e(TAG, "Invoking callback failed:" + e);
3621         }
3622     }
3623 
announceHotplugEvent(int portId, boolean connected)3624     private void announceHotplugEvent(int portId, boolean connected) {
3625         HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected);
3626         synchronized (mLock) {
3627             for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
3628                 invokeHotplugEventListenerLocked(record.mListener, event);
3629             }
3630         }
3631     }
3632 
invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener, HdmiHotplugEvent event)3633     private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener,
3634             HdmiHotplugEvent event) {
3635         try {
3636             listener.onReceived(event);
3637         } catch (RemoteException e) {
3638             Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e);
3639         }
3640     }
3641 
announceHdmiControlStatusChange(@dmiControlManager.HdmiCecControl int isEnabled)3642     private void announceHdmiControlStatusChange(@HdmiControlManager.HdmiCecControl int isEnabled) {
3643         assertRunOnServiceThread();
3644         synchronized (mLock) {
3645             List<IHdmiControlStatusChangeListener> listeners = new ArrayList<>(
3646                     mHdmiControlStatusChangeListenerRecords.size());
3647             for (HdmiControlStatusChangeListenerRecord record :
3648                     mHdmiControlStatusChangeListenerRecords) {
3649                 listeners.add(record.mListener);
3650             }
3651             invokeHdmiControlStatusChangeListenerLocked(listeners, isEnabled);
3652         }
3653     }
3654 
invokeHdmiControlStatusChangeListenerLocked( IHdmiControlStatusChangeListener listener, @HdmiControlManager.HdmiCecControl int isEnabled)3655     private void invokeHdmiControlStatusChangeListenerLocked(
3656             IHdmiControlStatusChangeListener listener,
3657             @HdmiControlManager.HdmiCecControl int isEnabled) {
3658         invokeHdmiControlStatusChangeListenerLocked(Collections.singletonList(listener), isEnabled);
3659     }
3660 
invokeHdmiControlStatusChangeListenerLocked( Collection<IHdmiControlStatusChangeListener> listeners, @HdmiControlManager.HdmiCecControl int isEnabled)3661     private void invokeHdmiControlStatusChangeListenerLocked(
3662             Collection<IHdmiControlStatusChangeListener> listeners,
3663             @HdmiControlManager.HdmiCecControl int isEnabled) {
3664         if (isEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED) {
3665             queryDisplayStatus(new IHdmiControlCallback.Stub() {
3666                 public void onComplete(int status) {
3667                     mIsCecAvailable = status != HdmiControlManager.POWER_STATUS_UNKNOWN;
3668                     if (!listeners.isEmpty()) {
3669                         invokeHdmiControlStatusChangeListenerLocked(listeners,
3670                                 isEnabled, mIsCecAvailable);
3671                     }
3672                 }
3673             });
3674         } else {
3675             mIsCecAvailable = false;
3676             if (!listeners.isEmpty()) {
3677                 invokeHdmiControlStatusChangeListenerLocked(listeners, isEnabled, mIsCecAvailable);
3678             }
3679         }
3680     }
3681 
invokeHdmiControlStatusChangeListenerLocked( Collection<IHdmiControlStatusChangeListener> listeners, @HdmiControlManager.HdmiCecControl int isEnabled, boolean isCecAvailable)3682     private void invokeHdmiControlStatusChangeListenerLocked(
3683             Collection<IHdmiControlStatusChangeListener> listeners,
3684             @HdmiControlManager.HdmiCecControl int isEnabled,
3685             boolean isCecAvailable) {
3686         for (IHdmiControlStatusChangeListener listener : listeners) {
3687             try {
3688                 listener.onStatusChange(isEnabled, isCecAvailable);
3689             } catch (RemoteException e) {
3690                 Slog.e(TAG,
3691                         "Failed to report HdmiControlStatusChange: " + isEnabled + " isAvailable: "
3692                                 + isCecAvailable, e);
3693             }
3694         }
3695     }
3696 
announceHdmiCecVolumeControlFeatureChange( @dmiControlManager.VolumeControl int hdmiCecVolumeControl)3697     private void announceHdmiCecVolumeControlFeatureChange(
3698             @HdmiControlManager.VolumeControl int hdmiCecVolumeControl) {
3699         assertRunOnServiceThread();
3700         synchronized (mLock) {
3701             mHdmiCecVolumeControlFeatureListenerRecords.broadcast(listener -> {
3702                 try {
3703                     listener.onHdmiCecVolumeControlFeature(hdmiCecVolumeControl);
3704                 } catch (RemoteException e) {
3705                     Slog.e(TAG,
3706                             "Failed to report HdmiControlVolumeControlStatusChange: "
3707                                     + hdmiCecVolumeControl);
3708                 }
3709             });
3710         }
3711     }
3712 
tv()3713     public HdmiCecLocalDeviceTv tv() {
3714         return (HdmiCecLocalDeviceTv) mHdmiCecNetwork.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
3715     }
3716 
isTvDevice()3717     boolean isTvDevice() {
3718         return mCecLocalDevices.contains(HdmiDeviceInfo.DEVICE_TV);
3719     }
3720 
isAudioSystemDevice()3721     boolean isAudioSystemDevice() {
3722         return mCecLocalDevices.contains(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
3723     }
3724 
isPlaybackDevice()3725     boolean isPlaybackDevice() {
3726         return mCecLocalDevices.contains(HdmiDeviceInfo.DEVICE_PLAYBACK);
3727     }
3728 
isSwitchDevice()3729     boolean isSwitchDevice() {
3730         return HdmiProperties.is_switch().orElse(false);
3731     }
3732 
isTvDeviceEnabled()3733     boolean isTvDeviceEnabled() {
3734         return isTvDevice() && tv() != null;
3735     }
3736 
playback()3737     protected HdmiCecLocalDevicePlayback playback() {
3738         return (HdmiCecLocalDevicePlayback)
3739                 mHdmiCecNetwork.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK);
3740     }
3741 
audioSystem()3742     public HdmiCecLocalDeviceAudioSystem audioSystem() {
3743         return (HdmiCecLocalDeviceAudioSystem) mHdmiCecNetwork.getLocalDevice(
3744                 HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
3745     }
3746 
3747     /**
3748      * Returns null before the boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}.
3749      */
3750     @Nullable
getAudioManager()3751     AudioManagerWrapper getAudioManager() {
3752         return mAudioManager;
3753     }
3754 
3755     /**
3756      * Returns null before the boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}.
3757      */
3758     @Nullable
getAudioDeviceVolumeManager()3759     private AudioDeviceVolumeManagerWrapper getAudioDeviceVolumeManager() {
3760         return mAudioDeviceVolumeManager;
3761     }
3762 
isCecControlEnabled()3763     boolean isCecControlEnabled() {
3764         synchronized (mLock) {
3765             return mHdmiControlEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED;
3766         }
3767     }
3768 
isEarcEnabled()3769     public boolean isEarcEnabled() {
3770         synchronized (mLock) {
3771             return mEarcEnabled;
3772         }
3773     }
3774 
3775     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
isEarcSupported()3776     protected boolean isEarcSupported() {
3777         synchronized (mLock) {
3778             return mEarcSupported;
3779         }
3780     }
3781 
isDsmEnabled()3782     private boolean isDsmEnabled() {
3783         return mHdmiCecConfig.getIntValue(HdmiControlManager.CEC_SETTING_NAME_SOUNDBAR_MODE)
3784                 == SOUNDBAR_MODE_ENABLED;
3785     }
3786 
3787     @VisibleForTesting
isArcSupported()3788     protected boolean isArcSupported() {
3789         return SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true);
3790     }
3791 
3792     @ServiceThreadOnly
getPowerStatus()3793     int getPowerStatus() {
3794         assertRunOnServiceThread();
3795         return mPowerStatusController.getPowerStatus();
3796     }
3797 
3798     @ServiceThreadOnly
3799     @VisibleForTesting
setPowerStatus(int powerStatus)3800     void setPowerStatus(int powerStatus) {
3801         assertRunOnServiceThread();
3802         mPowerStatusController.setPowerStatus(powerStatus);
3803     }
3804 
3805     @ServiceThreadOnly
isPowerOnOrTransient()3806     boolean isPowerOnOrTransient() {
3807         assertRunOnServiceThread();
3808         return mPowerStatusController.isPowerStatusOn()
3809                 || mPowerStatusController.isPowerStatusTransientToOn();
3810     }
3811 
3812     @ServiceThreadOnly
isPowerStandbyOrTransient()3813     boolean isPowerStandbyOrTransient() {
3814         assertRunOnServiceThread();
3815         return mPowerStatusController.isPowerStatusStandby()
3816                 || mPowerStatusController.isPowerStatusTransientToStandby();
3817     }
3818 
3819     @ServiceThreadOnly
isPowerStandby()3820     boolean isPowerStandby() {
3821         assertRunOnServiceThread();
3822         return mPowerStatusController.isPowerStatusStandby();
3823     }
3824 
3825     @ServiceThreadOnly
wakeUp()3826     void wakeUp() {
3827         assertRunOnServiceThread();
3828         mWakeUpMessageReceived = true;
3829         mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_HDMI,
3830                 "android.server.hdmi:WAKE");
3831         // PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets
3832         // the intent, the sequence will continue at onWakeUp().
3833     }
3834 
3835     @ServiceThreadOnly
standby()3836     void standby() {
3837         assertRunOnServiceThread();
3838         if (!canGoToStandby()) {
3839             return;
3840         }
3841         mStandbyMessageReceived = true;
3842         mPowerManager.goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_HDMI, 0);
3843         // PowerManger will send the broadcast Intent.ACTION_SCREEN_OFF and after this gets
3844         // the intent, the sequence will continue at onStandby().
3845     }
3846 
isWakeUpMessageReceived()3847     boolean isWakeUpMessageReceived() {
3848         return mWakeUpMessageReceived;
3849     }
3850 
isStandbyMessageReceived()3851     protected boolean isStandbyMessageReceived() {
3852         return mStandbyMessageReceived;
3853     }
3854 
3855     @ServiceThreadOnly
3856     @VisibleForTesting
onWakeUp(@akeReason final int wakeUpAction)3857     protected void onWakeUp(@WakeReason final int wakeUpAction) {
3858         assertRunOnServiceThread();
3859         mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON,
3860                 false);
3861         if (mCecController != null) {
3862             if (isTvDevice() && getWasCecDisabledOnStandbyByLowEnergyMode()) {
3863                 Slog.w(TAG, "Re-enable CEC on wake-up since it was disabled due to low energy "
3864                         + " mode.");
3865                 getHdmiCecConfig().setIntValue(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
3866                         HDMI_CEC_CONTROL_ENABLED);
3867                 setWasCecDisabledOnStandbyByLowEnergyMode(false);
3868                 int controlStateChangedReason = -1;
3869                 switch (wakeUpAction) {
3870                     case WAKE_UP_SCREEN_ON:
3871                         controlStateChangedReason =
3872                                 HdmiControlManager.CONTROL_STATE_CHANGED_REASON_WAKEUP;
3873                         break;
3874                     case WAKE_UP_BOOT_UP:
3875                         controlStateChangedReason =
3876                                 HdmiControlManager.CONTROL_STATE_CHANGED_REASON_START;
3877                         break;
3878                     default:
3879                         Slog.e(TAG, "wakeUpAction " + wakeUpAction + " not defined.");
3880                         return;
3881 
3882                 }
3883                 // Since CEC is going to be initialized by the setting value update, we must invoke
3884                 // the vendor command listeners here with the reason TV woke up.
3885                 invokeVendorCommandListenersOnControlStateChanged(true,
3886                         controlStateChangedReason);
3887             } else if (isCecControlEnabled()) {
3888                 int startReason = -1;
3889                 switch (wakeUpAction) {
3890                     case WAKE_UP_SCREEN_ON:
3891                         startReason = INITIATED_BY_SCREEN_ON;
3892                         if (mWakeUpMessageReceived) {
3893                             startReason = INITIATED_BY_WAKE_UP_MESSAGE;
3894                         }
3895                         break;
3896                     case WAKE_UP_BOOT_UP:
3897                         startReason = INITIATED_BY_BOOT_UP;
3898                         break;
3899                     default:
3900                         Slog.e(TAG, "wakeUpAction " + wakeUpAction + " not defined.");
3901                         return;
3902 
3903                 }
3904                 initializeCec(startReason);
3905             }
3906         } else {
3907             Slog.i(TAG, "Device does not support HDMI-CEC.");
3908         }
3909         if (isEarcSupported()) {
3910             if (isEarcEnabled()) {
3911                 int startReason = -1;
3912                 switch (wakeUpAction) {
3913                     case WAKE_UP_SCREEN_ON:
3914                         startReason = INITIATED_BY_SCREEN_ON;
3915                         break;
3916                     case WAKE_UP_BOOT_UP:
3917                         startReason = INITIATED_BY_BOOT_UP;
3918                         break;
3919                     default:
3920                         Slog.e(TAG, "wakeUpAction " + wakeUpAction + " not defined.");
3921                         return;
3922                 }
3923                 initializeEarc(startReason);
3924             } else {
3925                 setEarcEnabledInHal(false, false);
3926             }
3927         }
3928         if (isTvDevice()) {
3929             int earcStatus = getEarcStatus();
3930             getAtomWriter().earcStatusChanged(isEarcSupported(), isEarcEnabled(),
3931                     earcStatus, earcStatus, HdmiStatsEnums.LOG_REASON_WAKE);
3932         } else if (isPlaybackDevice()) {
3933             getAtomWriter().dsmStatusChanged(isArcSupported(), isDsmEnabled(),
3934                     HdmiStatsEnums.LOG_REASON_DSM_WAKE);
3935         }
3936         // TODO: Initialize MHL local devices.
3937     }
3938 
3939     @ServiceThreadOnly
3940     @VisibleForTesting
onStandby(final int standbyAction)3941     protected void onStandby(final int standbyAction) {
3942         if (shouldAcquireWakeLockOnStandby()) {
3943             acquireWakeLock();
3944         }
3945         mWakeUpMessageReceived = false;
3946         assertRunOnServiceThread();
3947         mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY,
3948                 false);
3949         invokeVendorCommandListenersOnControlStateChanged(false,
3950                 HdmiControlManager.CONTROL_STATE_CHANGED_REASON_STANDBY);
3951 
3952         final List<HdmiCecLocalDevice> devices = getAllCecLocalDevices();
3953 
3954         if (!isStandbyMessageReceived() && !canGoToStandby()) {
3955             mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_STANDBY);
3956             for (HdmiCecLocalDevice device : devices) {
3957                 device.onStandby(mStandbyMessageReceived, standbyAction);
3958             }
3959             return;
3960         }
3961 
3962         disableCecLocalDevices(new PendingActionClearedCallback() {
3963             @Override
3964             public void onCleared(HdmiCecLocalDevice device) {
3965                 Slog.v(TAG, "On standby-action cleared:" + device.mDeviceType);
3966                 devices.remove(device);
3967                 if (devices.isEmpty()) {
3968                     onPendingActionsCleared(standbyAction);
3969                     // We will not clear local devices here, since some OEM/SOC will keep passing
3970                     // the received packets until the application processor enters to the sleep
3971                     // actually.
3972                 }
3973             }
3974         });
3975 
3976         // Make sure we switch away from absolute volume behavior (AVB) when entering standby.
3977         // We do this because AVB should not be used unless AbsoluteVolumeAudioStatusAction exists,
3978         // and the action cannot exist in standby because there are no local devices.
3979         checkAndUpdateAbsoluteVolumeBehavior();
3980     }
3981 
canGoToStandby()3982     boolean canGoToStandby() {
3983         for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
3984             if (!device.canGoToStandby()) return false;
3985         }
3986         return true;
3987     }
3988 
3989     @ServiceThreadOnly
onLanguageChanged(String language)3990     private void onLanguageChanged(String language) {
3991         assertRunOnServiceThread();
3992         mMenuLanguage = language;
3993 
3994         if (isTvDeviceEnabled()) {
3995             tv().broadcastMenuLanguage(language);
3996             mCecController.setLanguage(language);
3997         }
3998     }
3999 
4000     /**
4001      * Gets the CEC menu language.
4002      *
4003      * <p>This is the ISO/FDIS 639-2 3 letter language code sent in the CEC message @{code <Set Menu
4004      * Language>}.
4005      * See HDMI 1.4b section CEC 13.6.2
4006      *
4007      * @see {@link Locale#getISO3Language()}
4008      */
4009     @ServiceThreadOnly
getLanguage()4010     String getLanguage() {
4011         assertRunOnServiceThread();
4012         return mMenuLanguage;
4013     }
4014 
4015     @VisibleForTesting
disableCecLocalDevices(PendingActionClearedCallback callback)4016     protected void disableCecLocalDevices(PendingActionClearedCallback callback) {
4017         if (mCecController != null) {
4018             for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
4019                 device.disableDevice(mStandbyMessageReceived, callback);
4020             }
4021         }
4022         mMhlController.clearAllLocalDevices();
4023     }
4024 
4025     @ServiceThreadOnly
4026     @VisibleForTesting
clearCecLocalDevices()4027     protected void clearCecLocalDevices() {
4028         assertRunOnServiceThread();
4029         if (mCecController == null) {
4030             return;
4031         }
4032         mCecController.clearLogicalAddress();
4033         mHdmiCecNetwork.clearLocalDevices();
4034     }
4035 
4036     /**
4037      * Normally called after all devices have cleared their pending actions, to execute the final
4038      * phase of the standby flow.
4039      *
4040      * This can also be called during wakeup, when pending actions are cleared after failing to be
4041      * cleared during standby. In this case, it does not execute the standby flow.
4042      */
4043     @ServiceThreadOnly
4044     @VisibleForTesting
onPendingActionsCleared(int standbyAction)4045     protected void onPendingActionsCleared(int standbyAction) {
4046         assertRunOnServiceThread();
4047         Slog.v(TAG, "onPendingActionsCleared");
4048         int localDevicesCount = getAllCecLocalDevices().size();
4049         final int[] countStandbyCompletedDevices = new int[1];
4050         StandbyCompletedCallback callback = new StandbyCompletedCallback() {
4051             @Override
4052             public void onStandbyCompleted() {
4053                 if (localDevicesCount < ++countStandbyCompletedDevices[0]) {
4054                     return;
4055                 }
4056 
4057                 releaseWakeLock();
4058                 if (isAudioSystemDevice() || !isPowerStandby()) {
4059                     return;
4060                 }
4061                 if (isTvDevice() && getDisableCecOnStandbyByLowEnergyMode()
4062                         && mPowerManager.isLowPowerStandbyEnabled()
4063                         && !userEnabledCecInOfflineMode()) {
4064                     Slog.w(TAG, "Disable CEC on standby due to low power energy mode.");
4065                     setWasCecDisabledOnStandbyByLowEnergyMode(true);
4066                     getHdmiCecConfig().setIntValue(
4067                             HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
4068                             HDMI_CEC_CONTROL_DISABLED);
4069                 }
4070                 mCecController.enableSystemCecControl(false);
4071                 mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED);
4072             }
4073         };
4074 
4075         if (mPowerStatusController.isPowerStatusTransientToStandby()) {
4076             mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_STANDBY);
4077             for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
4078                 device.onStandby(mStandbyMessageReceived, standbyAction, callback);
4079             }
4080         }
4081         // Always reset this flag to set up for the next standby
4082         mStandbyMessageReceived = false;
4083     }
4084 
shouldAcquireWakeLockOnStandby()4085     private boolean shouldAcquireWakeLockOnStandby() {
4086         boolean sendStandbyOnSleep = false;
4087         if (tv() != null) {
4088             sendStandbyOnSleep = mHdmiCecConfig.getIntValue(
4089                     HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP)
4090                     == TV_SEND_STANDBY_ON_SLEEP_ENABLED;
4091         } else if (playback() != null) {
4092             sendStandbyOnSleep = !mHdmiCecConfig.getStringValue(
4093                             HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)
4094                     .equals(POWER_CONTROL_MODE_NONE);
4095         }
4096 
4097         return isCecControlEnabled() && isPowerOnOrTransient() && sendStandbyOnSleep;
4098     }
4099 
4100     /**
4101      * Acquire the wake lock used to hold the system awake until the standby process is finished.
4102      */
4103     @VisibleForTesting
acquireWakeLock()4104     protected void acquireWakeLock() {
4105         releaseWakeLock();
4106         mWakeLock = mPowerManager.newWakeLock(
4107                 PowerManager.PARTIAL_WAKE_LOCK, TAG);
4108         mWakeLock.acquire(DEVICE_CLEANUP_TIMEOUT);
4109     }
4110 
4111     /**
4112      * Release the wake lock acquired when the standby process started.
4113      */
4114     @VisibleForTesting
releaseWakeLock()4115     protected void releaseWakeLock() {
4116         if (mWakeLock != null) {
4117             try {
4118                 if (mWakeLock.isHeld()) {
4119                     mWakeLock.release();
4120                 }
4121             } catch (RuntimeException e) {
4122                 Slog.w(TAG, "Exception when releasing wake lock.");
4123             }
4124             mWakeLock = null;
4125         }
4126     }
4127 
4128     @VisibleForTesting
addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId)4129     void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {
4130         VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, vendorId);
4131         try {
4132             listener.asBinder().linkToDeath(record, 0);
4133         } catch (RemoteException e) {
4134             Slog.w(TAG, "Listener already died");
4135             return;
4136         }
4137         synchronized (mLock) {
4138             mVendorCommandListenerRecords.add(record);
4139         }
4140     }
4141 
invokeVendorCommandListenersOnReceived(int deviceType, int srcAddress, int destAddress, byte[] params, boolean hasVendorId)4142     boolean invokeVendorCommandListenersOnReceived(int deviceType, int srcAddress, int destAddress,
4143             byte[] params, boolean hasVendorId) {
4144         synchronized (mLock) {
4145             if (mVendorCommandListenerRecords.isEmpty()) {
4146                 return false;
4147             }
4148             boolean notifiedVendorCommandToListeners = false;
4149             for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
4150                 if (hasVendorId) {
4151                     int vendorId =
4152                             ((params[0] & 0xFF) << 16)
4153                                     + ((params[1] & 0xFF) << 8)
4154                                     + (params[2] & 0xFF);
4155                     if (record.mVendorId != vendorId) {
4156                         continue;
4157                     }
4158                 }
4159                 try {
4160                     record.mListener.onReceived(srcAddress, destAddress, params, hasVendorId);
4161                     notifiedVendorCommandToListeners = true;
4162                 } catch (RemoteException e) {
4163                     Slog.e(TAG, "Failed to notify vendor command reception", e);
4164                 }
4165             }
4166             return notifiedVendorCommandToListeners;
4167         }
4168     }
4169 
invokeVendorCommandListenersOnControlStateChanged(boolean enabled, int reason)4170     boolean invokeVendorCommandListenersOnControlStateChanged(boolean enabled, int reason) {
4171         synchronized (mLock) {
4172             if (mVendorCommandListenerRecords.isEmpty()) {
4173                 return false;
4174             }
4175             for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
4176                 try {
4177                     record.mListener.onControlStateChanged(enabled, reason);
4178                 } catch (RemoteException e) {
4179                     Slog.e(TAG, "Failed to notify control-state-changed to vendor handler", e);
4180                 }
4181             }
4182             return true;
4183         }
4184     }
4185 
addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener)4186     private void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener) {
4187         HdmiMhlVendorCommandListenerRecord record =
4188                 new HdmiMhlVendorCommandListenerRecord(listener);
4189         try {
4190             listener.asBinder().linkToDeath(record, 0);
4191         } catch (RemoteException e) {
4192             Slog.w(TAG, "Listener already died.");
4193             return;
4194         }
4195 
4196         synchronized (mLock) {
4197             mMhlVendorCommandListenerRecords.add(record);
4198         }
4199     }
4200 
invokeMhlVendorCommandListeners(int portId, int offest, int length, byte[] data)4201     void invokeMhlVendorCommandListeners(int portId, int offest, int length, byte[] data) {
4202         synchronized (mLock) {
4203             for (HdmiMhlVendorCommandListenerRecord record : mMhlVendorCommandListenerRecords) {
4204                 try {
4205                     record.mListener.onReceived(portId, offest, length, data);
4206                 } catch (RemoteException e) {
4207                     Slog.e(TAG, "Failed to notify MHL vendor command", e);
4208                 }
4209             }
4210         }
4211     }
4212 
setStandbyMode(boolean isStandbyModeOn)4213     void setStandbyMode(boolean isStandbyModeOn) {
4214         assertRunOnServiceThread();
4215         if (isPowerOnOrTransient() && isStandbyModeOn) {
4216             mPowerManager.goToSleep(SystemClock.uptimeMillis(),
4217                     PowerManager.GO_TO_SLEEP_REASON_HDMI, 0);
4218             if (playback() != null) {
4219                 playback().sendStandby(0 /* unused */);
4220             }
4221         } else if (isPowerStandbyOrTransient() && !isStandbyModeOn) {
4222             mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_HDMI,
4223                     "android.server.hdmi:WAKE");
4224             if (playback() != null) {
4225                 oneTouchPlay(new IHdmiControlCallback.Stub() {
4226                     @Override
4227                     public void onComplete(int result) {
4228                         if (result != HdmiControlManager.RESULT_SUCCESS) {
4229                             Slog.w(TAG, "Failed to complete 'one touch play'. result=" + result);
4230                         }
4231                     }
4232                 });
4233             }
4234         }
4235     }
4236 
4237     @HdmiControlManager.VolumeControl
getHdmiCecVolumeControl()4238     int getHdmiCecVolumeControl() {
4239         synchronized (mLock) {
4240             return mHdmiCecVolumeControl;
4241         }
4242     }
4243 
isProhibitMode()4244     boolean isProhibitMode() {
4245         synchronized (mLock) {
4246             return mProhibitMode;
4247         }
4248     }
4249 
setProhibitMode(boolean enabled)4250     void setProhibitMode(boolean enabled) {
4251         synchronized (mLock) {
4252             mProhibitMode = enabled;
4253         }
4254     }
4255 
isSystemAudioActivated()4256     boolean isSystemAudioActivated() {
4257         synchronized (mLock) {
4258             return mSystemAudioActivated;
4259         }
4260     }
4261 
setSystemAudioActivated(boolean on)4262     void setSystemAudioActivated(boolean on) {
4263         synchronized (mLock) {
4264             mSystemAudioActivated = on;
4265         }
4266         runOnServiceThread(this::checkAndUpdateAbsoluteVolumeBehavior);
4267     }
4268 
4269     @ServiceThreadOnly
setCecEnabled(@dmiControlManager.HdmiCecControl int enabled)4270     void setCecEnabled(@HdmiControlManager.HdmiCecControl int enabled) {
4271         assertRunOnServiceThread();
4272 
4273         synchronized (mLock) {
4274             mHdmiControlEnabled = enabled;
4275         }
4276 
4277         if (enabled == HDMI_CEC_CONTROL_ENABLED) {
4278             onEnableCec();
4279             setHdmiCecVolumeControlEnabledInternal(getHdmiCecConfig().getIntValue(
4280                     HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE));
4281             return;
4282         }
4283 
4284         setHdmiCecVolumeControlEnabledInternal(HdmiControlManager.VOLUME_CONTROL_DISABLED);
4285         // Call the vendor handler before the service is disabled.
4286         invokeVendorCommandListenersOnControlStateChanged(false,
4287                 HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING);
4288         // Post the remained tasks in the service thread again to give the vendor-issued-tasks
4289         // a chance to run.
4290         runOnServiceThread(new Runnable() {
4291             @Override
4292             public void run() {
4293                 onDisableCec();
4294             }
4295         });
4296         announceHdmiControlStatusChange(enabled);
4297 
4298         return;
4299     }
4300 
4301     @ServiceThreadOnly
onEnableCec()4302     private void onEnableCec() {
4303         mCecController.enableCec(true);
4304         mCecController.enableSystemCecControl(true);
4305         mMhlController.setOption(OPTION_MHL_ENABLE, ENABLED);
4306 
4307         initializeCec(INITIATED_BY_ENABLE_CEC);
4308     }
4309 
4310     @ServiceThreadOnly
onDisableCec()4311     private void onDisableCec() {
4312         disableCecLocalDevices(
4313                 new PendingActionClearedCallback() {
4314                     @Override
4315                     public void onCleared(HdmiCecLocalDevice device) {
4316                         assertRunOnServiceThread();
4317                         mCecController.flush(
4318                                 new Runnable() {
4319                                     @Override
4320                                     public void run() {
4321                                         mCecController.enableCec(false);
4322                                         mCecController.enableSystemCecControl(false);
4323                                         mMhlController.setOption(OPTION_MHL_ENABLE, DISABLED);
4324                                         clearCecLocalDevices();
4325                                     }
4326                                 });
4327                     }
4328                 });
4329     }
4330 
4331     @ServiceThreadOnly
setActivePortId(int portId)4332     void setActivePortId(int portId) {
4333         assertRunOnServiceThread();
4334         mActivePortId = portId;
4335 
4336         // Resets last input for MHL, which stays valid only after the MHL device was selected,
4337         // and no further switching is done.
4338         setLastInputForMhl(Constants.INVALID_PORT_ID);
4339     }
4340 
getLocalActiveSource()4341     ActiveSource getLocalActiveSource() {
4342         synchronized (mLock) {
4343             return mActiveSource;
4344         }
4345     }
4346 
4347     @VisibleForTesting
pauseActiveMediaSessions()4348     void pauseActiveMediaSessions() {
4349         MediaSessionManager mediaSessionManager = getContext()
4350                 .getSystemService(MediaSessionManager.class);
4351         List<MediaController> mediaControllers = mediaSessionManager.getActiveSessions(null);
4352         for (MediaController mediaController : mediaControllers) {
4353             mediaController.getTransportControls().pause();
4354         }
4355     }
4356 
setActiveSource(int logicalAddress, int physicalAddress, String caller)4357     void setActiveSource(int logicalAddress, int physicalAddress, String caller) {
4358         synchronized (mLock) {
4359             mActiveSource.logicalAddress = logicalAddress;
4360             mActiveSource.physicalAddress = physicalAddress;
4361         }
4362 
4363         getAtomWriter().activeSourceChanged(logicalAddress, physicalAddress,
4364                 HdmiUtils.pathRelationship(getPhysicalAddress(), physicalAddress));
4365 
4366         // If the current device is a source device, check if the current Active Source matches
4367         // the local device info.
4368         for (HdmiCecLocalDevice device : getAllCecLocalDevices()) {
4369             boolean deviceIsActiveSource =
4370                     logicalAddress == device.getDeviceInfo().getLogicalAddress()
4371                             && physicalAddress == getPhysicalAddress();
4372 
4373             device.addActiveSourceHistoryItem(new ActiveSource(logicalAddress, physicalAddress),
4374                     deviceIsActiveSource, caller);
4375         }
4376 
4377         runOnServiceThread(this::checkAndUpdateAbsoluteVolumeBehavior);
4378     }
4379 
4380     // This method should only be called when the device can be the active source
4381     // and all the device types call into this method.
4382     // For example, when receiving broadcast messages, all the device types will call this
4383     // method but only one of them will be the Active Source.
setAndBroadcastActiveSource( int physicalAddress, int deviceType, int source, String caller)4384     protected void setAndBroadcastActiveSource(
4385             int physicalAddress, int deviceType, int source, String caller) {
4386         // If the device has both playback and audio system logical addresses,
4387         // playback will claim active source. Otherwise audio system will.
4388         if (deviceType == HdmiDeviceInfo.DEVICE_PLAYBACK) {
4389             HdmiCecLocalDevicePlayback playback = playback();
4390             playback.dismissUiOnActiveSourceStatusRecovered();
4391             playback.removeAction(RequestActiveSourceAction.class);
4392             playback.setActiveSource(playback.getDeviceInfo().getLogicalAddress(), physicalAddress,
4393                     caller);
4394             playback.wakeUpIfActiveSource();
4395             playback.maySendActiveSource(source);
4396             playback.mDelayedStandbyOnActiveSourceLostHandler.removeCallbacksAndMessages(null);
4397         }
4398 
4399         if (deviceType == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) {
4400             HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
4401             if (playback() == null) {
4402                 audioSystem.setActiveSource(audioSystem.getDeviceInfo().getLogicalAddress(),
4403                         physicalAddress, caller);
4404                 audioSystem.wakeUpIfActiveSource();
4405                 audioSystem.maySendActiveSource(source);
4406             }
4407         }
4408     }
4409 
4410     // This method should only be called when the device can be the active source
4411     // and only one of the device types calls into this method.
4412     // For example, when receiving One Touch Play, only playback device handles it
4413     // and this method updates Active Source in all the device types sharing the same
4414     // Physical Address.
setAndBroadcastActiveSourceFromOneDeviceType( int sourceAddress, int physicalAddress, String caller)4415     protected void setAndBroadcastActiveSourceFromOneDeviceType(
4416             int sourceAddress, int physicalAddress, String caller) {
4417         // If the device has both playback and audio system logical addresses,
4418         // playback will claim active source. Otherwise audio system will.
4419         HdmiCecLocalDevicePlayback playback = playback();
4420         HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
4421         if (playback != null) {
4422             playback.dismissUiOnActiveSourceStatusRecovered();
4423             playback.setActiveSource(playback.getDeviceInfo().getLogicalAddress(), physicalAddress,
4424                     caller);
4425             playback.wakeUpIfActiveSource();
4426             playback.maySendActiveSource(sourceAddress);
4427         } else if (audioSystem != null) {
4428             audioSystem.setActiveSource(audioSystem.getDeviceInfo().getLogicalAddress(),
4429                     physicalAddress, caller);
4430             audioSystem.wakeUpIfActiveSource();
4431             audioSystem.maySendActiveSource(sourceAddress);
4432         }
4433     }
4434 
4435     @ServiceThreadOnly
setLastInputForMhl(int portId)4436     void setLastInputForMhl(int portId) {
4437         assertRunOnServiceThread();
4438         mLastInputMhl = portId;
4439     }
4440 
4441     @ServiceThreadOnly
getLastInputForMhl()4442     int getLastInputForMhl() {
4443         assertRunOnServiceThread();
4444         return mLastInputMhl;
4445     }
4446 
4447     /**
4448      * Performs input change, routing control for MHL device.
4449      *
4450      * @param portId MHL port, or the last port to go back to if {@code contentOn} is false
4451      * @param contentOn {@code true} if RAP data is content on; otherwise false
4452      */
4453     @ServiceThreadOnly
changeInputForMhl(int portId, boolean contentOn)4454     void changeInputForMhl(int portId, boolean contentOn) {
4455         assertRunOnServiceThread();
4456         if (tv() == null) return;
4457         final int lastInput = contentOn ? tv().getActivePortId() : Constants.INVALID_PORT_ID;
4458         if (portId != Constants.INVALID_PORT_ID) {
4459             tv().doManualPortSwitching(portId, new IHdmiControlCallback.Stub() {
4460                 @Override
4461                 public void onComplete(int result) throws RemoteException {
4462                     // Keep the last input to switch back later when RAP[ContentOff] is received.
4463                     // This effectively sets the port to invalid one if the switching is for
4464                     // RAP[ContentOff].
4465                     setLastInputForMhl(lastInput);
4466                 }
4467             });
4468         }
4469         // MHL device is always directly connected to the port. Update the active port ID to avoid
4470         // unnecessary post-routing control task.
4471         tv().setActivePortId(portId);
4472 
4473         // The port is either the MHL-enabled port where the mobile device is connected, or
4474         // the last port to go back to when turnoff command is received. Note that the last port
4475         // may not be the MHL-enabled one. In this case the device info to be passed to
4476         // input change listener should be the one describing the corresponding HDMI port.
4477         HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
4478         HdmiDeviceInfo info = (device != null) ? device.getInfo()
4479                 : mHdmiCecNetwork.getDeviceForPortId(portId);
4480         invokeInputChangeListener(info);
4481     }
4482 
setMhlInputChangeEnabled(boolean enabled)4483     void setMhlInputChangeEnabled(boolean enabled) {
4484         mMhlController.setOption(OPTION_MHL_INPUT_SWITCHING, toInt(enabled));
4485 
4486         synchronized (mLock) {
4487             mMhlInputChangeEnabled = enabled;
4488         }
4489     }
4490 
4491     @VisibleForTesting
getAtomWriter()4492     protected HdmiCecAtomWriter getAtomWriter() {
4493         return mAtomWriter;
4494     }
4495 
isMhlInputChangeEnabled()4496     boolean isMhlInputChangeEnabled() {
4497         synchronized (mLock) {
4498             return mMhlInputChangeEnabled;
4499         }
4500     }
4501 
4502     @ServiceThreadOnly
displayOsd(int messageId)4503     void displayOsd(int messageId) {
4504         assertRunOnServiceThread();
4505         Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
4506         intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
4507         sendBroadcastAsUser(intent);
4508     }
4509 
4510     @ServiceThreadOnly
displayOsd(int messageId, int extra)4511     void displayOsd(int messageId, int extra) {
4512         assertRunOnServiceThread();
4513         Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
4514         intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
4515         intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_EXTRA_PARAM1, extra);
4516         sendBroadcastAsUser(intent);
4517     }
4518 
4519     // This method is used such that we can override it inside unit tests to avoid a
4520     // SecurityException.
4521     @ServiceThreadOnly
4522     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
4523     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
sendBroadcastAsUser(@equiresPermission Intent intent)4524     protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
4525         assertRunOnServiceThread();
4526         getContext().sendBroadcastAsUser(intent, UserHandle.ALL, HdmiControlService.PERMISSION);
4527     }
4528 
4529     @VisibleForTesting
getHdmiCecConfig()4530     protected HdmiCecConfig getHdmiCecConfig() {
4531         return mHdmiCecConfig;
4532     }
4533 
4534     private HdmiCecConfig.SettingChangeListener mSettingChangeListener =
4535             new HdmiCecConfig.SettingChangeListener() {
4536                 @Override
4537                 public void onChange(String name) {
4538                     synchronized (mLock) {
4539                         if (!mHdmiCecSettingChangeListenerRecords.containsKey(name)) {
4540                             return;
4541                         }
4542                         mHdmiCecSettingChangeListenerRecords.get(name).broadcast(listener -> {
4543                             invokeCecSettingChangeListenerLocked(name, listener);
4544                         });
4545                     }
4546                 }
4547             };
4548 
addCecSettingChangeListener(String name, final IHdmiCecSettingChangeListener listener)4549     private void addCecSettingChangeListener(String name,
4550             final IHdmiCecSettingChangeListener listener) {
4551         synchronized (mLock) {
4552             if (!mHdmiCecSettingChangeListenerRecords.containsKey(name)) {
4553                 mHdmiCecSettingChangeListenerRecords.put(name, new RemoteCallbackList<>());
4554                 mHdmiCecConfig.registerChangeListener(name, mSettingChangeListener);
4555             }
4556             mHdmiCecSettingChangeListenerRecords.get(name).register(listener);
4557         }
4558     }
4559 
removeCecSettingChangeListener(String name, final IHdmiCecSettingChangeListener listener)4560     private void removeCecSettingChangeListener(String name,
4561             final IHdmiCecSettingChangeListener listener) {
4562         synchronized (mLock) {
4563             if (!mHdmiCecSettingChangeListenerRecords.containsKey(name)) {
4564                 return;
4565             }
4566             mHdmiCecSettingChangeListenerRecords.get(name).unregister(listener);
4567             if (mHdmiCecSettingChangeListenerRecords.get(name).getRegisteredCallbackCount() == 0) {
4568                 mHdmiCecSettingChangeListenerRecords.remove(name);
4569                 mHdmiCecConfig.removeChangeListener(name, mSettingChangeListener);
4570             }
4571         }
4572     }
4573 
invokeCecSettingChangeListenerLocked(String name, final IHdmiCecSettingChangeListener listener)4574     private void invokeCecSettingChangeListenerLocked(String name,
4575             final IHdmiCecSettingChangeListener listener) {
4576         try {
4577             listener.onChange(name);
4578         } catch (RemoteException e) {
4579             Slog.e(TAG, "Failed to report setting change", e);
4580         }
4581     }
4582 
4583     /**
4584      * Listener for changes to the volume behavior of an audio output device. Caches the
4585      * volume behavior of devices used for absolute volume behavior.
4586      */
4587     @VisibleForTesting
4588     @ServiceThreadOnly
onDeviceVolumeBehaviorChanged(AudioDeviceAttributes device, int volumeBehavior)4589     void onDeviceVolumeBehaviorChanged(AudioDeviceAttributes device, int volumeBehavior) {
4590         assertRunOnServiceThread();
4591         if (AVB_AUDIO_OUTPUT_DEVICES.contains(device)) {
4592             synchronized (mLock) {
4593                 mAudioDeviceVolumeBehaviors.put(device, volumeBehavior);
4594             }
4595             checkAndUpdateAbsoluteVolumeBehavior();
4596         }
4597     }
4598 
4599     /**
4600      * Wrapper for {@link AudioManager#getDeviceVolumeBehavior} that takes advantage of cached
4601      * results for the volume behaviors of HDMI audio devices.
4602      */
4603     @AudioDeviceVolumeManager.DeviceVolumeBehavior
getDeviceVolumeBehavior(AudioDeviceAttributes device)4604     private int getDeviceVolumeBehavior(AudioDeviceAttributes device) {
4605         if (AVB_AUDIO_OUTPUT_DEVICES.contains(device)) {
4606             synchronized (mLock) {
4607                 if (mAudioDeviceVolumeBehaviors.containsKey(device)) {
4608                     return mAudioDeviceVolumeBehaviors.get(device);
4609                 }
4610             }
4611         }
4612         return getAudioDeviceVolumeManager().getDeviceVolumeBehavior(device);
4613     }
4614 
4615     /**
4616      * Returns whether absolute volume behavior is enabled or not. This is true if any AVB-capable
4617      * audio output device is using absolute volume behavior.
4618      */
isAbsoluteVolumeBehaviorEnabled()4619     public boolean isAbsoluteVolumeBehaviorEnabled() {
4620         if (!isTvDevice() && !isPlaybackDevice()) {
4621             return false;
4622         }
4623         for (AudioDeviceAttributes device : getAvbCapableAudioOutputDevices()) {
4624             if (ABSOLUTE_VOLUME_BEHAVIORS.contains(getDeviceVolumeBehavior(device))) {
4625                 return true;
4626             }
4627         }
4628         return false;
4629     }
4630 
4631     /**
4632      * Returns a list of audio output devices that may adopt absolute volume behavior.
4633      */
getAvbCapableAudioOutputDevices()4634     private List<AudioDeviceAttributes> getAvbCapableAudioOutputDevices() {
4635         if (tv() != null) {
4636             return TV_AVB_AUDIO_OUTPUT_DEVICES;
4637         } else if (playback() != null) {
4638             return PLAYBACK_AVB_AUDIO_OUTPUT_DEVICES;
4639         } else {
4640             return Collections.emptyList();
4641         }
4642     }
4643 
4644     /**
4645      * This method is responsible for adopting or disabling absolute volume behavior and
4646      * adjust-only absolute volume behavior in AudioService. These volume behaviors are adopted on
4647      * specific audio output devices: HDMI for playback devices, and HDMI_ARC or HDMI_EARC for TVs.
4648      *
4649      * This method enables absolute volume behavior on a Playback device or TV panel when it is
4650      * playing audio on an external device (the System Audio device) that supports the feature.
4651      * This allows the volume level of the System Audio device to be tracked and set by Android.
4652      *
4653      * Absolute volume behavior requires the following conditions:
4654      * 1. The device is not in standby or transient to standby
4655      * 2. If the System Audio Device is an Audio System: System Audio Mode is active
4656      * 3. All AVB-capable audio output devices are already using full/absolute volume behavior
4657      * 4. CEC volume is enabled
4658      * 5. The System Audio device supports the <Set Audio Volume Level> message
4659      *
4660      * This method enables adjust-only absolute volume behavior on TV panels when conditions
4661      * 1, 2, and 3 are met, but condition 4 is not. This allows TVs to track the volume level of
4662      * the System Audio device and display numeric volume UI for it, even if the System Audio device
4663      * does not support <Set Audio Volume Level>.
4664      */
4665     @ServiceThreadOnly
checkAndUpdateAbsoluteVolumeBehavior()4666     void checkAndUpdateAbsoluteVolumeBehavior() {
4667         assertRunOnServiceThread();
4668 
4669         // Can't set volume behavior before we have access to system services
4670         if (getAudioManager() == null) {
4671             return;
4672         }
4673 
4674         // Condition 1: The device is not in standby or transient to standby
4675         if (mPowerStatusController != null && isPowerStandbyOrTransient()) {
4676             switchToFullVolumeBehavior();
4677             return;
4678         }
4679 
4680         HdmiCecLocalDevice localCecDevice;
4681         if (isTvDevice() && tv() != null) {
4682             localCecDevice = tv();
4683             // Condition 2: TVs need System Audio Mode to be active
4684             // (Doesn't apply to Playback Devices, where if SAM isn't active, we assume the
4685             // TV is the System Audio Device instead.)
4686             if (!isSystemAudioActivated()) {
4687                 switchToFullVolumeBehavior();
4688                 return;
4689             }
4690         } else if (isPlaybackDevice() && playback() != null) {
4691             localCecDevice = playback();
4692         } else {
4693             // Either this device type doesn't support AVB, or it hasn't fully initialized yet
4694             return;
4695         }
4696 
4697         HdmiDeviceInfo systemAudioDeviceInfo = getDeviceInfo(
4698                 localCecDevice.findAudioReceiverAddress());
4699 
4700         // Condition 3: All AVB-capable audio outputs already use full/absolute volume behavior
4701         // We only need to check the first AVB-capable audio output because only TV panels
4702         // have more than one of them, and they always have the same volume behavior.
4703         @AudioDeviceVolumeManager.DeviceVolumeBehavior int currentVolumeBehavior =
4704                 getDeviceVolumeBehavior(getAvbCapableAudioOutputDevices().get(0));
4705         boolean alreadyUsingFullOrAbsoluteVolume =
4706                 FULL_AND_ABSOLUTE_VOLUME_BEHAVIORS.contains(currentVolumeBehavior);
4707 
4708         // Condition 4: CEC volume is enabled
4709         boolean cecVolumeEnabled =
4710                 getHdmiCecVolumeControl() == HdmiControlManager.VOLUME_CONTROL_ENABLED;
4711 
4712         if (!cecVolumeEnabled || !alreadyUsingFullOrAbsoluteVolume) {
4713             switchToFullVolumeBehavior();
4714             return;
4715         }
4716 
4717         // Check for safety: if the System Audio device is a candidate for AVB, we should already
4718         // have received messages from it to trigger the other conditions.
4719         if (systemAudioDeviceInfo == null) {
4720             switchToFullVolumeBehavior();
4721             return;
4722         }
4723 
4724         // Condition 5: The System Audio device supports <Set Audio Volume Level>
4725         switch (systemAudioDeviceInfo.getDeviceFeatures().getSetAudioVolumeLevelSupport()) {
4726             case DeviceFeatures.FEATURE_SUPPORTED:
4727                 if (currentVolumeBehavior
4728                         != AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
4729                     // Start an action that will call enableAbsoluteVolumeBehavior
4730                     // once the System Audio device sends <Report Audio Status>
4731                     localCecDevice.startNewAvbAudioStatusAction(
4732                             systemAudioDeviceInfo.getLogicalAddress());
4733                 }
4734                 return;
4735             case DeviceFeatures.FEATURE_NOT_SUPPORTED:
4736                 // TVs may adopt adjust-only absolute volume behavior if condition 4 isn't met.
4737                 // This allows the device to display numeric volume UI for the System Audio device.
4738                 if (tv() != null && mNumericSoundbarVolumeUiOnTvFeatureFlagEnabled) {
4739                     if (currentVolumeBehavior
4740                             != AudioDeviceVolumeManager
4741                             .DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY) {
4742                         // If we're currently using absolute volume behavior, switch to full volume
4743                         // behavior until we successfully adopt adjust-only absolute volume behavior
4744                         if (currentVolumeBehavior
4745                                 == AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
4746                             for (AudioDeviceAttributes device : getAvbCapableAudioOutputDevices()) {
4747                                 getAudioDeviceVolumeManager().setDeviceVolumeBehavior(device,
4748                                         AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
4749                             }
4750                         }
4751                         // Start an action that will call enableAbsoluteVolumeBehavior
4752                         // once the System Audio device sends <Report Audio Status>
4753                         localCecDevice.startNewAvbAudioStatusAction(
4754                                 systemAudioDeviceInfo.getLogicalAddress());
4755                     }
4756                 } else {
4757                     switchToFullVolumeBehavior();
4758                 }
4759                 return;
4760             case DeviceFeatures.FEATURE_SUPPORT_UNKNOWN:
4761                 if (currentVolumeBehavior
4762                         == AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
4763                     switchToFullVolumeBehavior();
4764                 }
4765                 localCecDevice.querySetAudioVolumeLevelSupport(
4766                         systemAudioDeviceInfo.getLogicalAddress());
4767         }
4768     }
4769 
4770     /**
4771      * Switches to full volume behavior, if either absolute or adjust-only absolute volume behavior
4772      * are currently used. Removes the action for handling volume updates for these behaviors.
4773      */
switchToFullVolumeBehavior()4774     private void switchToFullVolumeBehavior() {
4775         Slog.d(TAG, "Switching to full volume behavior");
4776 
4777         if (playback() != null) {
4778             playback().removeAvbAudioStatusAction();
4779         } else if (tv() != null) {
4780             tv().removeAvbAudioStatusAction();
4781         }
4782 
4783         for (AudioDeviceAttributes device : getAvbCapableAudioOutputDevices()) {
4784             if (ABSOLUTE_VOLUME_BEHAVIORS.contains(getDeviceVolumeBehavior(device))) {
4785                 getAudioDeviceVolumeManager().setDeviceVolumeBehavior(device,
4786                         AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
4787             }
4788         }
4789     }
4790 
4791     /**
4792      * Enables absolute volume behavior or adjust-only absolute volume behavior. Should only be
4793      * called when the conditions for one of these behaviors is met -
4794      * see {@link #checkAndUpdateAbsoluteVolumeBehavior}.
4795      *
4796      * @param audioStatus The initial audio status to set the audio output device to
4797      */
enableAbsoluteVolumeBehavior(AudioStatus audioStatus)4798     void enableAbsoluteVolumeBehavior(AudioStatus audioStatus) {
4799         HdmiCecLocalDevice localDevice = isPlaybackDevice() ? playback() : tv();
4800         HdmiDeviceInfo systemAudioDevice = getDeviceInfo(localDevice.findAudioReceiverAddress());
4801         VolumeInfo volumeInfo = new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
4802                 .setMuted(audioStatus.getMute())
4803                 .setVolumeIndex(audioStatus.getVolume())
4804                 .setMaxVolumeIndex(AudioStatus.MAX_VOLUME)
4805                 .setMinVolumeIndex(AudioStatus.MIN_VOLUME)
4806                 .build();
4807         mAbsoluteVolumeChangedListener = new AbsoluteVolumeChangedListener(
4808                 localDevice, systemAudioDevice);
4809 
4810         // AudioService sets the volume of the stream and device based on the input VolumeInfo
4811         // when enabling absolute volume behavior, but not the mute state
4812         notifyAvbMuteChange(audioStatus.getMute());
4813 
4814         // If <Set Audio Volume Level> is supported, enable absolute volume behavior.
4815         // Otherwise, enable adjust-only AVB on TVs only.
4816         if (systemAudioDevice.getDeviceFeatures().getSetAudioVolumeLevelSupport()
4817                 == DeviceFeatures.FEATURE_SUPPORTED) {
4818             Slog.d(TAG, "Enabling absolute volume behavior");
4819             for (AudioDeviceAttributes device : getAvbCapableAudioOutputDevices()) {
4820                 getAudioDeviceVolumeManager().setDeviceAbsoluteVolumeBehavior(
4821                         device, volumeInfo, true, mServiceThreadExecutor,
4822                         mAbsoluteVolumeChangedListener);
4823             }
4824         } else if (tv() != null) {
4825             Slog.d(TAG, "Enabling adjust-only absolute volume behavior");
4826             for (AudioDeviceAttributes device : getAvbCapableAudioOutputDevices()) {
4827                 getAudioDeviceVolumeManager().setDeviceAbsoluteVolumeAdjustOnlyBehavior(
4828                         device, volumeInfo, true, mServiceThreadExecutor,
4829                         mAbsoluteVolumeChangedListener);
4830             }
4831         }
4832 
4833     }
4834 
4835     private AbsoluteVolumeChangedListener mAbsoluteVolumeChangedListener;
4836 
4837     @VisibleForTesting
getAbsoluteVolumeChangedListener()4838     AbsoluteVolumeChangedListener getAbsoluteVolumeChangedListener() {
4839         return mAbsoluteVolumeChangedListener;
4840     }
4841 
4842     /**
4843      * Listeners for changes reported by AudioService to the state of an audio output device using
4844      * absolute volume behavior.
4845      */
4846     @VisibleForTesting
4847     class AbsoluteVolumeChangedListener implements
4848             AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener {
4849         private HdmiCecLocalDevice mLocalDevice;
4850         private HdmiDeviceInfo mSystemAudioDevice;
4851 
AbsoluteVolumeChangedListener(HdmiCecLocalDevice localDevice, HdmiDeviceInfo systemAudioDevice)4852         private AbsoluteVolumeChangedListener(HdmiCecLocalDevice localDevice,
4853                 HdmiDeviceInfo systemAudioDevice) {
4854             mLocalDevice = localDevice;
4855             mSystemAudioDevice = systemAudioDevice;
4856         }
4857 
4858         /**
4859          * Called when AudioService sets the volume level of an absolute volume audio output device
4860          * to a numeric value.
4861          */
4862         @Override
onAudioDeviceVolumeChanged( @onNull AudioDeviceAttributes audioDevice, @NonNull VolumeInfo volumeInfo)4863         public void onAudioDeviceVolumeChanged(
4864                 @NonNull AudioDeviceAttributes audioDevice,
4865                 @NonNull VolumeInfo volumeInfo) {
4866             int localDeviceAddress = mLocalDevice.getDeviceInfo().getLogicalAddress();
4867 
4868             // We can't send <Set Audio Volume Level> if the System Audio device doesn't support it.
4869             // But AudioService has already updated its volume and expects us to set it.
4870             // So the best we can do is to send <Give Audio Status>, which triggers
4871             // <Report Audio Status>, which should update AudioService with its correct volume.
4872             if (mSystemAudioDevice.getDeviceFeatures().getSetAudioVolumeLevelSupport()
4873                     != DeviceFeatures.FEATURE_SUPPORTED) {
4874                 // Update the volume tracked in AbsoluteVolumeAudioStatusAction
4875                 // so it correctly processes the next <Report Audio Status>
4876                 HdmiCecLocalDevice avbDevice = isTvDevice() ? tv() : playback();
4877                 avbDevice.updateAvbVolume(volumeInfo.getVolumeIndex());
4878                 // Send <Give Audio Status>
4879                 sendCecCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(
4880                         localDeviceAddress,
4881                         mSystemAudioDevice.getLogicalAddress()
4882                 ));
4883                 return;
4884             }
4885 
4886             // Send <Set Audio Volume Level> to notify the System Audio device of the volume change
4887             sendCecCommand(SetAudioVolumeLevelMessage.build(
4888                             localDeviceAddress,
4889                             mSystemAudioDevice.getLogicalAddress(),
4890                             volumeInfo.getVolumeIndex()),
4891                     // If sending the message fails, ask the System Audio device for its
4892                     // audio status so that we can update AudioService
4893                     (int errorCode) -> {
4894                         if (errorCode == SendMessageResult.SUCCESS) {
4895                             // Update the volume tracked in our AbsoluteVolumeAudioStatusAction
4896                             // so it correctly processes incoming <Report Audio Status> messages
4897                             HdmiCecLocalDevice avbDevice = isTvDevice() ? tv() : playback();
4898                             avbDevice.updateAvbVolume(volumeInfo.getVolumeIndex());
4899                         } else {
4900                             sendCecCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(
4901                                     localDeviceAddress,
4902                                     mSystemAudioDevice.getLogicalAddress()
4903                             ));
4904                         }
4905                     });
4906         }
4907 
4908         /**
4909          * Called when AudioService adjusts the volume or mute state of an absolute volume
4910          * audio output device
4911          */
4912         @Override
onAudioDeviceVolumeAdjusted( @onNull AudioDeviceAttributes audioDevice, @NonNull VolumeInfo volumeInfo, @AudioManager.VolumeAdjustment int direction, @AudioDeviceVolumeManager.VolumeAdjustmentMode int mode )4913         public void onAudioDeviceVolumeAdjusted(
4914                 @NonNull AudioDeviceAttributes audioDevice,
4915                 @NonNull VolumeInfo volumeInfo,
4916                 @AudioManager.VolumeAdjustment int direction,
4917                 @AudioDeviceVolumeManager.VolumeAdjustmentMode int mode
4918         ) {
4919             int keyCode;
4920             switch (direction) {
4921                 case AudioManager.ADJUST_RAISE:
4922                     keyCode = KeyEvent.KEYCODE_VOLUME_UP;
4923                     break;
4924                 case AudioManager.ADJUST_LOWER:
4925                     keyCode = KeyEvent.KEYCODE_VOLUME_DOWN;
4926                     break;
4927                 case AudioManager.ADJUST_TOGGLE_MUTE:
4928                 case AudioManager.ADJUST_MUTE:
4929                 case AudioManager.ADJUST_UNMUTE:
4930                     // Many CEC devices only support toggle mute. Therefore, we send the
4931                     // same keycode for all three mute options.
4932                     keyCode = KeyEvent.KEYCODE_VOLUME_MUTE;
4933                     break;
4934                 case AudioManager.ADJUST_SAME:
4935                     // Query the current audio status of the Audio System and display UI for it
4936                     // Only for TVs, because Playback devices don't display UI when using AVB
4937                     if (tv() != null) {
4938                         tv().requestAndUpdateAvbAudioStatus();
4939                     }
4940                     return;
4941                 default:
4942                     return;
4943             }
4944             switch (mode) {
4945                 case AudioDeviceVolumeManager.ADJUST_MODE_NORMAL:
4946                     mLocalDevice.sendVolumeKeyEvent(keyCode, true);
4947                     mLocalDevice.sendVolumeKeyEvent(keyCode, false);
4948                     break;
4949                 case AudioDeviceVolumeManager.ADJUST_MODE_START:
4950                     mLocalDevice.sendVolumeKeyEvent(keyCode, true);
4951                     break;
4952                 case AudioDeviceVolumeManager.ADJUST_MODE_END:
4953                     mLocalDevice.sendVolumeKeyEvent(keyCode, false);
4954                     break;
4955                 default:
4956                     return;
4957             }
4958         }
4959     }
4960 
4961     /**
4962      * Notifies AudioService of a change in the volume of the System Audio device. Has no effect if
4963      * AVB is disabled, or STREAM_MUSIC is not playing on any AVB device.
4964      */
notifyAvbVolumeChange(int volume)4965     void notifyAvbVolumeChange(int volume) {
4966         if (!isAbsoluteVolumeBehaviorEnabled()) return;
4967         List<AudioDeviceAttributes> streamMusicDevices =
4968                 getAudioManager().getDevicesForAttributes(STREAM_MUSIC_ATTRIBUTES);
4969         for (AudioDeviceAttributes streamMusicDevice : streamMusicDevices) {
4970             if (getAvbCapableAudioOutputDevices().contains(streamMusicDevice)) {
4971                 int flags = AudioManager.FLAG_ABSOLUTE_VOLUME;
4972                 if (isTvDevice()) {
4973                     flags |= AudioManager.FLAG_SHOW_UI;
4974                 }
4975                 setStreamMusicVolume(volume, flags);
4976                 return;
4977             }
4978         }
4979     }
4980 
4981     /**
4982      * Notifies AudioService of a change in the mute status of the System Audio device. Has no
4983      * effect if AVB is disabled, or the audio output device for AVB is not playing for STREAM_MUSIC
4984      */
notifyAvbMuteChange(boolean mute)4985     void notifyAvbMuteChange(boolean mute) {
4986         if (!isAbsoluteVolumeBehaviorEnabled()) return;
4987         List<AudioDeviceAttributes> streamMusicDevices =
4988                 getAudioManager().getDevicesForAttributes(STREAM_MUSIC_ATTRIBUTES);
4989         for (AudioDeviceAttributes streamMusicDevice : streamMusicDevices) {
4990             if (getAvbCapableAudioOutputDevices().contains(streamMusicDevice)) {
4991                 int direction = mute ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE;
4992                 int flags = AudioManager.FLAG_ABSOLUTE_VOLUME;
4993                 if (isTvDevice()) {
4994                     flags |= AudioManager.FLAG_SHOW_UI;
4995                 }
4996                 getAudioManager().adjustStreamVolume(AudioManager.STREAM_MUSIC, direction, flags);
4997                 return;
4998             }
4999         }
5000     }
5001 
5002     /**
5003      * Sets the volume index of {@link AudioManager#STREAM_MUSIC}. Rescales the input volume index
5004      * from HDMI-CEC volume range to STREAM_MUSIC's.
5005      */
setStreamMusicVolume(int volume, int flags)5006     void setStreamMusicVolume(int volume, int flags) {
5007         getAudioManager().setStreamVolume(AudioManager.STREAM_MUSIC,
5008                 volume * mStreamMusicMaxVolume / AudioStatus.MAX_VOLUME, flags);
5009     }
5010 
initializeEarc(int initiatedBy)5011     private void initializeEarc(int initiatedBy) {
5012         Slog.i(TAG, "eARC initialized, reason = " + initiatedBy);
5013         initializeEarcLocalDevice(initiatedBy);
5014 
5015         if (initiatedBy == INITIATED_BY_ENABLE_EARC) {
5016             // Since ARC and eARC cannot be connected simultaneously, we need to terminate ARC
5017             // before even enabling eARC.
5018             setEarcEnabledInHal(true, true);
5019         } else {
5020             // On boot, wake-up, and hotplug in, eARC will always be attempted before ARC.
5021             // So there is no need to explicitly terminate ARC before enabling eARC.
5022             setEarcEnabledInHal(true, false);
5023         }
5024     }
5025 
5026     @ServiceThreadOnly
5027     @VisibleForTesting
initializeEarcLocalDevice(final int initiatedBy)5028     protected void initializeEarcLocalDevice(final int initiatedBy) {
5029         // TODO remove initiatedBy argument if it stays unused
5030         assertRunOnServiceThread();
5031         if (mEarcLocalDevice == null) {
5032             mEarcLocalDevice = HdmiEarcLocalDevice.create(this, HdmiDeviceInfo.DEVICE_TV);
5033         }
5034         // TODO create HdmiEarcLocalDeviceRx if we're an audio system device.
5035     }
5036 
5037     @ServiceThreadOnly
5038     @VisibleForTesting
setEarcEnabled(@dmiControlManager.EarcFeature int enabled)5039     protected void setEarcEnabled(@HdmiControlManager.EarcFeature int enabled) {
5040         assertRunOnServiceThread();
5041         synchronized (mLock) {
5042             mEarcEnabled = (enabled == EARC_FEATURE_ENABLED);
5043 
5044             if (!isEarcSupported()) {
5045                 Slog.i(TAG, "Enabled/disabled eARC setting, but the hardware doesn´t support eARC. "
5046                         + "This settings change doesn´t have an effect.");
5047                 return;
5048             }
5049 
5050             if (mEarcEnabled) {
5051                 onEnableEarc();
5052                 return;
5053             }
5054         }
5055         runOnServiceThread(new Runnable() {
5056             @Override
5057             public void run() {
5058                 onDisableEarc();
5059             }
5060         });
5061     }
5062 
5063     @VisibleForTesting
setEarcSupported(boolean supported)5064     protected void setEarcSupported(boolean supported) {
5065         synchronized (mLock) {
5066             mEarcSupported = supported;
5067         }
5068     }
5069 
5070     @ServiceThreadOnly
onEnableEarc()5071     private void onEnableEarc() {
5072         // This will terminate ARC as well.
5073         initializeEarc(INITIATED_BY_ENABLE_EARC);
5074     }
5075 
5076     @ServiceThreadOnly
onDisableEarc()5077     private void onDisableEarc() {
5078         disableEarcLocalDevice();
5079         setEarcEnabledInHal(false, false);
5080         clearEarcLocalDevice();
5081     }
5082 
5083     @ServiceThreadOnly
5084     @VisibleForTesting
clearEarcLocalDevice()5085     protected void clearEarcLocalDevice() {
5086         assertRunOnServiceThread();
5087         mEarcLocalDevice = null;
5088     }
5089 
5090     @ServiceThreadOnly
5091     @VisibleForTesting
addEarcLocalDevice(HdmiEarcLocalDevice localDevice)5092     protected void addEarcLocalDevice(HdmiEarcLocalDevice localDevice) {
5093         assertRunOnServiceThread();
5094         mEarcLocalDevice = localDevice;
5095     }
5096 
5097     @ServiceThreadOnly
getEarcStatus()5098     private int getEarcStatus() {
5099         assertRunOnServiceThread();
5100         if (mEarcLocalDevice != null) {
5101             synchronized (mLock) {
5102                 return mEarcLocalDevice.mEarcStatus;
5103             }
5104         }
5105         return HDMI_EARC_STATUS_UNKNOWN;
5106     }
5107 
5108     @ServiceThreadOnly
5109     @VisibleForTesting
getEarcLocalDevice()5110     HdmiEarcLocalDevice getEarcLocalDevice() {
5111         assertRunOnServiceThread();
5112         return mEarcLocalDevice;
5113     }
5114 
disableEarcLocalDevice()5115     private void disableEarcLocalDevice() {
5116         if (mEarcLocalDevice == null) {
5117             return;
5118         }
5119         mEarcLocalDevice.disableDevice();
5120     }
5121 
5122     @ServiceThreadOnly
5123     @VisibleForTesting
setEarcEnabledInHal(boolean enabled, boolean terminateArcFirst)5124     protected void setEarcEnabledInHal(boolean enabled, boolean terminateArcFirst) {
5125         assertRunOnServiceThread();
5126         if (terminateArcFirst) {
5127             startArcAction(false, new IHdmiControlCallback.Stub() {
5128                 @Override
5129                 public void onComplete(int result) throws RemoteException {
5130                     if (result != HdmiControlManager.RESULT_SUCCESS) {
5131                         Slog.w(TAG,
5132                                 "ARC termination before enabling eARC in the HAL failed with "
5133                                         + "result: " + result);
5134                     }
5135                     // Independently of the result (i.e. independently of whether the ARC RX device
5136                     // responded with <Terminate ARC> or not), we always end up terminating ARC in
5137                     // the HAL. As soon as we do that, we can enable eARC in the HAL.
5138                     mEarcController.setEarcEnabled(enabled);
5139                     mCecController.setHpdSignalType(
5140                             enabled ? Constants.HDMI_HPD_TYPE_STATUS_BIT
5141                                     : Constants.HDMI_HPD_TYPE_PHYSICAL,
5142                             mEarcPortId);
5143                 }
5144             });
5145         } else {
5146             mEarcController.setEarcEnabled(enabled);
5147             mCecController.setHpdSignalType(
5148                     enabled ? Constants.HDMI_HPD_TYPE_STATUS_BIT : Constants.HDMI_HPD_TYPE_PHYSICAL,
5149                     mEarcPortId);
5150         }
5151     }
5152 
5153     @ServiceThreadOnly
handleEarcStateChange(int status, int portId)5154     void handleEarcStateChange(int status, int portId) {
5155         assertRunOnServiceThread();
5156         int oldEarcStatus = getEarcStatus();
5157         if (!getPortInfo(portId).isEarcSupported()) {
5158             Slog.w(TAG, "Tried to update eARC status on a port that doesn't support eARC.");
5159             getAtomWriter().earcStatusChanged(isEarcSupported(), isEarcEnabled(), oldEarcStatus,
5160                     status, HdmiStatsEnums.LOG_REASON_EARC_STATUS_CHANGED_UNSUPPORTED_PORT);
5161             return;
5162         }
5163         if (mEarcLocalDevice != null) {
5164             mEarcLocalDevice.handleEarcStateChange(status);
5165             getAtomWriter().earcStatusChanged(isEarcSupported(), isEarcEnabled(),
5166                     oldEarcStatus, status, HdmiStatsEnums.LOG_REASON_EARC_STATUS_CHANGED);
5167         } else if (status == HDMI_EARC_STATUS_ARC_PENDING) {
5168             // If eARC is disabled, the local device is null. This is why we notify
5169             // AudioService here that the eARC connection is terminated.
5170             HdmiLogger.debug("eARC state change [new: HDMI_EARC_STATUS_ARC_PENDING(2)]");
5171             notifyEarcStatusToAudioService(false, new ArrayList<>());
5172             mHandler.postDelayed( new Runnable() {
5173                 @Override
5174                 public void run() {
5175                     startArcAction(true, null);
5176                 }
5177             }, EARC_TRIGGER_START_ARC_ACTION_DELAY);
5178             getAtomWriter().earcStatusChanged(isEarcSupported(), isEarcEnabled(),
5179                     oldEarcStatus, status, HdmiStatsEnums.LOG_REASON_EARC_STATUS_CHANGED);
5180         } else {
5181             getAtomWriter().earcStatusChanged(isEarcSupported(), isEarcEnabled(),
5182                     oldEarcStatus, status,
5183                     HdmiStatsEnums.LOG_REASON_EARC_STATUS_CHANGED_WRONG_STATE);
5184         }
5185     }
5186 
notifyEarcStatusToAudioService( boolean enabled, List<AudioDescriptor> audioDescriptors)5187     protected void notifyEarcStatusToAudioService(
5188             boolean enabled, List<AudioDescriptor> audioDescriptors) {
5189         AudioDeviceAttributes attributes = new AudioDeviceAttributes(
5190                 AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_HDMI_EARC, "", "",
5191                 new ArrayList<AudioProfile>(), audioDescriptors);
5192         // Set SAM to ON whenever CEC is disabled. Failure to do so may result in the absence
5193         // of sound when CEC is disabled and eARC is enabled due to SAM being in the off state.
5194         if (!isCecControlEnabled()) {
5195             setSystemAudioActivated(true);
5196         }
5197         getAudioManager().setWiredDeviceConnectionState(attributes, enabled ? 1 : 0);
5198     }
5199 
5200     @ServiceThreadOnly
handleEarcCapabilitiesReported(byte[] rawCapabilities, int portId)5201     void handleEarcCapabilitiesReported(byte[] rawCapabilities, int portId) {
5202         assertRunOnServiceThread();
5203         if (!getPortInfo(portId).isEarcSupported()) {
5204             Slog.w(TAG,
5205                     "Tried to process eARC capabilities from a port that doesn't support eARC.");
5206             return;
5207         }
5208         // If eARC is disabled, the local device is null. In this case, the HAL shouldn't have
5209         // reported eARC capabilities, but even if it did, it won't take effect.
5210         if (mEarcLocalDevice != null) {
5211             mEarcLocalDevice.handleEarcCapabilitiesReported(rawCapabilities);
5212         }
5213     }
5214 
earcBlocksArcConnection()5215     protected boolean earcBlocksArcConnection() {
5216         if (mEarcLocalDevice == null) {
5217             return false;
5218         }
5219         synchronized (mLock) {
5220             return mEarcLocalDevice.mEarcStatus != HDMI_EARC_STATUS_ARC_PENDING;
5221         }
5222     }
5223 
startArcAction(boolean enabled, IHdmiControlCallback callback)5224     protected void startArcAction(boolean enabled, IHdmiControlCallback callback) {
5225         if (!isTvDeviceEnabled()) {
5226             invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
5227         } else {
5228             tv().startArcAction(enabled, callback);
5229         }
5230     }
5231 
isHdmiControlEnhancedBehaviorFlagEnabled()5232     protected boolean isHdmiControlEnhancedBehaviorFlagEnabled() {
5233         return hdmiControlEnhancedBehavior();
5234     }
5235 
5236     /**
5237      * Reads the property value that decides whether CEC should be disabled on standby when the low
5238      * energy mode option is used.
5239      */
5240     @VisibleForTesting
getDisableCecOnStandbyByLowEnergyMode()5241     protected boolean getDisableCecOnStandbyByLowEnergyMode() {
5242         return SystemProperties.getBoolean(
5243                 Constants.PROPERTY_DISABLE_CEC_ON_STANDBY_IN_LOW_ENERGY_MODE, false);
5244     }
5245 
5246     /**
5247      * Reads the property that checks if CEC was disabled on standby by low energy mode.
5248      */
5249     @VisibleForTesting
getWasCecDisabledOnStandbyByLowEnergyMode()5250     protected boolean getWasCecDisabledOnStandbyByLowEnergyMode() {
5251         return SystemProperties.getBoolean(
5252                 Constants.PROPERTY_WAS_CEC_DISABLED_ON_STANDBY_BY_LOW_ENERGY_MODE, false);
5253     }
5254 
5255     /**
5256      * Sets the truth value of the property that checks if CEC was disabled on standby by low energy
5257      * mode.
5258      */
5259     @VisibleForTesting
setWasCecDisabledOnStandbyByLowEnergyMode(boolean value)5260     protected void setWasCecDisabledOnStandbyByLowEnergyMode(boolean value) {
5261         writeStringSystemProperty(
5262                 Constants.PROPERTY_WAS_CEC_DISABLED_ON_STANDBY_BY_LOW_ENERGY_MODE,
5263                 String.valueOf(value));
5264     }
5265 
5266     /**
5267      * Writes a HdmiPowerStateChangeOnActiveSourceLostToggled atom representing a
5268      * HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST setting change.
5269      */
writePowerStateChangeOnActiveSourceLostAtom(boolean isSettingEnabled)5270     protected void writePowerStateChangeOnActiveSourceLostAtom(boolean isSettingEnabled) {
5271         String manufacturerPnpId = "undefined";
5272         int manufactureYear = -1;
5273         int manufactureWeek = -1;
5274         Display display = getContext().getDisplay();
5275         if (display != null) {
5276             DeviceProductInfo deviceProductInfo = display.getDeviceProductInfo();
5277             manufacturerPnpId = deviceProductInfo.getManufacturerPnpId();
5278             manufactureYear = deviceProductInfo.getManufactureYear();
5279         }
5280         int enumLogReason =
5281                 HdmiStatsEnums.LOG_REASON_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_TOGGLE_UNKNOWN;
5282         if (playback() != null) {
5283             if (playback().isActiveSourceLostPopupLaunched()) {
5284                 enumLogReason = HdmiStatsEnums.LOG_REASON_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_TOGGLE_POP_UP;
5285             } else {
5286                 enumLogReason = HdmiStatsEnums.LOG_REASON_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_TOGGLE_SETTING;
5287             }
5288         }
5289 
5290         getAtomWriter().powerStateChangeOnActiveSourceLostChanged(isSettingEnabled, enumLogReason,
5291                 manufacturerPnpId, manufactureYear, manufactureWeek);
5292     }
5293 
5294     /**
5295      * Reads the property that checks if CEC was enabled by the user while in offline mode such that
5296      * it won't be disabled when going to sleep by low energy mode.
5297      */
5298     @VisibleForTesting
userEnabledCecInOfflineMode()5299     protected boolean userEnabledCecInOfflineMode() {
5300         return SystemProperties.getBoolean(
5301                 Constants.PROPERTY_USER_ACTION_KEEP_CEC_ENABLED_IN_OFFLINE_MODE, false);
5302     }
5303 }
5304