diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 820700b0af236..7f5a7c8b7aae3 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -127,6 +127,8 @@ import java.util.stream.Collectors; /** * Provides a service for sending and processing HDMI control messages, * HDMI-CEC and MHL control command, and providing the information on both standard. + * + * Additionally takes care of establishing and managing an eARC connection. */ public class HdmiControlService extends SystemService { private static final String TAG = "HdmiControlService"; @@ -386,7 +388,8 @@ public class HdmiControlService extends SystemService { @HdmiControlManager.HdmiCecControl private int mHdmiControlEnabled; - // Set to true while the eARC feature is supported by the hardware and the eARC HAL is present. + // Set to true while the eARC feature is supported by the hardware on at least one port + // and the eARC HAL is present. @GuardedBy("mLock") @VisibleForTesting protected boolean mEarcSupported; @@ -428,6 +431,9 @@ public class HdmiControlService extends SystemService { private HdmiCecPowerStatusController mPowerStatusController; + @Nullable + private HdmiEarcController mEarcController; + @Nullable private HdmiEarcLocalDevice mEarcLocalDevice; @@ -664,7 +670,6 @@ public class HdmiControlService extends SystemService { } if (mCecController == null) { Slog.i(TAG, "Device does not support HDMI-CEC."); - return; } if (mMhlController == null) { mMhlController = HdmiMhlControllerStub.create(this); @@ -672,20 +677,46 @@ public class HdmiControlService extends SystemService { if (!mMhlController.isReady()) { Slog.i(TAG, "Device does not support MHL-control."); } + if (mEarcController == null) { + mEarcController = HdmiEarcController.create(this); + } + if (mEarcController == null) { + Slog.i(TAG, "Device does not support eARC."); + } + if (mCecController == null && mEarcController == null) { + return; + } mHdmiCecNetwork = new HdmiCecNetwork(this, mCecController, mMhlController); if (isCecControlEnabled()) { initializeCec(INITIATED_BY_BOOT_UP); } else { mCecController.enableCec(false); } + synchronized (mLock) { - if (mEarcSupported && mEarcEnabled) { - initializeEarc(INITIATED_BY_BOOT_UP); - } + mMhlDevices = Collections.emptyList(); } - mMhlDevices = Collections.emptyList(); mHdmiCecNetwork.initPortInfo(); + List ports = getPortInfo(); + synchronized (mLock) { + mEarcSupported = false; + for (HdmiPortInfo port : ports) { + if (mEarcSupported && port.isEarcSupported()) { + // The HDMI specification only allows 1 active eARC connection. Android does not + // support devices with multiple eARC-enabled ports. + Slog.e(TAG, "HDMI eARC supported on more than 1 port."); + break; + } + mEarcSupported |= port.isEarcSupported(); + } + + mEarcSupported &= (mEarcController != null); + } + if (isEarcSupportedAndEnabled()) { + initializeEarc(INITIATED_BY_BOOT_UP); + } + mHdmiCecConfig.registerChangeListener(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, new HdmiCecConfig.SettingChangeListener() { @Override diff --git a/services/core/java/com/android/server/hdmi/HdmiEarcController.java b/services/core/java/com/android/server/hdmi/HdmiEarcController.java new file mode 100644 index 0000000000000..e4acbd11acb2d --- /dev/null +++ b/services/core/java/com/android/server/hdmi/HdmiEarcController.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.hdmi; + +import android.os.Handler; +import android.os.Looper; + +import com.android.internal.annotations.VisibleForTesting; + +final class HdmiEarcController { + private static final String TAG = "HdmiEarcController"; + + // Handler instance to process HAL calls. + private Handler mControlHandler; + + private final HdmiControlService mService; + + // Private constructor. Use HdmiEarcController.create(). + private HdmiEarcController(HdmiControlService service) { + mService = service; + } + + /** + * A factory method to get {@link HdmiEarcController}. If it fails to initialize + * inner device or has no device it will return {@code null}. + * + *

Declared as package-private, accessed by {@link HdmiControlService} only. + * @param service {@link HdmiControlService} instance used to create internal handler + * and to pass callback for incoming message or event. + * @return {@link HdmiEarcController} if device is initialized successfully. Otherwise, + * returns {@code null}. + */ + static HdmiEarcController create(HdmiControlService service) { + // TODO add the native wrapper and return null if eARC HAL is not present. + HdmiEarcController controller = new HdmiEarcController(service); + controller.init(); + return controller; + } + + private void init() { + mControlHandler = new Handler(mService.getServiceLooper()); + } + + private void assertRunOnServiceThread() { + if (Looper.myLooper() != mControlHandler.getLooper()) { + throw new IllegalStateException("Should run on service thread."); + } + } + + @VisibleForTesting + void runOnServiceThread(Runnable runnable) { + mControlHandler.post(new WorkSourceUidPreservingRunnable(runnable)); + } + + /** + * Enable eARC in the HAL + * @param enabled + */ + @HdmiAnnotations.ServiceThreadOnly + void setEarcEnabled(boolean enabled) { + assertRunOnServiceThread(); + // Stub. + // TODO: bind to native. + // TODO: handle error return values here, with logging. + } + + // TODO: bind to native. +}