Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bab13a49 authored by Paul Colța's avatar Paul Colța Committed by Android (Google) Code Review
Browse files

Merge changes Ib6cba8c5,I82f7dccf

* changes:
  HDMICEC: Add lock for CEC standby.
  HDMICEC: Disable CecController after the process of going to standby is completed
parents 2e02a5c1 fb160c56
Loading
Loading
Loading
Loading
+27 −4
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.hdmi;

import static com.android.server.hdmi.HdmiControlService.DEVICE_CLEANUP_TIMEOUT;

import android.annotation.CallSuper;
import android.hardware.hdmi.DeviceFeatures;
import android.hardware.hdmi.HdmiControlManager;
@@ -61,9 +63,6 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice {
    private static final int MAX_HDMI_ACTIVE_SOURCE_HISTORY = 10;
    private static final int MSG_DISABLE_DEVICE_TIMEOUT = 1;
    private static final int MSG_USER_CONTROL_RELEASE_TIMEOUT = 2;
    // Timeout in millisecond for device clean up (5s).
    // Normal actions timeout is 2s but some of them would have several sequence of timeout.
    private static final int DEVICE_CLEANUP_TIMEOUT = 5000;
    // Within the timer, a received <User Control Pressed> will start "Press and Hold" behavior.
    // When it expires, we can assume <User Control Release> is received.
    private static final int FOLLOWER_SAFETY_TIMEOUT = 550;
@@ -174,6 +173,14 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice {
                }
            };

    /**
     * A callback interface used by local devices use to indicate that they have finished their part
     * of the standby process.
     */
    interface StandbyCompletedCallback {
        void onStandbyCompleted();
    }

    /**
     * A callback interface to get notified when all pending action is cleared. It can be called
     * when timeout happened.
@@ -1260,8 +1267,14 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice {
     *     messages like &lt;Standby&gt;
     * @param standbyAction Intent action that drives the standby process, either {@link
     *     HdmiControlService#STANDBY_SCREEN_OFF} or {@link HdmiControlService#STANDBY_SHUTDOWN}
     * @param callback callback invoked after the standby process for the local device is completed.
     */
    protected void onStandby(boolean initiatedByCec, int standbyAction) {}
    protected void onStandby(boolean initiatedByCec, int standbyAction,
            StandbyCompletedCallback callback) {}

    protected void onStandby(boolean initiatedByCec, int standbyAction) {
        onStandby(initiatedByCec, standbyAction, null);
    }

    /**
     * Called when the initialization of local devices is complete.
@@ -1422,6 +1435,16 @@ abstract class HdmiCecLocalDevice extends HdmiLocalDevice {
        }
    }

    @ServiceThreadOnly
    @VisibleForTesting
    public void invokeStandbyCompletedCallback(StandbyCompletedCallback callback) {
        assertRunOnServiceThread();
        if (callback == null) {
            return;
        }
        callback.onStandbyCompleted();
    }

    void sendUserControlPressedAndReleased(int targetAddress, int cecKeycode) {
        mService.sendCecCommand(
                HdmiCecMessageBuilder.buildUserControlPressed(
+18 −3
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED;
import static com.android.server.hdmi.Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
import static com.android.server.hdmi.Constants.PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
import static com.android.server.hdmi.Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
import static com.android.server.hdmi.HdmiControlService.SendMessageCallback;

import android.annotation.Nullable;
import android.content.ActivityNotFoundException;
@@ -243,7 +244,8 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {

    @Override
    @ServiceThreadOnly
    protected void onStandby(boolean initiatedByCec, int standbyAction) {
    protected void onStandby(boolean initiatedByCec, int standbyAction,
            StandbyCompletedCallback callback) {
        assertRunOnServiceThread();
        // Invalidate the internal active source record when goes to standby
        // This set will also update mIsActiveSource
@@ -256,7 +258,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
                    Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL,
                    isSystemAudioActivated() ? "true" : "false");
        }
        terminateSystemAudioMode();
        terminateSystemAudioMode(callback);
    }

    @Override
@@ -1092,9 +1094,16 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
    }

    protected void terminateSystemAudioMode() {
        terminateSystemAudioMode(null);
    }

    // Since this method is not just called during the standby process, the callback should be
    // generalized in the future.
    protected void terminateSystemAudioMode(StandbyCompletedCallback callback) {
        // remove pending initiation actions
        removeAction(SystemAudioInitiationActionFromAvr.class);
        if (!isSystemAudioActivated()) {
            invokeStandbyCompletedCallback(callback);
            return;
        }

@@ -1102,7 +1111,13 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
            // send <Set System Audio Mode> [“Off”]
            mService.sendCecCommand(
                    HdmiCecMessageBuilder.buildSetSystemAudioMode(
                            getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST, false));
                            getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST, false),
                    new SendMessageCallback() {
                        @Override
                        public void onSendCompleted(int error) {
                            invokeStandbyCompletedCallback(callback);
                        }
                    });
        }
    }

+20 −9
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@ import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Binder;
import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.SystemProperties;
import android.sysprop.HdmiProperties;
import android.util.Slog;
@@ -238,9 +237,11 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {

    @Override
    @ServiceThreadOnly
    protected void onStandby(boolean initiatedByCec, int standbyAction) {
    protected void onStandby(boolean initiatedByCec, int standbyAction,
            StandbyCompletedCallback callback) {
        assertRunOnServiceThread();
        if (!mService.isCecControlEnabled()) {
            invokeStandbyCompletedCallback(callback);
            return;
        }
        boolean wasActiveSource = isActiveSource();
@@ -248,12 +249,20 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
        mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS,
                "HdmiCecLocalDevicePlayback#onStandby()");
        if (!wasActiveSource) {
            invokeStandbyCompletedCallback(callback);
            return;
        }
        SendMessageCallback sendMessageCallback = new SendMessageCallback() {
            @Override
            public void onSendCompleted(int error) {
                invokeStandbyCompletedCallback(callback);
            }
        };
        if (initiatedByCec) {
            mService.sendCecCommand(
                    HdmiCecMessageBuilder.buildInactiveSource(
                            getDeviceInfo().getLogicalAddress(), mService.getPhysicalAddress()));
                            getDeviceInfo().getLogicalAddress(), mService.getPhysicalAddress()),
                    sendMessageCallback);
            return;
        }
        switch (standbyAction) {
@@ -266,7 +275,8 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
                    case HdmiControlManager.POWER_CONTROL_MODE_TV:
                        mService.sendCecCommand(
                                HdmiCecMessageBuilder.buildStandby(
                                        getDeviceInfo().getLogicalAddress(), Constants.ADDR_TV));
                                        getDeviceInfo().getLogicalAddress(), Constants.ADDR_TV),
                                sendMessageCallback);
                        break;
                    case HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM:
                        mService.sendCecCommand(
@@ -275,19 +285,19 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
                        mService.sendCecCommand(
                                HdmiCecMessageBuilder.buildStandby(
                                        getDeviceInfo().getLogicalAddress(),
                                        Constants.ADDR_AUDIO_SYSTEM));
                                        Constants.ADDR_AUDIO_SYSTEM), sendMessageCallback);
                        break;
                    case HdmiControlManager.POWER_CONTROL_MODE_BROADCAST:
                        mService.sendCecCommand(
                                HdmiCecMessageBuilder.buildStandby(
                                        getDeviceInfo().getLogicalAddress(),
                                        Constants.ADDR_BROADCAST));
                                        Constants.ADDR_BROADCAST), sendMessageCallback);
                        break;
                    case HdmiControlManager.POWER_CONTROL_MODE_NONE:
                        mService.sendCecCommand(
                                HdmiCecMessageBuilder.buildInactiveSource(
                                        getDeviceInfo().getLogicalAddress(),
                                        mService.getPhysicalAddress()));
                                        mService.getPhysicalAddress()), sendMessageCallback);
                        break;
                }
                break;
@@ -295,7 +305,8 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
                // ACTION_SHUTDOWN is taken as a signal to power off all the devices.
                mService.sendCecCommand(
                        HdmiCecMessageBuilder.buildStandby(
                                getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST));
                                getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST),
                        sendMessageCallback);
                break;
        }
    }
@@ -590,7 +601,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
    }

    private class SystemWakeLock implements ActiveWakeLock {
        private final WakeLock mWakeLock;
        private final WakeLockWrapper mWakeLock;
        public SystemWakeLock() {
            mWakeLock = mService.getPowerManager().newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
            mWakeLock.setReferenceCounted(false);
+12 −2
Original line number Diff line number Diff line
@@ -1400,10 +1400,12 @@ public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {

    @Override
    @ServiceThreadOnly
    protected void onStandby(boolean initiatedByCec, int standbyAction) {
    protected void onStandby(boolean initiatedByCec, int standbyAction,
            StandbyCompletedCallback callback) {
        assertRunOnServiceThread();
        // Seq #11
        if (!mService.isCecControlEnabled()) {
            invokeStandbyCompletedCallback(callback);
            return;
        }
        boolean sendStandbyOnSleep =
@@ -1413,7 +1415,15 @@ public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        if (!initiatedByCec && sendStandbyOnSleep) {
            mService.sendCecCommand(
                    HdmiCecMessageBuilder.buildStandby(
                            getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST));
                            getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST),
                    new SendMessageCallback() {
                        @Override
                        public void onSendCompleted(int error) {
                            invokeStandbyCompletedCallback(callback);
                        }
                    });
        } else {
            invokeStandbyCompletedCallback(callback);
        }
    }

+79 −9
Original line number Diff line number Diff line
@@ -21,8 +21,10 @@ import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVIC
import static android.hardware.hdmi.HdmiControlManager.EARC_FEATURE_DISABLED;
import static android.hardware.hdmi.HdmiControlManager.EARC_FEATURE_ENABLED;
import static android.hardware.hdmi.HdmiControlManager.HDMI_CEC_CONTROL_ENABLED;
import static android.hardware.hdmi.HdmiControlManager.POWER_CONTROL_MODE_NONE;
import static android.hardware.hdmi.HdmiControlManager.SOUNDBAR_MODE_DISABLED;
import static android.hardware.hdmi.HdmiControlManager.SOUNDBAR_MODE_ENABLED;
import static android.hardware.hdmi.HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED;

import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
import static com.android.server.hdmi.Constants.DISABLED;
@@ -110,6 +112,7 @@ import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
import com.android.server.hdmi.HdmiCecLocalDevice.PendingActionClearedCallback;
import com.android.server.hdmi.HdmiCecLocalDevice.StandbyCompletedCallback;

import libcore.util.EmptyArray;

@@ -224,6 +227,10 @@ public class HdmiControlService extends SystemService {
    public @interface WakeReason {
    }

    // Timeout in millisecond for device clean up (5s).
    // Normal actions timeout is 2s but some of them would have several sequences of timeout.
    static final int DEVICE_CLEANUP_TIMEOUT = 5000;

    @VisibleForTesting
    static final AudioDeviceAttributes AUDIO_OUTPUT_DEVICE_HDMI = new AudioDeviceAttributes(
            AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_HDMI, "");
@@ -491,6 +498,9 @@ public class HdmiControlService extends SystemService {
    @Nullable
    private DeviceConfigWrapper mDeviceConfig;

    @Nullable
    private WakeLockWrapper mWakeLock;

    @Nullable
    private PowerManagerWrapper mPowerManager;

@@ -3040,7 +3050,7 @@ public class HdmiControlService extends SystemService {
        }
        String powerControlMode = getHdmiCecConfig().getStringValue(
                HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
        if (powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_NONE)) {
        if (powerControlMode.equals(POWER_CONTROL_MODE_NONE)) {
            return false;
        }
        int hdmiCecEnabled = getHdmiCecConfig().getIntValue(
@@ -3687,6 +3697,9 @@ public class HdmiControlService extends SystemService {
    @ServiceThreadOnly
    @VisibleForTesting
    protected void onStandby(final int standbyAction) {
        if (shouldAcquireWakeLock()) {
            acquireWakeLock();
        }
        mWakeUpMessageReceived = false;
        assertRunOnServiceThread();
        mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY,
@@ -3752,7 +3765,8 @@ public class HdmiControlService extends SystemService {
        return mMenuLanguage;
    }

    private void disableCecLocalDevices(PendingActionClearedCallback callback) {
    @VisibleForTesting
    protected void disableCecLocalDevices(PendingActionClearedCallback callback) {
        if (mCecController != null) {
            for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
                device.disableDevice(mStandbyMessageReceived, callback);
@@ -3780,25 +3794,81 @@ public class HdmiControlService extends SystemService {
     * cleared during standby. In this case, it does not execute the standby flow.
     */
    @ServiceThreadOnly
    private void onPendingActionsCleared(int standbyAction) {
    @VisibleForTesting
    protected void onPendingActionsCleared(int standbyAction) {
        assertRunOnServiceThread();
        Slog.v(TAG, "onPendingActionsCleared");
        int localDevicesCount = getAllCecLocalDevices().size();
        final int[] countStandbyCompletedDevices = new int[1];
        StandbyCompletedCallback callback = new StandbyCompletedCallback() {
            @Override
            public void onStandbyCompleted() {
                if (localDevicesCount < ++countStandbyCompletedDevices[0]) {
                    return;
                }

        if (mPowerStatusController.isPowerStatusTransientToStandby()) {
            mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_STANDBY);
            for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
                device.onStandby(mStandbyMessageReceived, standbyAction);
                releaseWakeLock();
                if (isAudioSystemDevice() || !isPowerStandby()) {
                    return;
                }
            if (!isAudioSystemDevice()) {
                mCecController.enableSystemCecControl(false);
                mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED);
            }
        }
        };

        if (mPowerStatusController.isPowerStatusTransientToStandby()) {
            mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_STANDBY);
            for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
                device.onStandby(mStandbyMessageReceived, standbyAction, callback);
            }
        }
        // Always reset this flag to set up for the next standby
        mStandbyMessageReceived = false;
    }

    private boolean shouldAcquireWakeLock() {
        boolean sendStandbyOnSleep = false;
        if (tv() != null) {
            sendStandbyOnSleep = mHdmiCecConfig.getIntValue(
                    HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP)
                    == TV_SEND_STANDBY_ON_SLEEP_ENABLED;
        } else if (playback() != null) {
            sendStandbyOnSleep = !mHdmiCecConfig.getStringValue(
                            HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)
                    .equals(POWER_CONTROL_MODE_NONE);
        }

        return isCecControlEnabled() && isPowerOnOrTransient() && sendStandbyOnSleep;
    }

    /**
     * Acquire the wake lock used to hold the thread until the standby process is finished.
     */
    @VisibleForTesting
    protected void acquireWakeLock() {
        releaseWakeLock();
        mWakeLock = mPowerManager.newWakeLock(
                PowerManager.PARTIAL_WAKE_LOCK, TAG);
        mWakeLock.acquire(DEVICE_CLEANUP_TIMEOUT);
    }

    /**
     * Release the wake lock acquired when the standby process started.
     */
    @VisibleForTesting
    protected void releaseWakeLock() {
        if (mWakeLock != null) {
            try {
                if (mWakeLock.isHeld()) {
                    mWakeLock.release();
                }
            } catch (RuntimeException e) {
                Slog.w(TAG, "Exception when releasing wake lock.");
            }
            mWakeLock = null;
        }
    }

    @VisibleForTesting
    void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {
        VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, vendorId);
Loading