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

Commit 4fc1d105 authored by Jungshik Jang's avatar Jungshik Jang
Browse files

Revamp disabling device in local device.

When a cec deivce turns into disabled mode such as power off, standby,
and cec feature disabled, cec service should execute cleanup tasks.
Including cleaning up all feature actions, each device should run
their own cleanup.
It should stop system audio mode and arc for tv, while it should
send inactive source for for playback device.
Along with this, to prevent stale feature action, added timeout
to local device so that if there is stale action, it enforce to
finish it.

Bug: 16118520

Change-Id: I5ce30ab0f4459b6e2834f8d31b6a7ff789b35d07
parent b2492254
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -276,6 +276,12 @@ final class HdmiCecController {
        nativeClearLogicalAddress(mNativePtr);
    }

    @ServiceThreadOnly
    void clearLocalDevices() {
        assertRunOnServiceThread();
        mLocalDevices.clear();
    }

    /**
     * Return the physical address of the device.
     *
+59 −13
Original line number Diff line number Diff line
@@ -17,7 +17,9 @@
package com.android.server.hdmi;

import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
@@ -35,6 +37,11 @@ import java.util.List;
abstract class HdmiCecLocalDevice {
    private static final String TAG = "HdmiCecLocalDevice";

    private static final int MSG_DISABLE_DEVICE_TIMEOUT = 1;
    // 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;

    protected final HdmiControlService mService;
    protected final int mDeviceType;
    protected int mAddress;
@@ -58,6 +65,27 @@ abstract class HdmiCecLocalDevice {
    // Note that access to this collection should happen in service thread.
    private final LinkedList<FeatureAction> mActions = new LinkedList<>();

    private Handler mHandler = new Handler () {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_DISABLE_DEVICE_TIMEOUT:
                    handleDisableDeviceTimeout();
                    break;
            }
        }
    };

    /**
     * A callback interface to get notified when all pending action is cleared.
     * It can be called when timeout happened.
     */
    interface PendingActionClearedCallback {
        void onCleared(HdmiCecLocalDevice device);
    }

    protected PendingActionClearedCallback mPendingActionClearedCallback;

    protected HdmiCecLocalDevice(HdmiControlService service, int deviceType) {
        mService = service;
        mDeviceType = deviceType;
@@ -482,10 +510,11 @@ abstract class HdmiCecLocalDevice {
    }

    protected void checkIfPendingActionsCleared() {
        if (mActions.isEmpty()) {
            mService.onPendingActionsCleared();
        if (mActions.isEmpty() && mPendingActionClearedCallback != null) {
            mPendingActionClearedCallback.onCleared(this);
        }
    }

    protected void assertRunOnServiceThread() {
        if (Looper.myLooper() != mService.getServiceLooper()) {
            throw new IllegalStateException("Should run on service thread.");
@@ -578,28 +607,45 @@ abstract class HdmiCecLocalDevice {
    }

    /**
     * Called when the system started transition to standby mode.
     * Called when the system goes to standby mode.
     *
     * @param initiatedByCec true if this power sequence is initiated
     *         by the reception the CEC messages like &lt;StandBy&gt;
     *         by the reception the CEC messages like &lt;Standby&gt;
     */
    protected void onTransitionToStandby(boolean initiatedByCec) {
        // If there are no outstanding actions, we'll go to STANDBY state.
        checkIfPendingActionsCleared();
    }
    protected void onStandby(boolean initiatedByCec) {}

    /**
     * Called when the system goes to standby mode.
     * Disable device. {@code callback} is used to get notified when all pending
     * actions are completed or timeout is issued.
     *
     * @param initiatedByCec true if this power sequence is initiated
     *         by the reception the CEC messages like &lt;StandBy&gt;
     * @param initiatedByCec true if this sequence is initiated
     *         by the reception the CEC messages like &lt;Standby&gt;
     * @param callback callback interface to get notified when all pending actions are cleared
     */
    protected void onStandBy(boolean initiatedByCec) {}
    protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
        mPendingActionClearedCallback = callback;
        mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_DISABLE_DEVICE_TIMEOUT),
                DEVICE_CLEANUP_TIMEOUT);
    }

    @ServiceThreadOnly
    private void handleDisableDeviceTimeout() {
        assertRunOnServiceThread();

        // If all actions are not cleared in DEVICE_CLEANUP_TIMEOUT, enforce to finish them.
        // onCleard will be called at the last action's finish method.
        Iterator<FeatureAction> iter = mActions.iterator();
        while (iter.hasNext()) {
            FeatureAction action = iter.next();
            action.finish();
            iter.remove();
        }
    }

    /**
     * Send a key event to other device.
     *
     * @param keyCode key code defined in {@link android.os.KeyEvent}
     * @param keyCode key code defined in {@link android.view.KeyEvent}
     * @param isPressed {@code true} for key down event
     */
    protected void sendKeyEvent(int keyCode, boolean isPressed) {
+3 −1
Original line number Diff line number Diff line
@@ -141,7 +141,9 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {

    @Override
    @ServiceThreadOnly
    protected void onTransitionToStandby(boolean initiatedByCec) {
    protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
        super.disableDevice(initiatedByCec, callback);

        assertRunOnServiceThread();
        if (!initiatedByCec && mIsActiveSource) {
            mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(
+41 −2
Original line number Diff line number Diff line
@@ -1034,19 +1034,58 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {

    @Override
    @ServiceThreadOnly
    protected void onTransitionToStandby(boolean initiatedByCec) {
    protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
        super.disableDevice(initiatedByCec, callback);
        assertRunOnServiceThread();
        // Remove any repeated working actions.
        // HotplugDetectionAction will be reinstated during the wake up process.
        // HdmiControlService.onWakeUp() -> initializeLocalDevices() ->
        //     LocalDeviceTv.onAddressAllocated() -> launchDeviceDiscovery().
        removeAction(DeviceDiscoveryAction.class);
        removeAction(HotplugDetectionAction.class);

        disableSystemAudioIfExist();
        disableArcIfExist();
        checkIfPendingActionsCleared();
    }

    @ServiceThreadOnly
    private void disableSystemAudioIfExist() {
        assertRunOnServiceThread();
        if (getAvrDeviceInfo() == null) {
            return;
        }

        // Seq #31.
        removeAction(SystemAudioActionFromAvr.class);
        removeAction(SystemAudioActionFromTv.class);
        removeAction(SystemAudioAutoInitiationAction.class);
        removeAction(SystemAudioStatusAction.class);
        removeAction(VolumeControlAction.class);

        // Once adding additional param which describes whether to record it to NVM or not to this
        // method, put "false" for it.
        setSystemAudioMode(false);
    }

    @ServiceThreadOnly
    private void disableArcIfExist() {
        assertRunOnServiceThread();
        HdmiCecDeviceInfo avr = getAvrDeviceInfo();
        if (avr == null) {
            return;
        }

        // Seq #44.
        removeAction(RequestArcInitiationAction.class);
        if (!hasAction(RequestArcTerminationAction.class) && isArcEstabilished()) {
            addAndStartAction(new RequestArcTerminationAction(this, avr.getLogicalAddress()));
        }
    }

    @Override
    @ServiceThreadOnly
    protected void onStandBy(boolean initiatedByCec) {
    protected void onStandby(boolean initiatedByCec) {
        assertRunOnServiceThread();
        // Seq #11
        if (!mService.isControlEnabled()) {
+1 −1
Original line number Diff line number Diff line
@@ -274,7 +274,7 @@ public class HdmiCecMessageBuilder {
     * @return newly created {@link HdmiCecMessage}
     */
    static HdmiCecMessage buildInactiveSource(int src, int physicalAddress) {
        return buildCommand(src, Constants.ADDR_BROADCAST,
        return buildCommand(src, Constants.ADDR_TV,
                Constants.MESSAGE_INACTIVE_SOURCE, physicalAddressToParam(physicalAddress));
    }

Loading