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