HdmiControlService.java revision 13c030e828a90fcfc57b52024b72326757cec583
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.Context; 21import android.hardware.hdmi.HdmiCec; 22import android.hardware.hdmi.HdmiCecDeviceInfo; 23import android.hardware.hdmi.HdmiCecMessage; 24import android.hardware.hdmi.HdmiHotplugEvent; 25import android.hardware.hdmi.HdmiPortInfo; 26import android.hardware.hdmi.IHdmiControlCallback; 27import android.hardware.hdmi.IHdmiControlService; 28import android.hardware.hdmi.IHdmiDeviceEventListener; 29import android.hardware.hdmi.IHdmiHotplugEventListener; 30import android.os.Build; 31import android.os.Handler; 32import android.os.HandlerThread; 33import android.os.IBinder; 34import android.os.Looper; 35import android.os.RemoteException; 36import android.util.Slog; 37import android.util.SparseArray; 38import android.util.SparseIntArray; 39 40import com.android.internal.annotations.GuardedBy; 41import com.android.server.SystemService; 42import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback; 43 44import java.util.ArrayList; 45import java.util.Collections; 46import java.util.List; 47 48/** 49 * Provides a service for sending and processing HDMI control messages, 50 * HDMI-CEC and MHL control command, and providing the information on both standard. 51 */ 52public final class HdmiControlService extends SystemService { 53 private static final String TAG = "HdmiControlService"; 54 55 // TODO: Rename the permission to HDMI_CONTROL. 56 private static final String PERMISSION = "android.permission.HDMI_CEC"; 57 58 /** 59 * Interface to report send result. 60 */ 61 interface SendMessageCallback { 62 /** 63 * Called when {@link HdmiControlService#sendCecCommand} is completed. 64 * 65 * @param error result of send request. 66 * @see {@link #SEND_RESULT_SUCCESS} 67 * @see {@link #SEND_RESULT_NAK} 68 * @see {@link #SEND_RESULT_FAILURE} 69 */ 70 void onSendCompleted(int error); 71 } 72 73 /** 74 * Interface to get a list of available logical devices. 75 */ 76 interface DevicePollingCallback { 77 /** 78 * Called when device polling is finished. 79 * 80 * @param ackedAddress a list of logical addresses of available devices 81 */ 82 void onPollingFinished(List<Integer> ackedAddress); 83 } 84 85 // A thread to handle synchronous IO of CEC and MHL control service. 86 // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms) 87 // and sparse call it shares a thread to handle IO operations. 88 private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread"); 89 90 // Used to synchronize the access to the service. 91 private final Object mLock = new Object(); 92 93 // Type of logical devices hosted in the system. Stored in the unmodifiable list. 94 private final List<Integer> mLocalDevices; 95 96 // List of listeners registered by callers that want to get notified of 97 // hotplug events. 98 @GuardedBy("mLock") 99 private final ArrayList<IHdmiHotplugEventListener> mHotplugEventListeners = new ArrayList<>(); 100 101 // List of records for hotplug event listener to handle the the caller killed in action. 102 @GuardedBy("mLock") 103 private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords = 104 new ArrayList<>(); 105 106 // List of listeners registered by callers that want to get notified of 107 // device status events. 108 @GuardedBy("mLock") 109 private final ArrayList<IHdmiDeviceEventListener> mDeviceEventListeners = new ArrayList<>(); 110 111 // List of records for device event listener to handle the the caller killed in action. 112 @GuardedBy("mLock") 113 private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords = 114 new ArrayList<>(); 115 116 // Handler used to run a task in service thread. 117 private final Handler mHandler = new Handler(); 118 119 @Nullable 120 private HdmiCecController mCecController; 121 122 @Nullable 123 private HdmiMhlController mMhlController; 124 125 // HDMI port information. Stored in the unmodifiable list to keep the static information 126 // from being modified. 127 private List<HdmiPortInfo> mPortInfo; 128 129 public HdmiControlService(Context context) { 130 super(context); 131 mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray( 132 com.android.internal.R.array.config_hdmiCecLogicalDeviceType)); 133 } 134 135 @Override 136 public void onStart() { 137 mIoThread.start(); 138 mCecController = HdmiCecController.create(this); 139 140 if (mCecController != null) { 141 initializeLocalDevices(mLocalDevices); 142 } else { 143 Slog.i(TAG, "Device does not support HDMI-CEC."); 144 } 145 146 mMhlController = HdmiMhlController.create(this); 147 if (mMhlController == null) { 148 Slog.i(TAG, "Device does not support MHL-control."); 149 } 150 mPortInfo = initPortInfo(); 151 publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService()); 152 153 // TODO: Read the preference for SystemAudioMode and initialize mSystemAudioMode and 154 // start to monitor the preference value and invoke SystemAudioActionFromTv if needed. 155 } 156 157 private void initializeLocalDevices(final List<Integer> deviceTypes) { 158 // A container for [Logical Address, Local device info]. 159 final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>(); 160 final SparseIntArray finished = new SparseIntArray(); 161 mCecController.clearLogicalAddress(); 162 for (int type : deviceTypes) { 163 final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type); 164 localDevice.init(); 165 mCecController.allocateLogicalAddress(type, 166 localDevice.getPreferredAddress(), new AllocateAddressCallback() { 167 @Override 168 public void onAllocated(int deviceType, int logicalAddress) { 169 if (logicalAddress == HdmiCec.ADDR_UNREGISTERED) { 170 Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]"); 171 } else { 172 HdmiCecDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType); 173 localDevice.setDeviceInfo(deviceInfo); 174 mCecController.addLocalDevice(deviceType, localDevice); 175 mCecController.addLogicalAddress(logicalAddress); 176 devices.append(logicalAddress, localDevice); 177 } 178 finished.append(deviceType, logicalAddress); 179 180 // Address allocation completed for all devices. Notify each device. 181 if (deviceTypes.size() == finished.size()) { 182 notifyAddressAllocated(devices); 183 } 184 } 185 }); 186 } 187 } 188 189 private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices) { 190 for (int i = 0; i < devices.size(); ++i) { 191 int address = devices.keyAt(i); 192 HdmiCecLocalDevice device = devices.valueAt(i); 193 device.handleAddressAllocated(address); 194 } 195 } 196 197 // Initialize HDMI port information. Combine the information from CEC and MHL HAL and 198 // keep them in one place. 199 private List<HdmiPortInfo> initPortInfo() { 200 HdmiPortInfo[] cecPortInfo = null; 201 202 // CEC HAL provides majority of the info while MHL does only MHL support flag for 203 // each port. Return empty array if CEC HAL didn't provide the info. 204 if (mCecController != null) { 205 cecPortInfo = mCecController.getPortInfos(); 206 } 207 if (cecPortInfo == null) { 208 return Collections.emptyList(); 209 } 210 211 HdmiPortInfo[] mhlPortInfo = new HdmiPortInfo[0]; 212 if (mMhlController != null) { 213 // TODO: Implement plumbing logic to get MHL port information. 214 // mhlPortInfo = mMhlController.getPortInfos(); 215 } 216 217 // Use the id (port number) to find the matched info between CEC and MHL to combine them 218 // into one. Leave the field `mhlSupported` to false if matched MHL entry is not found. 219 ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length); 220 for (int i = 0; i < cecPortInfo.length; ++i) { 221 HdmiPortInfo cec = cecPortInfo[i]; 222 int id = cec.getId(); 223 boolean mhlInfoFound = false; 224 for (HdmiPortInfo mhl : mhlPortInfo) { 225 if (id == mhl.getId()) { 226 result.add(new HdmiPortInfo(id, cec.getType(), cec.getAddress(), 227 cec.isCecSupported(), mhl.isMhlSupported(), cec.isArcSupported())); 228 mhlInfoFound = true; 229 break; 230 } 231 } 232 if (!mhlInfoFound) { 233 result.add(cec); 234 } 235 } 236 237 return Collections.unmodifiableList(result); 238 } 239 240 /** 241 * Returns HDMI port information for the given port id. 242 * 243 * @param portId HDMI port id 244 * @return {@link HdmiPortInfo} for the given port 245 */ 246 HdmiPortInfo getPortInfo(int portId) { 247 // mPortInfo is an unmodifiable list and the only reference to its inner list. 248 // No lock is necessary. 249 for (HdmiPortInfo info : mPortInfo) { 250 if (portId == info.getId()) { 251 return info; 252 } 253 } 254 return null; 255 } 256 257 /** 258 * Returns the routing path (physical address) of the HDMI port for the given 259 * port id. 260 */ 261 int portIdToPath(int portId) { 262 HdmiPortInfo portInfo = getPortInfo(portId); 263 if (portInfo == null) { 264 Slog.e(TAG, "Cannot find the port info: " + portId); 265 return HdmiConstants.INVALID_PHYSICAL_ADDRESS; 266 } 267 return portInfo.getAddress(); 268 } 269 270 /** 271 * Returns the id of HDMI port located at the top of the hierarchy of 272 * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance, 273 * the port id to be returned is the ID associated with the port address 274 * 0x1000 (1.0.0.0) which is the topmost path of the given routing path. 275 */ 276 int pathToPortId(int path) { 277 int portAddress = path & HdmiConstants.ROUTING_PATH_TOP_MASK; 278 for (HdmiPortInfo info : mPortInfo) { 279 if (portAddress == info.getAddress()) { 280 return info.getId(); 281 } 282 } 283 return HdmiConstants.INVALID_PORT_ID; 284 } 285 286 /** 287 * Returns {@link Looper} for IO operation. 288 * 289 * <p>Declared as package-private. 290 */ 291 Looper getIoLooper() { 292 return mIoThread.getLooper(); 293 } 294 295 /** 296 * Returns {@link Looper} of main thread. Use this {@link Looper} instance 297 * for tasks that are running on main service thread. 298 * 299 * <p>Declared as package-private. 300 */ 301 Looper getServiceLooper() { 302 return mHandler.getLooper(); 303 } 304 305 /** 306 * Returns physical address of the device. 307 */ 308 int getPhysicalAddress() { 309 return mCecController.getPhysicalAddress(); 310 } 311 312 /** 313 * Returns vendor id of CEC service. 314 */ 315 int getVendorId() { 316 return mCecController.getVendorId(); 317 } 318 319 HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) { 320 assertRunOnServiceThread(); 321 HdmiCecLocalDeviceTv tv = tv(); 322 if (tv == null) { 323 return null; 324 } 325 return tv.getDeviceInfo(logicalAddress); 326 } 327 328 /** 329 * Returns version of CEC. 330 */ 331 int getCecVersion() { 332 return mCecController.getVersion(); 333 } 334 335 /** 336 * Whether a device of the specified physical address is connected to ARC enabled port. 337 */ 338 boolean isConnectedToArcPort(int physicalAddress) { 339 for (HdmiPortInfo portInfo : mPortInfo) { 340 if (hasSameTopPort(portInfo.getAddress(), physicalAddress) 341 && portInfo.isArcSupported()) { 342 return true; 343 } 344 } 345 return false; 346 } 347 348 void runOnServiceThread(Runnable runnable) { 349 mHandler.post(runnable); 350 } 351 352 void runOnServiceThreadAtFrontOfQueue(Runnable runnable) { 353 mHandler.postAtFrontOfQueue(runnable); 354 } 355 356 private void assertRunOnServiceThread() { 357 if (Looper.myLooper() != mHandler.getLooper()) { 358 throw new IllegalStateException("Should run on service thread."); 359 } 360 } 361 362 /** 363 * Transmit a CEC command to CEC bus. 364 * 365 * @param command CEC command to send out 366 * @param callback interface used to the result of send command 367 */ 368 void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) { 369 mCecController.sendCommand(command, callback); 370 } 371 372 void sendCecCommand(HdmiCecMessage command) { 373 mCecController.sendCommand(command, null); 374 } 375 376 boolean handleCecCommand(HdmiCecMessage message) { 377 return dispatchMessageToLocalDevice(message); 378 } 379 380 void setAudioReturnChannel(boolean enabled) { 381 mCecController.setAudioReturnChannel(enabled); 382 } 383 384 private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) { 385 for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { 386 if (device.dispatchMessage(message) 387 && message.getDestination() != HdmiCec.ADDR_BROADCAST) { 388 return true; 389 } 390 } 391 392 Slog.w(TAG, "Unhandled cec command:" + message); 393 return false; 394 } 395 396 /** 397 * Called when a new hotplug event is issued. 398 * 399 * @param portNo hdmi port number where hot plug event issued. 400 * @param connected whether to be plugged in or not 401 */ 402 void onHotplug(int portNo, boolean connected) { 403 assertRunOnServiceThread(); 404 405 for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { 406 device.onHotplug(portNo, connected); 407 } 408 409 announceHotplugEvent(portNo, connected); 410 } 411 412 /** 413 * Poll all remote devices. It sends <Polling Message> to all remote 414 * devices. 415 * 416 * @param callback an interface used to get a list of all remote devices' address 417 * @param pickStrategy strategy how to pick polling candidates 418 * @param retryCount the number of retry used to send polling message to remote devices 419 * @throw IllegalArgumentException if {@code pickStrategy} is invalid value 420 */ 421 void pollDevices(DevicePollingCallback callback, int pickStrategy, int retryCount) { 422 mCecController.pollDevices(callback, checkPollStrategy(pickStrategy), retryCount); 423 } 424 425 private int checkPollStrategy(int pickStrategy) { 426 int strategy = pickStrategy & HdmiConstants.POLL_STRATEGY_MASK; 427 if (strategy == 0) { 428 throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy); 429 } 430 int iterationStrategy = pickStrategy & HdmiConstants.POLL_ITERATION_STRATEGY_MASK; 431 if (iterationStrategy == 0) { 432 throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy); 433 } 434 return strategy | iterationStrategy; 435 } 436 437 List<HdmiCecLocalDevice> getAllLocalDevices() { 438 assertRunOnServiceThread(); 439 return mCecController.getLocalDeviceList(); 440 } 441 442 Object getServiceLock() { 443 return mLock; 444 } 445 446 void setAudioStatus(boolean mute, int volume) { 447 // TODO: Hook up with AudioManager. 448 } 449 450 private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) { 451 // TODO: find better name instead of model name. 452 String displayName = Build.MODEL; 453 return new HdmiCecDeviceInfo(logicalAddress, 454 getPhysicalAddress(), deviceType, getVendorId(), displayName); 455 } 456 457 // Record class that monitors the event of the caller of being killed. Used to clean up 458 // the listener list and record list accordingly. 459 private final class HotplugEventListenerRecord implements IBinder.DeathRecipient { 460 private final IHdmiHotplugEventListener mListener; 461 462 public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) { 463 mListener = listener; 464 } 465 466 @Override 467 public void binderDied() { 468 synchronized (mLock) { 469 mHotplugEventListenerRecords.remove(this); 470 mHotplugEventListeners.remove(mListener); 471 } 472 } 473 } 474 475 private final class DeviceEventListenerRecord implements IBinder.DeathRecipient { 476 private final IHdmiDeviceEventListener mListener; 477 478 public DeviceEventListenerRecord(IHdmiDeviceEventListener listener) { 479 mListener = listener; 480 } 481 482 @Override 483 public void binderDied() { 484 synchronized (mLock) { 485 mDeviceEventListenerRecords.remove(this); 486 mDeviceEventListeners.remove(mListener); 487 } 488 } 489 } 490 491 private void enforceAccessPermission() { 492 getContext().enforceCallingOrSelfPermission(PERMISSION, TAG); 493 } 494 495 private final class BinderService extends IHdmiControlService.Stub { 496 @Override 497 public int[] getSupportedTypes() { 498 enforceAccessPermission(); 499 // mLocalDevices is an unmodifiable list - no lock necesary. 500 int[] localDevices = new int[mLocalDevices.size()]; 501 for (int i = 0; i < localDevices.length; ++i) { 502 localDevices[i] = mLocalDevices.get(i); 503 } 504 return localDevices; 505 } 506 507 @Override 508 public void deviceSelect(final int logicalAddress, final IHdmiControlCallback callback) { 509 enforceAccessPermission(); 510 runOnServiceThread(new Runnable() { 511 @Override 512 public void run() { 513 HdmiCecLocalDeviceTv tv = tv(); 514 if (tv == null) { 515 Slog.w(TAG, "Local tv device not available"); 516 invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE); 517 return; 518 } 519 tv.deviceSelect(logicalAddress, callback); 520 } 521 }); 522 } 523 524 @Override 525 public void portSelect(final int portId, final IHdmiControlCallback callback) { 526 enforceAccessPermission(); 527 runOnServiceThread(new Runnable() { 528 @Override 529 public void run() { 530 HdmiCecLocalDeviceTv tv = tv(); 531 if (tv == null) { 532 Slog.w(TAG, "Local tv device not available"); 533 invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE); 534 return; 535 } 536 tv.portSelect(portId, callback); 537 } 538 }); 539 } 540 541 @Override 542 public void sendKeyEvent(final int keyCode, final boolean isPressed) { 543 enforceAccessPermission(); 544 runOnServiceThread(new Runnable() { 545 @Override 546 public void run() { 547 // TODO: sendKeyEvent is for TV device only for now. Allow other 548 // local devices of different types to use this as well. 549 HdmiCecLocalDeviceTv tv = tv(); 550 if (tv == null) { 551 Slog.w(TAG, "Local tv device not available"); 552 return; 553 } 554 tv.sendKeyEvent(keyCode, isPressed); 555 } 556 }); 557 } 558 559 @Override 560 public void oneTouchPlay(final IHdmiControlCallback callback) { 561 enforceAccessPermission(); 562 runOnServiceThread(new Runnable() { 563 @Override 564 public void run() { 565 HdmiControlService.this.oneTouchPlay(callback); 566 } 567 }); 568 } 569 570 @Override 571 public void queryDisplayStatus(final IHdmiControlCallback callback) { 572 enforceAccessPermission(); 573 runOnServiceThread(new Runnable() { 574 @Override 575 public void run() { 576 HdmiControlService.this.queryDisplayStatus(callback); 577 } 578 }); 579 } 580 581 @Override 582 public void addHotplugEventListener(final IHdmiHotplugEventListener listener) { 583 enforceAccessPermission(); 584 runOnServiceThread(new Runnable() { 585 @Override 586 public void run() { 587 HdmiControlService.this.addHotplugEventListener(listener); 588 } 589 }); 590 } 591 592 @Override 593 public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) { 594 enforceAccessPermission(); 595 runOnServiceThread(new Runnable() { 596 @Override 597 public void run() { 598 HdmiControlService.this.removeHotplugEventListener(listener); 599 } 600 }); 601 } 602 603 @Override 604 public void addDeviceEventListener(final IHdmiDeviceEventListener listener) { 605 enforceAccessPermission(); 606 runOnServiceThread(new Runnable() { 607 @Override 608 public void run() { 609 HdmiControlService.this.addDeviceEventListener(listener); 610 } 611 }); 612 } 613 614 @Override 615 public List<HdmiPortInfo> getPortInfo() { 616 enforceAccessPermission(); 617 return mPortInfo; 618 } 619 } 620 621 private void oneTouchPlay(final IHdmiControlCallback callback) { 622 assertRunOnServiceThread(); 623 HdmiCecLocalDevicePlayback source = playback(); 624 if (source == null) { 625 Slog.w(TAG, "Local playback device not available"); 626 invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE); 627 return; 628 } 629 source.oneTouchPlay(callback); 630 } 631 632 private void queryDisplayStatus(final IHdmiControlCallback callback) { 633 assertRunOnServiceThread(); 634 HdmiCecLocalDevicePlayback source = playback(); 635 if (source == null) { 636 Slog.w(TAG, "Local playback device not available"); 637 invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE); 638 return; 639 } 640 source.queryDisplayStatus(callback); 641 } 642 643 private void addHotplugEventListener(IHdmiHotplugEventListener listener) { 644 HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener); 645 try { 646 listener.asBinder().linkToDeath(record, 0); 647 } catch (RemoteException e) { 648 Slog.w(TAG, "Listener already died"); 649 return; 650 } 651 synchronized (mLock) { 652 mHotplugEventListenerRecords.add(record); 653 mHotplugEventListeners.add(listener); 654 } 655 } 656 657 private void removeHotplugEventListener(IHdmiHotplugEventListener listener) { 658 synchronized (mLock) { 659 for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) { 660 if (record.mListener.asBinder() == listener.asBinder()) { 661 listener.asBinder().unlinkToDeath(record, 0); 662 mHotplugEventListenerRecords.remove(record); 663 break; 664 } 665 } 666 mHotplugEventListeners.remove(listener); 667 } 668 } 669 670 private void addDeviceEventListener(IHdmiDeviceEventListener listener) { 671 DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener); 672 try { 673 listener.asBinder().linkToDeath(record, 0); 674 } catch (RemoteException e) { 675 Slog.w(TAG, "Listener already died"); 676 return; 677 } 678 synchronized (mLock) { 679 mDeviceEventListeners.add(listener); 680 mDeviceEventListenerRecords.add(record); 681 } 682 } 683 684 void invokeDeviceEventListeners(HdmiCecDeviceInfo device, boolean activated) { 685 synchronized (mLock) { 686 for (IHdmiDeviceEventListener listener : mDeviceEventListeners) { 687 try { 688 listener.onStatusChanged(device, activated); 689 } catch (RemoteException e) { 690 Slog.e(TAG, "Failed to report device event:" + e); 691 } 692 } 693 } 694 } 695 696 private void invokeCallback(IHdmiControlCallback callback, int result) { 697 try { 698 callback.onComplete(result); 699 } catch (RemoteException e) { 700 Slog.e(TAG, "Invoking callback failed:" + e); 701 } 702 } 703 704 private void announceHotplugEvent(int portId, boolean connected) { 705 HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected); 706 synchronized (mLock) { 707 for (IHdmiHotplugEventListener listener : mHotplugEventListeners) { 708 invokeHotplugEventListenerLocked(listener, event); 709 } 710 } 711 } 712 713 private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener, 714 HdmiHotplugEvent event) { 715 try { 716 listener.onReceived(event); 717 } catch (RemoteException e) { 718 Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e); 719 } 720 } 721 722 private static boolean hasSameTopPort(int path1, int path2) { 723 return (path1 & HdmiConstants.ROUTING_PATH_TOP_MASK) 724 == (path2 & HdmiConstants.ROUTING_PATH_TOP_MASK); 725 } 726 727 private HdmiCecLocalDeviceTv tv() { 728 return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCec.DEVICE_TV); 729 } 730 731 private HdmiCecLocalDevicePlayback playback() { 732 return (HdmiCecLocalDevicePlayback) mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK); 733 } 734} 735