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

Commit 735655b4 authored by Jungshik Jang's avatar Jungshik Jang Committed by Android (Google) Code Review
Browse files

Merge "Implement PowerStatusMonitorAction." into lmp-dev

parents 58d467a5 410ca9c7
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -617,6 +617,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
                        }

                        addAndStartAction(new HotplugDetectionAction(HdmiCecLocalDeviceTv.this));
                        addAndStartAction(new PowerStatusMonitorAction(HdmiCecLocalDeviceTv.this));

                        // If there is AVR, initiate System Audio Auto initiation action,
                        // which turns on and off system audio according to last system
@@ -1300,6 +1301,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
        //     LocalDeviceTv.onAddressAllocated() -> launchDeviceDiscovery().
        removeAction(DeviceDiscoveryAction.class);
        removeAction(HotplugDetectionAction.class);
        removeAction(PowerStatusMonitorAction.class);
        // Remove recording actions.
        removeAction(OneTouchRecordAction.class);
        removeAction(TimerRecordingAction.class);
@@ -1521,4 +1523,22 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
            }
        });
    }

    void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) {
        HdmiDeviceInfo info = getDeviceInfo(logicalAddress);
        if (info == null) {
            Slog.w(TAG, "Can not update power status of non-existing device:" + logicalAddress);
            return;
        }

        if (info.getDevicePowerStatus() == newPowerStatus) {
            return;
        }

        HdmiDeviceInfo newInfo = HdmiUtils.cloneHdmiDeviceInfo(info, newPowerStatus);
        // addDeviceInfo replaces old device info with new one if exists.
        addDeviceInfo(newInfo);

        // TODO: notify this update to others.
    }
}
+5 −2
Original line number Diff line number Diff line
@@ -317,7 +317,10 @@ public final class HdmiControlService extends SystemService {
                    if (logicalAddress == Constants.ADDR_UNREGISTERED) {
                        Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
                    } else {
                        HdmiDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType);
                        // Set POWER_STATUS_ON to all local devices because they share lifetime
                        // with system.
                        HdmiDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType,
                                HdmiControlManager.POWER_STATUS_ON);
                        localDevice.setDeviceInfo(deviceInfo);
                        mCecController.addLocalDevice(deviceType, localDevice);
                        mCecController.addLogicalAddress(logicalAddress);
@@ -653,7 +656,7 @@ public final class HdmiControlService extends SystemService {
        }
    }

    private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
    private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType, int powerStatus) {
        // TODO: find better name instead of model name.
        String displayName = Build.MODEL;
        return new HdmiDeviceInfo(logicalAddress,
+10 −0
Original line number Diff line number Diff line
@@ -266,4 +266,14 @@ final class HdmiUtils {
        }
        return true;
    }

    /**
     * Clone {@link HdmiDeviceInfo} with new power status.
     */
    static HdmiDeviceInfo cloneHdmiDeviceInfo(HdmiDeviceInfo info, int newPowerStatus) {
        return new HdmiDeviceInfo(info.getLogicalAddress(),
                info.getPhysicalAddress(), info.getPortId(), info.getDeviceType(),
                info.getVendorId(), info.getDisplayName(), newPowerStatus);
    }

}
+146 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 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 static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_UNKNOWN;

import android.hardware.hdmi.HdmiDeviceInfo;
import android.util.SparseIntArray;

import com.android.server.hdmi.HdmiControlService.SendMessageCallback;

import java.util.List;

/**
 * Action that check each device's power status.
 */
public class PowerStatusMonitorAction extends HdmiCecFeatureAction {
    private static final String TAG = "PowerStatusMonitorAction";

    // State that waits for <Report Power Status> once sending <Give Device Power Status>
    // to all external devices.
    private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 1;
    // State that waits for next monitoring
    private static final int STATE_WAIT_FOR_NEXT_MONITORING = 2;

    private static final int INVALID_POWER_STATUS = POWER_STATUS_UNKNOWN - 1;

    // Monitoring interval (60s)
    private static final int MONITIROING_INTERNAL_MS = 60000;

    // Timeout once sending <Give Device Power Status>
    private static final int REPORT_POWER_STATUS_TIMEOUT_MS = 5000;

    // Container for current power status of all external devices.
    // The key is a logical address a device and the value is current power status of it
    // Whenever the action receives <Report Power Status> from a device,
    // it removes an entry of the given device.
    // If this is non-empty when timeout for STATE_WAIT_FOR_REPORT_POWER_STATUS happens,
    // updates power status of all remaining devices into POWER_STATUS_UNKNOWN.
    private final SparseIntArray mPowerStatus = new SparseIntArray();

    PowerStatusMonitorAction(HdmiCecLocalDevice source) {
        super(source);
    }

    @Override
    boolean start() {
        queryPowerStatus();
        return true;
    }

    @Override
    boolean processCommand(HdmiCecMessage cmd) {
        if (mState != STATE_WAIT_FOR_REPORT_POWER_STATUS) {
            return false;
        }
        return handleReportPowerStatus(cmd);
    }

    private boolean handleReportPowerStatus(HdmiCecMessage cmd) {
        int sourceAddress = cmd.getSource();
        int oldStatus = mPowerStatus.get(sourceAddress, INVALID_POWER_STATUS);
        if (oldStatus == INVALID_POWER_STATUS) {
            // if no device exists for incoming message, hands it over to other actions.
            return false;
        }
        int newStatus = cmd.getParams()[0];
        updatePowerStatus(sourceAddress, newStatus, true);
        return true;
    }

    @Override
    void handleTimerEvent(int state) {
        switch (mState) {
            case STATE_WAIT_FOR_NEXT_MONITORING:
                queryPowerStatus();
                break;
            case STATE_WAIT_FOR_REPORT_POWER_STATUS:
                handleTimeout();
                break;
        }
    }

    private void handleTimeout() {
        for (int i = 0; i < mPowerStatus.size(); ++i) {
            int logicalAddress = mPowerStatus.keyAt(i);
            updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, false);
        }
        mPowerStatus.clear();
        mState = STATE_WAIT_FOR_NEXT_MONITORING;
    }

    private void resetPowerStatus(List<HdmiDeviceInfo> deviceInfos) {
        mPowerStatus.clear();
        for (HdmiDeviceInfo info : deviceInfos) {
            mPowerStatus.append(info.getLogicalAddress(), info.getDevicePowerStatus());
        }
    }

    private void queryPowerStatus() {
        List<HdmiDeviceInfo> deviceInfos = tv().getDeviceInfoList(false);
        resetPowerStatus(deviceInfos);
        for (HdmiDeviceInfo info : deviceInfos) {
            final int logicalAddress = info.getLogicalAddress();
            sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
                    logicalAddress),
                    new SendMessageCallback() {
                        @Override
                        public void onSendCompleted(int error) {
                            // If fails to send <Give Device Power Status>,
                            // update power status into UNKNOWN.
                            if (error != Constants.SEND_RESULT_SUCCESS) {
                               updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, true);
                            }
                        }
                    });
        }

        mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;

        // Add both timers, monitoring and timeout.
        addTimer(STATE_WAIT_FOR_NEXT_MONITORING, MONITIROING_INTERNAL_MS);
        addTimer(STATE_WAIT_FOR_REPORT_POWER_STATUS, REPORT_POWER_STATUS_TIMEOUT_MS);
    }

    private void updatePowerStatus(int logicalAddress, int newStatus, boolean remove) {
        tv().updateDevicePowerStatus(logicalAddress, newStatus);

        if (remove) {
            mPowerStatus.delete(logicalAddress);
        }
    }
}