diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 420304e4389a2..1a3fcfe0572e3 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -42,12 +42,21 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @GuardedBy("mLock") private boolean mArcStatusEnabled = false; - @GuardedBy("mLock") // Whether SystemAudioMode is "On" or not. + @GuardedBy("mLock") private boolean mSystemAudioMode; + + // Copy of mDeviceInfos to guarantee thread-safety. + @GuardedBy("mLock") + private List mSafeAllDeviceInfos = Collections.emptyList(); + // All external cec device which excludes local devices. + @GuardedBy("mLock") + private List mSafeExternalDeviceInfos = Collections.emptyList(); + // Map-like container of all cec devices including local ones. // A logical address of device is used as key of container. + // This is not thread-safe. For external purpose use mSafeDeviceInfos. private final SparseArray mDeviceInfos = new SparseArray<>(); HdmiCecLocalDeviceTv(HdmiControlService service) { @@ -124,7 +133,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { /** * Sends key to a target CEC device. * - * @param keyCode key code to send. Defined in {@link KeyEvent}. + * @param keyCode key code to send. Defined in {@link android.view.KeyEvent}. * @param isPressed true if this is keypress event */ void sendKeyEvent(int keyCode, boolean isPressed) { @@ -251,6 +260,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { private void clearDeviceInfoList() { assertRunOnServiceThread(); mDeviceInfos.clear(); + updateSafeDeviceInfoList(); } void changeSystemAudioMode(boolean enabled, IHdmiControlCallback callback) { @@ -393,6 +403,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { removeDeviceInfo(deviceInfo.getLogicalAddress()); } mDeviceInfos.append(deviceInfo.getLogicalAddress(), deviceInfo); + updateSafeDeviceInfoList(); return oldDeviceInfo; } @@ -411,6 +422,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { if (deviceInfo != null) { mDeviceInfos.remove(logicalAddress); } + updateSafeDeviceInfoList(); return deviceInfo; } @@ -418,13 +430,13 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { * Return a list of all {@link HdmiCecDeviceInfo}. * *

Declared as package-private. accessed by {@link HdmiControlService} only. + * This is not thread-safe. For thread safety, call {@link #getSafeDeviceInfoList(boolean)}. */ List getDeviceInfoList(boolean includelLocalDevice) { assertRunOnServiceThread(); if (includelLocalDevice) { - return HdmiUtils.sparseArrayToList(mDeviceInfos); + return HdmiUtils.sparseArrayToList(mDeviceInfos); } else { - ArrayList infoList = new ArrayList<>(); for (int i = 0; i < mDeviceInfos.size(); ++i) { HdmiCecDeviceInfo info = mDeviceInfos.valueAt(i); @@ -436,6 +448,31 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } } + /** + * Return a list of {@link HdmiCecDeviceInfo}. + * + * @param includeLocalDevice whether to include local device in result. + */ + List getSafeDeviceInfoList(boolean includeLocalDevice) { + synchronized (mLock) { + if (includeLocalDevice) { + return mSafeAllDeviceInfos; + } else { + return mSafeExternalDeviceInfos; + } + } + } + + private void updateSafeDeviceInfoList() { + assertRunOnServiceThread(); + List copiedDevices = HdmiUtils.sparseArrayToList(mDeviceInfos); + List externalDeviceInfos = getDeviceInfoList(false); + synchronized (mLock) { + mSafeAllDeviceInfos = copiedDevices; + mSafeExternalDeviceInfos = externalDeviceInfos; + } + } + private boolean isLocalDeviceAddress(int address) { assertRunOnServiceThread(); for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) { @@ -450,6 +487,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { * Return a {@link HdmiCecDeviceInfo} corresponding to the given {@code logicalAddress}. * *

Declared as package-private. accessed by {@link HdmiControlService} only. + * This is not thread-safe. For thread safety, call {@link #getSafeDeviceInfo(int)}. * * @param logicalAddress logical address to be retrieved * @return {@link HdmiCecDeviceInfo} matched with the given {@code logicalAddress}. @@ -465,6 +503,23 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { return getDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM); } + /** + * Thread safe version of {@link #getDeviceInfo(int)}. + * + * @param logicalAddress logical address to be retrieved + * @return {@link HdmiCecDeviceInfo} matched with the given {@code logicalAddress}. + * Returns null if no logical address matched + */ + HdmiCecDeviceInfo getSafeDeviceInfo(int logicalAddress) { + synchronized (mLock) { + return mSafeAllDeviceInfos.get(logicalAddress); + } + } + + HdmiCecDeviceInfo getSafeAvrDeviceInfo() { + return getSafeDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM); + } + /** * Called when a device is newly added or a new device is detected. *