/* * 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 once sending // 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 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 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 && cmd.getOpcode() == Constants.MESSAGE_REPORT_POWER_STATUS) { return handleReportPowerStatus(cmd); } return false; } 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] & 0xFF; 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 deviceInfos) { mPowerStatus.clear(); for (HdmiDeviceInfo info : deviceInfos) { mPowerStatus.append(info.getLogicalAddress(), info.getDevicePowerStatus()); } } private void queryPowerStatus() { List 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 , // 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); } } }