HdmiControlService.java revision 26ba7fddcaeb052710ca8672889830dabcbfd3ac
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 17package com.android.server.hdmi; 18 19import android.annotation.Nullable; 20import android.content.BroadcastReceiver; 21import android.content.ContentResolver; 22import android.content.Context; 23import android.content.Intent; 24import android.content.IntentFilter; 25import android.hardware.hdmi.HdmiCecDeviceInfo; 26import android.hardware.hdmi.HdmiControlManager; 27import android.hardware.hdmi.HdmiHotplugEvent; 28import android.hardware.hdmi.HdmiPortInfo; 29import android.hardware.hdmi.HdmiTvClient; 30import android.hardware.hdmi.IHdmiControlCallback; 31import android.hardware.hdmi.IHdmiControlService; 32import android.hardware.hdmi.IHdmiDeviceEventListener; 33import android.hardware.hdmi.IHdmiHotplugEventListener; 34import android.hardware.hdmi.IHdmiInputChangeListener; 35import android.hardware.hdmi.IHdmiRecordListener; 36import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener; 37import android.hardware.hdmi.IHdmiVendorCommandListener; 38import android.media.AudioManager; 39import android.os.Build; 40import android.os.Handler; 41import android.os.HandlerThread; 42import android.os.IBinder; 43import android.os.Looper; 44import android.os.PowerManager; 45import android.os.RemoteException; 46import android.os.SystemClock; 47import android.provider.Settings.Global; 48import android.util.ArraySet; 49import android.util.Slog; 50import android.util.SparseArray; 51import android.util.SparseIntArray; 52 53import com.android.internal.annotations.GuardedBy; 54import com.android.server.SystemService; 55import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; 56import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback; 57import com.android.server.hdmi.HdmiCecLocalDevice.PendingActionClearedCallback; 58 59import libcore.util.EmptyArray; 60 61import java.util.ArrayList; 62import java.util.Arrays; 63import java.util.Collections; 64import java.util.List; 65 66/** 67 * Provides a service for sending and processing HDMI control messages, 68 * HDMI-CEC and MHL control command, and providing the information on both standard. 69 */ 70public final class HdmiControlService extends SystemService { 71 private static final String TAG = "HdmiControlService"; 72 73 static final String PERMISSION = "android.permission.HDMI_CEC"; 74 75 /** 76 * Interface to report send result. 77 */ 78 interface SendMessageCallback { 79 /** 80 * Called when {@link HdmiControlService#sendCecCommand} is completed. 81 * 82 * @param error result of send request. 83 * <ul> 84 * <li>{@link Constants#SEND_RESULT_SUCCESS} 85 * <li>{@link Constants#SEND_RESULT_NAK} 86 * <li>{@link Constants#SEND_RESULT_FAILURE} 87 * </ul> 88 */ 89 void onSendCompleted(int error); 90 } 91 92 /** 93 * Interface to get a list of available logical devices. 94 */ 95 interface DevicePollingCallback { 96 /** 97 * Called when device polling is finished. 98 * 99 * @param ackedAddress a list of logical addresses of available devices 100 */ 101 void onPollingFinished(List<Integer> ackedAddress); 102 } 103 104 private class PowerStateReceiver extends BroadcastReceiver { 105 @Override 106 public void onReceive(Context context, Intent intent) { 107 switch (intent.getAction()) { 108 case Intent.ACTION_SCREEN_OFF: 109 if (isPowerOnOrTransient()) { 110 onStandby(); 111 } 112 break; 113 case Intent.ACTION_SCREEN_ON: 114 if (isPowerStandbyOrTransient()) { 115 onWakeUp(); 116 } 117 break; 118 } 119 } 120 } 121 122 // A thread to handle synchronous IO of CEC and MHL control service. 123 // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms) 124 // and sparse call it shares a thread to handle IO operations. 125 private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread"); 126 127 // Used to synchronize the access to the service. 128 private final Object mLock = new Object(); 129 130 // Type of logical devices hosted in the system. Stored in the unmodifiable list. 131 private final List<Integer> mLocalDevices; 132 133 // List of listeners registered by callers that want to get notified of 134 // hotplug events. 135 @GuardedBy("mLock") 136 private final ArrayList<IHdmiHotplugEventListener> mHotplugEventListeners = new ArrayList<>(); 137 138 // List of records for hotplug event listener to handle the the caller killed in action. 139 @GuardedBy("mLock") 140 private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords = 141 new ArrayList<>(); 142 143 // List of listeners registered by callers that want to get notified of 144 // device status events. 145 @GuardedBy("mLock") 146 private final ArrayList<IHdmiDeviceEventListener> mDeviceEventListeners = new ArrayList<>(); 147 148 // List of records for device event listener to handle the the caller killed in action. 149 @GuardedBy("mLock") 150 private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords = 151 new ArrayList<>(); 152 153 // List of records for vendor command listener to handle the the caller killed in action. 154 @GuardedBy("mLock") 155 private final ArrayList<VendorCommandListenerRecord> mVendorCommandListenerRecords = 156 new ArrayList<>(); 157 158 @GuardedBy("mLock") 159 private IHdmiInputChangeListener mInputChangeListener; 160 161 @GuardedBy("mLock") 162 private InputChangeListenerRecord mInputChangeListenerRecord; 163 164 @GuardedBy("mLock") 165 private IHdmiRecordListener mRecordListener; 166 167 @GuardedBy("mLock") 168 private HdmiRecordListenerRecord mRecordListenerRecord; 169 170 // Set to true while HDMI control is enabled. If set to false, HDMI-CEC/MHL protocol 171 // handling will be disabled and no request will be handled. 172 @GuardedBy("mLock") 173 private boolean mHdmiControlEnabled; 174 175 // Set to true while the service is in normal mode. While set to false, no input change is 176 // allowed. Used for situations where input change can confuse users such as channel auto-scan, 177 // system upgrade, etc., a.k.a. "prohibit mode". 178 @GuardedBy("mLock") 179 private boolean mProhibitMode; 180 181 // List of listeners registered by callers that want to get notified of 182 // system audio mode changes. 183 private final ArrayList<IHdmiSystemAudioModeChangeListener> 184 mSystemAudioModeChangeListeners = new ArrayList<>(); 185 // List of records for system audio mode change to handle the the caller killed in action. 186 private final ArrayList<SystemAudioModeChangeListenerRecord> 187 mSystemAudioModeChangeListenerRecords = new ArrayList<>(); 188 189 // Handler used to run a task in service thread. 190 private final Handler mHandler = new Handler(); 191 192 @Nullable 193 private HdmiCecController mCecController; 194 195 @Nullable 196 private HdmiMhlController mMhlController; 197 198 // HDMI port information. Stored in the unmodifiable list to keep the static information 199 // from being modified. 200 private List<HdmiPortInfo> mPortInfo; 201 202 // Map from path(physical address) to port ID. 203 private final SparseIntArray mPortIdMap = new SparseIntArray(); 204 205 // Map from port ID to HdmiPortInfo. 206 private final SparseArray<HdmiPortInfo> mPortInfoMap = new SparseArray<>(); 207 208 private HdmiCecMessageValidator mMessageValidator; 209 210 private final PowerStateReceiver mPowerStateReceiver = new PowerStateReceiver(); 211 212 @ServiceThreadOnly 213 private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY; 214 215 @ServiceThreadOnly 216 private boolean mStandbyMessageReceived = false; 217 218 public HdmiControlService(Context context) { 219 super(context); 220 mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray( 221 com.android.internal.R.array.config_hdmiCecLogicalDeviceType)); 222 } 223 224 @Override 225 public void onStart() { 226 mIoThread.start(); 227 mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON; 228 mProhibitMode = false; 229 mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true); 230 231 mCecController = HdmiCecController.create(this); 232 if (mCecController != null) { 233 // TODO: Remove this as soon as OEM's HAL implementation is corrected. 234 mCecController.setOption(HdmiTvClient.OPTION_CEC_ENABLE, 235 HdmiTvClient.ENABLED); 236 237 // TODO: load value for mHdmiControlEnabled from preference. 238 if (mHdmiControlEnabled) { 239 initializeCec(true); 240 } 241 } else { 242 Slog.i(TAG, "Device does not support HDMI-CEC."); 243 } 244 245 mMhlController = HdmiMhlController.create(this); 246 if (mMhlController == null) { 247 Slog.i(TAG, "Device does not support MHL-control."); 248 } 249 initPortInfo(); 250 mMessageValidator = new HdmiCecMessageValidator(this); 251 publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService()); 252 253 // Register broadcast receiver for power state change. 254 if (mCecController != null || mMhlController != null) { 255 IntentFilter filter = new IntentFilter(); 256 filter.addAction(Intent.ACTION_SCREEN_OFF); 257 filter.addAction(Intent.ACTION_SCREEN_ON); 258 getContext().registerReceiver(mPowerStateReceiver, filter); 259 } 260 } 261 262 boolean readBooleanSetting(String key, boolean defVal) { 263 ContentResolver cr = getContext().getContentResolver(); 264 return Global.getInt(cr, key, defVal ? Constants.TRUE : Constants.FALSE) == Constants.TRUE; 265 } 266 267 void writeBooleanSetting(String key, boolean value) { 268 ContentResolver cr = getContext().getContentResolver(); 269 Global.putInt(cr, key, value ? Constants.TRUE : Constants.FALSE); 270 } 271 272 private void initializeCec(boolean fromBootup) { 273 mCecController.setOption(HdmiTvClient.OPTION_CEC_SERVICE_CONTROL, 274 HdmiTvClient.ENABLED); 275 initializeLocalDevices(mLocalDevices, fromBootup); 276 } 277 278 @ServiceThreadOnly 279 private void initializeLocalDevices(final List<Integer> deviceTypes, final boolean fromBootup) { 280 assertRunOnServiceThread(); 281 // A container for [Logical Address, Local device info]. 282 final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>(); 283 final SparseIntArray finished = new SparseIntArray(); 284 mCecController.clearLogicalAddress(); 285 for (int type : deviceTypes) { 286 final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type); 287 localDevice.init(); 288 mCecController.allocateLogicalAddress(type, 289 localDevice.getPreferredAddress(), new AllocateAddressCallback() { 290 @Override 291 public void onAllocated(int deviceType, int logicalAddress) { 292 if (logicalAddress == Constants.ADDR_UNREGISTERED) { 293 Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]"); 294 } else { 295 HdmiCecDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType); 296 localDevice.setDeviceInfo(deviceInfo); 297 mCecController.addLocalDevice(deviceType, localDevice); 298 mCecController.addLogicalAddress(logicalAddress); 299 devices.append(logicalAddress, localDevice); 300 } 301 finished.append(deviceType, logicalAddress); 302 303 // Address allocation completed for all devices. Notify each device. 304 if (deviceTypes.size() == finished.size()) { 305 if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) { 306 mPowerStatus = HdmiControlManager.POWER_STATUS_ON; 307 } 308 notifyAddressAllocated(devices, fromBootup); 309 } 310 } 311 }); 312 } 313 } 314 315 @ServiceThreadOnly 316 private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices, 317 boolean fromBootup) { 318 assertRunOnServiceThread(); 319 for (int i = 0; i < devices.size(); ++i) { 320 int address = devices.keyAt(i); 321 HdmiCecLocalDevice device = devices.valueAt(i); 322 device.handleAddressAllocated(address, fromBootup); 323 } 324 } 325 326 // Initialize HDMI port information. Combine the information from CEC and MHL HAL and 327 // keep them in one place. 328 @ServiceThreadOnly 329 private void initPortInfo() { 330 assertRunOnServiceThread(); 331 HdmiPortInfo[] cecPortInfo = null; 332 333 // CEC HAL provides majority of the info while MHL does only MHL support flag for 334 // each port. Return empty array if CEC HAL didn't provide the info. 335 if (mCecController != null) { 336 cecPortInfo = mCecController.getPortInfos(); 337 } 338 if (cecPortInfo == null) { 339 return; 340 } 341 342 for (HdmiPortInfo info : cecPortInfo) { 343 mPortIdMap.put(info.getAddress(), info.getId()); 344 mPortInfoMap.put(info.getId(), info); 345 } 346 347 if (mMhlController == null) { 348 mPortInfo = Collections.unmodifiableList(Arrays.asList(cecPortInfo)); 349 return; 350 } else { 351 HdmiPortInfo[] mhlPortInfo = mMhlController.getPortInfos(); 352 ArraySet<Integer> mhlSupportedPorts = new ArraySet<Integer>(mhlPortInfo.length); 353 for (HdmiPortInfo info : mhlPortInfo) { 354 if (info.isMhlSupported()) { 355 mhlSupportedPorts.add(info.getId()); 356 } 357 } 358 359 // Build HDMI port info list with CEC port info plus MHL supported flag. 360 ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length); 361 for (HdmiPortInfo info : cecPortInfo) { 362 if (mhlSupportedPorts.contains(info.getId())) { 363 result.add(new HdmiPortInfo(info.getId(), info.getType(), info.getAddress(), 364 info.isCecSupported(), true, info.isArcSupported())); 365 } else { 366 result.add(info); 367 } 368 } 369 mPortInfo = Collections.unmodifiableList(result); 370 } 371 } 372 373 /** 374 * Returns HDMI port information for the given port id. 375 * 376 * @param portId HDMI port id 377 * @return {@link HdmiPortInfo} for the given port 378 */ 379 @ServiceThreadOnly 380 HdmiPortInfo getPortInfo(int portId) { 381 assertRunOnServiceThread(); 382 return mPortInfoMap.get(portId, null); 383 } 384 385 /** 386 * Returns the routing path (physical address) of the HDMI port for the given 387 * port id. 388 */ 389 @ServiceThreadOnly 390 int portIdToPath(int portId) { 391 assertRunOnServiceThread(); 392 HdmiPortInfo portInfo = getPortInfo(portId); 393 if (portInfo == null) { 394 Slog.e(TAG, "Cannot find the port info: " + portId); 395 return Constants.INVALID_PHYSICAL_ADDRESS; 396 } 397 return portInfo.getAddress(); 398 } 399 400 /** 401 * Returns the id of HDMI port located at the top of the hierarchy of 402 * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance, 403 * the port id to be returned is the ID associated with the port address 404 * 0x1000 (1.0.0.0) which is the topmost path of the given routing path. 405 */ 406 @ServiceThreadOnly 407 int pathToPortId(int path) { 408 assertRunOnServiceThread(); 409 int portAddress = path & Constants.ROUTING_PATH_TOP_MASK; 410 return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID); 411 } 412 413 @ServiceThreadOnly 414 boolean isValidPortId(int portId) { 415 assertRunOnServiceThread(); 416 return getPortInfo(portId) != null; 417 } 418 419 /** 420 * Returns {@link Looper} for IO operation. 421 * 422 * <p>Declared as package-private. 423 */ 424 Looper getIoLooper() { 425 return mIoThread.getLooper(); 426 } 427 428 /** 429 * Returns {@link Looper} of main thread. Use this {@link Looper} instance 430 * for tasks that are running on main service thread. 431 * 432 * <p>Declared as package-private. 433 */ 434 Looper getServiceLooper() { 435 return mHandler.getLooper(); 436 } 437 438 /** 439 * Returns physical address of the device. 440 */ 441 int getPhysicalAddress() { 442 return mCecController.getPhysicalAddress(); 443 } 444 445 /** 446 * Returns vendor id of CEC service. 447 */ 448 int getVendorId() { 449 return mCecController.getVendorId(); 450 } 451 452 @ServiceThreadOnly 453 HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) { 454 assertRunOnServiceThread(); 455 HdmiCecLocalDeviceTv tv = tv(); 456 if (tv == null) { 457 return null; 458 } 459 return tv.getDeviceInfo(logicalAddress); 460 } 461 462 /** 463 * Returns version of CEC. 464 */ 465 int getCecVersion() { 466 return mCecController.getVersion(); 467 } 468 469 /** 470 * Whether a device of the specified physical address is connected to ARC enabled port. 471 */ 472 @ServiceThreadOnly 473 boolean isConnectedToArcPort(int physicalAddress) { 474 assertRunOnServiceThread(); 475 int portId = mPortIdMap.get(physicalAddress); 476 if (portId != Constants.INVALID_PORT_ID) { 477 return mPortInfoMap.get(portId).isArcSupported(); 478 } 479 return false; 480 } 481 482 void runOnServiceThread(Runnable runnable) { 483 mHandler.post(runnable); 484 } 485 486 void runOnServiceThreadAtFrontOfQueue(Runnable runnable) { 487 mHandler.postAtFrontOfQueue(runnable); 488 } 489 490 private void assertRunOnServiceThread() { 491 if (Looper.myLooper() != mHandler.getLooper()) { 492 throw new IllegalStateException("Should run on service thread."); 493 } 494 } 495 496 /** 497 * Transmit a CEC command to CEC bus. 498 * 499 * @param command CEC command to send out 500 * @param callback interface used to the result of send command 501 */ 502 @ServiceThreadOnly 503 void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) { 504 assertRunOnServiceThread(); 505 mCecController.sendCommand(command, callback); 506 } 507 508 @ServiceThreadOnly 509 void sendCecCommand(HdmiCecMessage command) { 510 assertRunOnServiceThread(); 511 mCecController.sendCommand(command, null); 512 } 513 514 @ServiceThreadOnly 515 boolean handleCecCommand(HdmiCecMessage message) { 516 assertRunOnServiceThread(); 517 if (!mMessageValidator.isValid(message)) { 518 return false; 519 } 520 return dispatchMessageToLocalDevice(message); 521 } 522 523 void setAudioReturnChannel(boolean enabled) { 524 mCecController.setAudioReturnChannel(enabled); 525 } 526 527 @ServiceThreadOnly 528 private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) { 529 assertRunOnServiceThread(); 530 for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { 531 if (device.dispatchMessage(message) 532 && message.getDestination() != Constants.ADDR_BROADCAST) { 533 return true; 534 } 535 } 536 537 if (message.getDestination() != Constants.ADDR_BROADCAST) { 538 Slog.w(TAG, "Unhandled cec command:" + message); 539 } 540 return false; 541 } 542 543 /** 544 * Called when a new hotplug event is issued. 545 * 546 * @param portNo hdmi port number where hot plug event issued. 547 * @param connected whether to be plugged in or not 548 */ 549 @ServiceThreadOnly 550 void onHotplug(int portNo, boolean connected) { 551 assertRunOnServiceThread(); 552 for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { 553 device.onHotplug(portNo, connected); 554 } 555 announceHotplugEvent(portNo, connected); 556 } 557 558 /** 559 * Poll all remote devices. It sends <Polling Message> to all remote 560 * devices. 561 * 562 * @param callback an interface used to get a list of all remote devices' address 563 * @param sourceAddress a logical address of source device where sends polling message 564 * @param pickStrategy strategy how to pick polling candidates 565 * @param retryCount the number of retry used to send polling message to remote devices 566 * @throw IllegalArgumentException if {@code pickStrategy} is invalid value 567 */ 568 @ServiceThreadOnly 569 void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy, 570 int retryCount) { 571 assertRunOnServiceThread(); 572 mCecController.pollDevices(callback, sourceAddress, checkPollStrategy(pickStrategy), 573 retryCount); 574 } 575 576 private int checkPollStrategy(int pickStrategy) { 577 int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK; 578 if (strategy == 0) { 579 throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy); 580 } 581 int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK; 582 if (iterationStrategy == 0) { 583 throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy); 584 } 585 return strategy | iterationStrategy; 586 } 587 588 List<HdmiCecLocalDevice> getAllLocalDevices() { 589 assertRunOnServiceThread(); 590 return mCecController.getLocalDeviceList(); 591 } 592 593 Object getServiceLock() { 594 return mLock; 595 } 596 597 void setAudioStatus(boolean mute, int volume) { 598 AudioManager audioManager = getAudioManager(); 599 boolean muted = audioManager.isStreamMute(AudioManager.STREAM_MUSIC); 600 if (mute) { 601 if (!muted) { 602 audioManager.setStreamMute(AudioManager.STREAM_MUSIC, true); 603 } 604 } else { 605 if (muted) { 606 audioManager.setStreamMute(AudioManager.STREAM_MUSIC, false); 607 } 608 // FLAG_HDMI_SYSTEM_AUDIO_VOLUME prevents audio manager from announcing 609 // volume change notification back to hdmi control service. 610 audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 611 AudioManager.FLAG_SHOW_UI | 612 AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME); 613 } 614 } 615 616 void announceSystemAudioModeChange(boolean enabled) { 617 for (IHdmiSystemAudioModeChangeListener listener : mSystemAudioModeChangeListeners) { 618 invokeSystemAudioModeChange(listener, enabled); 619 } 620 } 621 622 private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) { 623 // TODO: find better name instead of model name. 624 String displayName = Build.MODEL; 625 return new HdmiCecDeviceInfo(logicalAddress, 626 getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType, 627 getVendorId(), displayName); 628 } 629 630 // Record class that monitors the event of the caller of being killed. Used to clean up 631 // the listener list and record list accordingly. 632 private final class HotplugEventListenerRecord implements IBinder.DeathRecipient { 633 private final IHdmiHotplugEventListener mListener; 634 635 public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) { 636 mListener = listener; 637 } 638 639 @Override 640 public void binderDied() { 641 synchronized (mLock) { 642 mHotplugEventListenerRecords.remove(this); 643 mHotplugEventListeners.remove(mListener); 644 } 645 } 646 } 647 648 private final class DeviceEventListenerRecord implements IBinder.DeathRecipient { 649 private final IHdmiDeviceEventListener mListener; 650 651 public DeviceEventListenerRecord(IHdmiDeviceEventListener listener) { 652 mListener = listener; 653 } 654 655 @Override 656 public void binderDied() { 657 synchronized (mLock) { 658 mDeviceEventListenerRecords.remove(this); 659 mDeviceEventListeners.remove(mListener); 660 } 661 } 662 } 663 664 private final class SystemAudioModeChangeListenerRecord implements IBinder.DeathRecipient { 665 private final IHdmiSystemAudioModeChangeListener mListener; 666 667 public SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener) { 668 mListener = listener; 669 } 670 671 @Override 672 public void binderDied() { 673 synchronized (mLock) { 674 mSystemAudioModeChangeListenerRecords.remove(this); 675 mSystemAudioModeChangeListeners.remove(mListener); 676 } 677 } 678 } 679 680 class VendorCommandListenerRecord implements IBinder.DeathRecipient { 681 private final IHdmiVendorCommandListener mListener; 682 private final int mDeviceType; 683 684 public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) { 685 mListener = listener; 686 mDeviceType = deviceType; 687 } 688 689 @Override 690 public void binderDied() { 691 synchronized (mLock) { 692 mVendorCommandListenerRecords.remove(this); 693 } 694 } 695 } 696 697 private class HdmiRecordListenerRecord implements IBinder.DeathRecipient { 698 @Override 699 public void binderDied() { 700 synchronized (mLock) { 701 mRecordListener = null; 702 } 703 } 704 } 705 706 private void enforceAccessPermission() { 707 getContext().enforceCallingOrSelfPermission(PERMISSION, TAG); 708 } 709 710 private final class BinderService extends IHdmiControlService.Stub { 711 @Override 712 public int[] getSupportedTypes() { 713 enforceAccessPermission(); 714 // mLocalDevices is an unmodifiable list - no lock necesary. 715 int[] localDevices = new int[mLocalDevices.size()]; 716 for (int i = 0; i < localDevices.length; ++i) { 717 localDevices[i] = mLocalDevices.get(i); 718 } 719 return localDevices; 720 } 721 722 @Override 723 public void deviceSelect(final int logicalAddress, final IHdmiControlCallback callback) { 724 enforceAccessPermission(); 725 runOnServiceThread(new Runnable() { 726 @Override 727 public void run() { 728 if (callback == null) { 729 Slog.e(TAG, "Callback cannot be null"); 730 return; 731 } 732 HdmiCecLocalDeviceTv tv = tv(); 733 if (tv == null) { 734 Slog.w(TAG, "Local tv device not available"); 735 invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE); 736 return; 737 } 738 tv.deviceSelect(logicalAddress, callback); 739 } 740 }); 741 } 742 743 @Override 744 public void portSelect(final int portId, final IHdmiControlCallback callback) { 745 enforceAccessPermission(); 746 runOnServiceThread(new Runnable() { 747 @Override 748 public void run() { 749 if (callback == null) { 750 Slog.e(TAG, "Callback cannot be null"); 751 return; 752 } 753 HdmiCecLocalDeviceTv tv = tv(); 754 if (tv == null) { 755 Slog.w(TAG, "Local tv device not available"); 756 invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE); 757 return; 758 } 759 tv.doManualPortSwitching(portId, callback); 760 } 761 }); 762 } 763 764 @Override 765 public void sendKeyEvent(final int deviceType, final int keyCode, final boolean isPressed) { 766 enforceAccessPermission(); 767 runOnServiceThread(new Runnable() { 768 @Override 769 public void run() { 770 HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType); 771 if (localDevice == null) { 772 Slog.w(TAG, "Local device not available"); 773 return; 774 } 775 localDevice.sendKeyEvent(keyCode, isPressed); 776 } 777 }); 778 } 779 780 @Override 781 public void oneTouchPlay(final IHdmiControlCallback callback) { 782 enforceAccessPermission(); 783 runOnServiceThread(new Runnable() { 784 @Override 785 public void run() { 786 HdmiControlService.this.oneTouchPlay(callback); 787 } 788 }); 789 } 790 791 @Override 792 public void queryDisplayStatus(final IHdmiControlCallback callback) { 793 enforceAccessPermission(); 794 runOnServiceThread(new Runnable() { 795 @Override 796 public void run() { 797 HdmiControlService.this.queryDisplayStatus(callback); 798 } 799 }); 800 } 801 802 @Override 803 public void addHotplugEventListener(final IHdmiHotplugEventListener listener) { 804 enforceAccessPermission(); 805 runOnServiceThread(new Runnable() { 806 @Override 807 public void run() { 808 HdmiControlService.this.addHotplugEventListener(listener); 809 } 810 }); 811 } 812 813 @Override 814 public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) { 815 enforceAccessPermission(); 816 runOnServiceThread(new Runnable() { 817 @Override 818 public void run() { 819 HdmiControlService.this.removeHotplugEventListener(listener); 820 } 821 }); 822 } 823 824 @Override 825 public void addDeviceEventListener(final IHdmiDeviceEventListener listener) { 826 enforceAccessPermission(); 827 runOnServiceThread(new Runnable() { 828 @Override 829 public void run() { 830 HdmiControlService.this.addDeviceEventListener(listener); 831 } 832 }); 833 } 834 835 @Override 836 public List<HdmiPortInfo> getPortInfo() { 837 enforceAccessPermission(); 838 return mPortInfo; 839 } 840 841 @Override 842 public boolean canChangeSystemAudioMode() { 843 enforceAccessPermission(); 844 HdmiCecLocalDeviceTv tv = tv(); 845 if (tv == null) { 846 return false; 847 } 848 return tv.hasSystemAudioDevice(); 849 } 850 851 @Override 852 public boolean getSystemAudioMode() { 853 enforceAccessPermission(); 854 HdmiCecLocalDeviceTv tv = tv(); 855 if (tv == null) { 856 return false; 857 } 858 return tv.isSystemAudioActivated(); 859 } 860 861 @Override 862 public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) { 863 enforceAccessPermission(); 864 runOnServiceThread(new Runnable() { 865 @Override 866 public void run() { 867 HdmiCecLocalDeviceTv tv = tv(); 868 if (tv == null) { 869 Slog.w(TAG, "Local tv device not available"); 870 invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE); 871 return; 872 } 873 tv.changeSystemAudioMode(enabled, callback); 874 } 875 }); 876 } 877 878 @Override 879 public void addSystemAudioModeChangeListener( 880 final IHdmiSystemAudioModeChangeListener listener) { 881 enforceAccessPermission(); 882 HdmiControlService.this.addSystemAudioModeChangeListner(listener); 883 } 884 885 @Override 886 public void removeSystemAudioModeChangeListener( 887 final IHdmiSystemAudioModeChangeListener listener) { 888 enforceAccessPermission(); 889 HdmiControlService.this.removeSystemAudioModeChangeListener(listener); 890 } 891 892 @Override 893 public void setInputChangeListener(final IHdmiInputChangeListener listener) { 894 enforceAccessPermission(); 895 HdmiControlService.this.setInputChangeListener(listener); 896 } 897 898 @Override 899 public List<HdmiCecDeviceInfo> getInputDevices() { 900 enforceAccessPermission(); 901 // No need to hold the lock for obtaining TV device as the local device instance 902 // is preserved while the HDMI control is enabled. 903 HdmiCecLocalDeviceTv tv = tv(); 904 if (tv == null) { 905 return Collections.emptyList(); 906 } 907 return tv.getSafeExternalInputs(); 908 } 909 910 @Override 911 public void setControlEnabled(final boolean enabled) { 912 enforceAccessPermission(); 913 runOnServiceThread(new Runnable() { 914 @Override 915 public void run() { 916 handleHdmiControlStatusChanged(enabled); 917 918 } 919 }); 920 } 921 922 @Override 923 public void setSystemAudioVolume(final int oldIndex, final int newIndex, 924 final int maxIndex) { 925 enforceAccessPermission(); 926 runOnServiceThread(new Runnable() { 927 @Override 928 public void run() { 929 HdmiCecLocalDeviceTv tv = tv(); 930 if (tv == null) { 931 Slog.w(TAG, "Local tv device not available"); 932 return; 933 } 934 tv.changeVolume(oldIndex, newIndex - oldIndex, maxIndex); 935 } 936 }); 937 } 938 939 @Override 940 public void setSystemAudioMute(final boolean mute) { 941 enforceAccessPermission(); 942 runOnServiceThread(new Runnable() { 943 @Override 944 public void run() { 945 HdmiCecLocalDeviceTv tv = tv(); 946 if (tv == null) { 947 Slog.w(TAG, "Local tv device not available"); 948 return; 949 } 950 tv.changeMute(mute); 951 } 952 }); 953 } 954 955 @Override 956 public void setArcMode(final boolean enabled) { 957 enforceAccessPermission(); 958 runOnServiceThread(new Runnable() { 959 @Override 960 public void run() { 961 HdmiCecLocalDeviceTv tv = tv(); 962 if (tv == null) { 963 Slog.w(TAG, "Local tv device not available to change arc mode."); 964 return; 965 } 966 } 967 }); 968 } 969 970 @Override 971 public void setOption(final int key, final int value) { 972 enforceAccessPermission(); 973 if (!isTvDevice()) { 974 return; 975 } 976 switch (key) { 977 case HdmiTvClient.OPTION_CEC_AUTO_WAKEUP: 978 mCecController.setOption(key, value); 979 break; 980 case HdmiTvClient.OPTION_CEC_AUTO_DEVICE_OFF: 981 // No need to pass this option to HAL. 982 tv().setAutoDeviceOff(value == HdmiTvClient.ENABLED); 983 break; 984 case HdmiTvClient.OPTION_MHL_INPUT_SWITCHING: // Fall through 985 case HdmiTvClient.OPTION_MHL_POWER_CHARGE: 986 if (mMhlController != null) { 987 mMhlController.setOption(key, value); 988 } 989 break; 990 } 991 } 992 993 @Override 994 public void setProhibitMode(final boolean enabled) { 995 enforceAccessPermission(); 996 if (!isTvDevice()) { 997 return; 998 } 999 HdmiControlService.this.setProhibitMode(enabled); 1000 } 1001 1002 @Override 1003 public void addVendorCommandListener(final IHdmiVendorCommandListener listener, 1004 final int deviceType) { 1005 enforceAccessPermission(); 1006 runOnServiceThread(new Runnable() { 1007 @Override 1008 public void run() { 1009 HdmiControlService.this.addVendorCommandListener(listener, deviceType); 1010 } 1011 }); 1012 } 1013 1014 @Override 1015 public void sendVendorCommand(final int deviceType, final int targetAddress, 1016 final byte[] params, final boolean hasVendorId) { 1017 enforceAccessPermission(); 1018 runOnServiceThread(new Runnable() { 1019 @Override 1020 public void run() { 1021 HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType); 1022 if (device == null) { 1023 Slog.w(TAG, "Local device not available"); 1024 return; 1025 } 1026 if (hasVendorId) { 1027 sendCecCommand(HdmiCecMessageBuilder.buildVendorCommandWithId( 1028 device.getDeviceInfo().getLogicalAddress(), targetAddress, 1029 getVendorId(), params)); 1030 } else { 1031 sendCecCommand(HdmiCecMessageBuilder.buildVendorCommand( 1032 device.getDeviceInfo().getLogicalAddress(), targetAddress, params)); 1033 } 1034 } 1035 }); 1036 } 1037 1038 @Override 1039 public void setHdmiRecordListener(IHdmiRecordListener listener) { 1040 HdmiControlService.this.setHdmiRecordListener(listener); 1041 } 1042 1043 @Override 1044 public void startOneTouchRecord(final int recorderAddress, final byte[] recordSource) { 1045 runOnServiceThread(new Runnable() { 1046 @Override 1047 public void run() { 1048 if (!isTvDevice()) { 1049 Slog.w(TAG, "No TV is available."); 1050 return; 1051 } 1052 tv().startOneTouchRecord(recorderAddress, recordSource); 1053 } 1054 }); 1055 } 1056 1057 @Override 1058 public void stopOneTouchRecord(final int recorderAddress) { 1059 runOnServiceThread(new Runnable() { 1060 @Override 1061 public void run() { 1062 if (!isTvDevice()) { 1063 Slog.w(TAG, "No TV is available."); 1064 return; 1065 } 1066 tv().stopOneTouchRecord(recorderAddress); 1067 } 1068 }); 1069 } 1070 1071 @Override 1072 public void startTimerRecording(final int recorderAddress, final int sourceType, 1073 final byte[] recordSource) { 1074 runOnServiceThread(new Runnable() { 1075 @Override 1076 public void run() { 1077 if (!isTvDevice()) { 1078 Slog.w(TAG, "No TV is available."); 1079 return; 1080 } 1081 tv().startTimerRecording(recorderAddress, sourceType, recordSource); 1082 } 1083 }); 1084 } 1085 1086 @Override 1087 public void clearTimerRecording(final int recorderAddress, final int sourceType, 1088 final byte[] recordSource) { 1089 runOnServiceThread(new Runnable() { 1090 @Override 1091 public void run() { 1092 if (!isTvDevice()) { 1093 Slog.w(TAG, "No TV is available."); 1094 return; 1095 } 1096 tv().clearTimerRecording(recorderAddress, sourceType, recordSource); 1097 } 1098 }); 1099 } 1100 } 1101 1102 @ServiceThreadOnly 1103 private void oneTouchPlay(final IHdmiControlCallback callback) { 1104 assertRunOnServiceThread(); 1105 HdmiCecLocalDevicePlayback source = playback(); 1106 if (source == null) { 1107 Slog.w(TAG, "Local playback device not available"); 1108 invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE); 1109 return; 1110 } 1111 source.oneTouchPlay(callback); 1112 } 1113 1114 @ServiceThreadOnly 1115 private void queryDisplayStatus(final IHdmiControlCallback callback) { 1116 assertRunOnServiceThread(); 1117 HdmiCecLocalDevicePlayback source = playback(); 1118 if (source == null) { 1119 Slog.w(TAG, "Local playback device not available"); 1120 invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE); 1121 return; 1122 } 1123 source.queryDisplayStatus(callback); 1124 } 1125 1126 private void addHotplugEventListener(IHdmiHotplugEventListener listener) { 1127 HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener); 1128 try { 1129 listener.asBinder().linkToDeath(record, 0); 1130 } catch (RemoteException e) { 1131 Slog.w(TAG, "Listener already died"); 1132 return; 1133 } 1134 synchronized (mLock) { 1135 mHotplugEventListenerRecords.add(record); 1136 mHotplugEventListeners.add(listener); 1137 } 1138 } 1139 1140 private void removeHotplugEventListener(IHdmiHotplugEventListener listener) { 1141 synchronized (mLock) { 1142 for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) { 1143 if (record.mListener.asBinder() == listener.asBinder()) { 1144 listener.asBinder().unlinkToDeath(record, 0); 1145 mHotplugEventListenerRecords.remove(record); 1146 break; 1147 } 1148 } 1149 mHotplugEventListeners.remove(listener); 1150 } 1151 } 1152 1153 private void addDeviceEventListener(IHdmiDeviceEventListener listener) { 1154 DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener); 1155 try { 1156 listener.asBinder().linkToDeath(record, 0); 1157 } catch (RemoteException e) { 1158 Slog.w(TAG, "Listener already died"); 1159 return; 1160 } 1161 synchronized (mLock) { 1162 mDeviceEventListeners.add(listener); 1163 mDeviceEventListenerRecords.add(record); 1164 } 1165 } 1166 1167 void invokeDeviceEventListeners(HdmiCecDeviceInfo device, boolean activated) { 1168 synchronized (mLock) { 1169 for (IHdmiDeviceEventListener listener : mDeviceEventListeners) { 1170 try { 1171 listener.onStatusChanged(device, activated); 1172 } catch (RemoteException e) { 1173 Slog.e(TAG, "Failed to report device event:" + e); 1174 } 1175 } 1176 } 1177 } 1178 1179 private void addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener) { 1180 SystemAudioModeChangeListenerRecord record = new SystemAudioModeChangeListenerRecord( 1181 listener); 1182 try { 1183 listener.asBinder().linkToDeath(record, 0); 1184 } catch (RemoteException e) { 1185 Slog.w(TAG, "Listener already died"); 1186 return; 1187 } 1188 synchronized (mLock) { 1189 mSystemAudioModeChangeListeners.add(listener); 1190 mSystemAudioModeChangeListenerRecords.add(record); 1191 } 1192 } 1193 1194 private void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) { 1195 synchronized (mLock) { 1196 for (SystemAudioModeChangeListenerRecord record : 1197 mSystemAudioModeChangeListenerRecords) { 1198 if (record.mListener.asBinder() == listener) { 1199 listener.asBinder().unlinkToDeath(record, 0); 1200 mSystemAudioModeChangeListenerRecords.remove(record); 1201 break; 1202 } 1203 } 1204 mSystemAudioModeChangeListeners.remove(listener); 1205 } 1206 } 1207 1208 private final class InputChangeListenerRecord implements IBinder.DeathRecipient { 1209 @Override 1210 public void binderDied() { 1211 synchronized (mLock) { 1212 mInputChangeListener = null; 1213 } 1214 } 1215 } 1216 1217 private void setInputChangeListener(IHdmiInputChangeListener listener) { 1218 synchronized (mLock) { 1219 mInputChangeListenerRecord = new InputChangeListenerRecord(); 1220 try { 1221 listener.asBinder().linkToDeath(mInputChangeListenerRecord, 0); 1222 } catch (RemoteException e) { 1223 Slog.w(TAG, "Listener already died"); 1224 return; 1225 } 1226 mInputChangeListener = listener; 1227 } 1228 } 1229 1230 void invokeInputChangeListener(HdmiCecDeviceInfo info) { 1231 synchronized (mLock) { 1232 if (mInputChangeListener != null) { 1233 try { 1234 mInputChangeListener.onChanged(info); 1235 } catch (RemoteException e) { 1236 Slog.w(TAG, "Exception thrown by IHdmiInputChangeListener: " + e); 1237 } 1238 } 1239 } 1240 } 1241 1242 private void setHdmiRecordListener(IHdmiRecordListener listener) { 1243 synchronized (mLock) { 1244 mRecordListenerRecord = new HdmiRecordListenerRecord(); 1245 try { 1246 listener.asBinder().linkToDeath(mRecordListenerRecord, 0); 1247 } catch (RemoteException e) { 1248 Slog.w(TAG, "Listener already died.", e); 1249 } 1250 mRecordListener = listener; 1251 } 1252 } 1253 1254 byte[] invokeRecordRequestListener(int recorderAddress) { 1255 synchronized (mLock) { 1256 if (mRecordListener != null) { 1257 try { 1258 return mRecordListener.getOneTouchRecordSource(recorderAddress); 1259 } catch (RemoteException e) { 1260 Slog.w(TAG, "Failed to start record.", e); 1261 } 1262 } 1263 return EmptyArray.BYTE; 1264 } 1265 } 1266 1267 void invokeOneTouchRecordResult(int result) { 1268 synchronized (mLock) { 1269 if (mRecordListener != null) { 1270 try { 1271 mRecordListener.onOneTouchRecordResult(result); 1272 } catch (RemoteException e) { 1273 Slog.w(TAG, "Failed to call onOneTouchRecordResult.", e); 1274 } 1275 } 1276 } 1277 } 1278 1279 void invokeTimerRecordingResult(int result) { 1280 synchronized (mLock) { 1281 if (mRecordListener != null) { 1282 try { 1283 mRecordListener.onTimerRecordingResult(result); 1284 } catch (RemoteException e) { 1285 Slog.w(TAG, "Failed to call onOneTouchRecordResult.", e); 1286 } 1287 } 1288 } 1289 } 1290 1291 private void invokeCallback(IHdmiControlCallback callback, int result) { 1292 try { 1293 callback.onComplete(result); 1294 } catch (RemoteException e) { 1295 Slog.e(TAG, "Invoking callback failed:" + e); 1296 } 1297 } 1298 1299 private void invokeSystemAudioModeChange(IHdmiSystemAudioModeChangeListener listener, 1300 boolean enabled) { 1301 try { 1302 listener.onStatusChanged(enabled); 1303 } catch (RemoteException e) { 1304 Slog.e(TAG, "Invoking callback failed:" + e); 1305 } 1306 } 1307 1308 private void announceHotplugEvent(int portId, boolean connected) { 1309 HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected); 1310 synchronized (mLock) { 1311 for (IHdmiHotplugEventListener listener : mHotplugEventListeners) { 1312 invokeHotplugEventListenerLocked(listener, event); 1313 } 1314 } 1315 } 1316 1317 private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener, 1318 HdmiHotplugEvent event) { 1319 try { 1320 listener.onReceived(event); 1321 } catch (RemoteException e) { 1322 Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e); 1323 } 1324 } 1325 1326 private static boolean hasSameTopPort(int path1, int path2) { 1327 return (path1 & Constants.ROUTING_PATH_TOP_MASK) 1328 == (path2 & Constants.ROUTING_PATH_TOP_MASK); 1329 } 1330 1331 private HdmiCecLocalDeviceTv tv() { 1332 return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCecDeviceInfo.DEVICE_TV); 1333 } 1334 1335 boolean isTvDevice() { 1336 return tv() != null; 1337 } 1338 1339 private HdmiCecLocalDevicePlayback playback() { 1340 return (HdmiCecLocalDevicePlayback) 1341 mCecController.getLocalDevice(HdmiCecDeviceInfo.DEVICE_PLAYBACK); 1342 } 1343 1344 AudioManager getAudioManager() { 1345 return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); 1346 } 1347 1348 boolean isControlEnabled() { 1349 synchronized (mLock) { 1350 return mHdmiControlEnabled; 1351 } 1352 } 1353 1354 int getPowerStatus() { 1355 return mPowerStatus; 1356 } 1357 1358 boolean isPowerOnOrTransient() { 1359 return mPowerStatus == HdmiControlManager.POWER_STATUS_ON 1360 || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON; 1361 } 1362 1363 boolean isPowerStandbyOrTransient() { 1364 return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY 1365 || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY; 1366 } 1367 1368 boolean isPowerStandby() { 1369 return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY; 1370 } 1371 1372 @ServiceThreadOnly 1373 void wakeUp() { 1374 assertRunOnServiceThread(); 1375 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); 1376 pm.wakeUp(SystemClock.uptimeMillis()); 1377 // PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets 1378 // the intent, the sequence will continue at onWakeUp(). 1379 } 1380 1381 @ServiceThreadOnly 1382 void standby() { 1383 assertRunOnServiceThread(); 1384 mStandbyMessageReceived = true; 1385 PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); 1386 pm.goToSleep(SystemClock.uptimeMillis()); 1387 // PowerManger will send the broadcast Intent.ACTION_SCREEN_OFF and after this gets 1388 // the intent, the sequence will continue at onStandby(). 1389 } 1390 1391 @ServiceThreadOnly 1392 private void onWakeUp() { 1393 assertRunOnServiceThread(); 1394 mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON; 1395 if (mCecController != null) { 1396 if (mHdmiControlEnabled) { 1397 initializeCec(true); 1398 } 1399 } else { 1400 Slog.i(TAG, "Device does not support HDMI-CEC."); 1401 } 1402 // TODO: Initialize MHL local devices. 1403 } 1404 1405 @ServiceThreadOnly 1406 private void onStandby() { 1407 assertRunOnServiceThread(); 1408 mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY; 1409 1410 final List<HdmiCecLocalDevice> devices = getAllLocalDevices(); 1411 disableDevices(new PendingActionClearedCallback() { 1412 @Override 1413 public void onCleared(HdmiCecLocalDevice device) { 1414 Slog.v(TAG, "On standby-action cleared:" + device.mDeviceType); 1415 devices.remove(device); 1416 if (devices.isEmpty()) { 1417 onStandbyCompleted(); 1418 clearLocalDevices(); 1419 } 1420 } 1421 }); 1422 } 1423 1424 private void disableDevices(PendingActionClearedCallback callback) { 1425 for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { 1426 device.disableDevice(mStandbyMessageReceived, callback); 1427 } 1428 } 1429 1430 @ServiceThreadOnly 1431 private void clearLocalDevices() { 1432 assertRunOnServiceThread(); 1433 if (mCecController == null) { 1434 return; 1435 } 1436 mCecController.clearLogicalAddress(); 1437 mCecController.clearLocalDevices(); 1438 } 1439 1440 @ServiceThreadOnly 1441 private void onStandbyCompleted() { 1442 assertRunOnServiceThread(); 1443 Slog.v(TAG, "onStandbyCompleted"); 1444 1445 if (mPowerStatus != HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) { 1446 return; 1447 } 1448 mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY; 1449 for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { 1450 device.onStandby(mStandbyMessageReceived); 1451 } 1452 mStandbyMessageReceived = false; 1453 mCecController.setOption(HdmiTvClient.OPTION_CEC_SERVICE_CONTROL, HdmiTvClient.DISABLED); 1454 } 1455 1456 private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) { 1457 VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType); 1458 try { 1459 listener.asBinder().linkToDeath(record, 0); 1460 } catch (RemoteException e) { 1461 Slog.w(TAG, "Listener already died"); 1462 return; 1463 } 1464 synchronized (mLock) { 1465 mVendorCommandListenerRecords.add(record); 1466 } 1467 } 1468 1469 void invokeVendorCommandListeners(int deviceType, int srcAddress, byte[] params, 1470 boolean hasVendorId) { 1471 synchronized (mLock) { 1472 for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) { 1473 if (record.mDeviceType != deviceType) { 1474 continue; 1475 } 1476 try { 1477 record.mListener.onReceived(srcAddress, params, hasVendorId); 1478 } catch (RemoteException e) { 1479 Slog.e(TAG, "Failed to notify vendor command reception", e); 1480 } 1481 } 1482 } 1483 } 1484 1485 boolean isProhibitMode() { 1486 synchronized (mLock) { 1487 return mProhibitMode; 1488 } 1489 } 1490 1491 void setProhibitMode(boolean enabled) { 1492 synchronized (mLock) { 1493 mProhibitMode = enabled; 1494 } 1495 } 1496 1497 @ServiceThreadOnly 1498 private void handleHdmiControlStatusChanged(boolean enabled) { 1499 assertRunOnServiceThread(); 1500 1501 int value = enabled ? HdmiTvClient.ENABLED : HdmiTvClient.DISABLED; 1502 mCecController.setOption(HdmiTvClient.OPTION_CEC_ENABLE, value); 1503 if (mMhlController != null) { 1504 mMhlController.setOption(HdmiTvClient.OPTION_MHL_ENABLE, value); 1505 } 1506 1507 synchronized (mLock) { 1508 mHdmiControlEnabled = enabled; 1509 } 1510 1511 if (enabled) { 1512 initializeCec(false); 1513 } else { 1514 disableDevices(new PendingActionClearedCallback() { 1515 @Override 1516 public void onCleared(HdmiCecLocalDevice device) { 1517 assertRunOnServiceThread(); 1518 clearLocalDevices(); 1519 } 1520 }); 1521 } 1522 } 1523} 1524