1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.server.hdmi; 17 18 import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_UNKNOWN; 19 20 import android.hardware.hdmi.HdmiDeviceInfo; 21 import android.util.SparseIntArray; 22 23 import com.android.server.hdmi.HdmiControlService.SendMessageCallback; 24 25 import java.util.List; 26 27 /** 28 * Action that check each device's power status. 29 */ 30 public class PowerStatusMonitorAction extends HdmiCecFeatureAction { 31 private static final String TAG = "PowerStatusMonitorAction"; 32 33 // State that waits for <Report Power Status> once sending <Give Device Power Status> 34 // to all external devices. 35 private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 1; 36 // State that waits for next monitoring 37 private static final int STATE_WAIT_FOR_NEXT_MONITORING = 2; 38 39 private static final int INVALID_POWER_STATUS = POWER_STATUS_UNKNOWN - 1; 40 41 // Monitoring interval (60s) 42 private static final int MONITIROING_INTERNAL_MS = 60000; 43 44 // Timeout once sending <Give Device Power Status> 45 private static final int REPORT_POWER_STATUS_TIMEOUT_MS = 5000; 46 47 // Container for current power status of all external devices. 48 // The key is a logical address a device and the value is current power status of it 49 // Whenever the action receives <Report Power Status> from a device, 50 // it removes an entry of the given device. 51 // If this is non-empty when timeout for STATE_WAIT_FOR_REPORT_POWER_STATUS happens, 52 // updates power status of all remaining devices into POWER_STATUS_UNKNOWN. 53 private final SparseIntArray mPowerStatus = new SparseIntArray(); 54 55 PowerStatusMonitorAction(HdmiCecLocalDevice source) { 56 super(source); 57 } 58 59 @Override 60 boolean start() { 61 queryPowerStatus(); 62 return true; 63 } 64 65 @Override 66 boolean processCommand(HdmiCecMessage cmd) { 67 if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS 68 && cmd.getOpcode() == Constants.MESSAGE_REPORT_POWER_STATUS) { 69 return handleReportPowerStatus(cmd); 70 } 71 return false; 72 } 73 74 private boolean handleReportPowerStatus(HdmiCecMessage cmd) { 75 int sourceAddress = cmd.getSource(); 76 int oldStatus = mPowerStatus.get(sourceAddress, INVALID_POWER_STATUS); 77 if (oldStatus == INVALID_POWER_STATUS) { 78 // if no device exists for incoming message, hands it over to other actions. 79 return false; 80 } 81 int newStatus = cmd.getParams()[0] & 0xFF; 82 updatePowerStatus(sourceAddress, newStatus, true); 83 return true; 84 } 85 86 @Override 87 void handleTimerEvent(int state) { 88 switch (mState) { 89 case STATE_WAIT_FOR_NEXT_MONITORING: 90 queryPowerStatus(); 91 break; 92 case STATE_WAIT_FOR_REPORT_POWER_STATUS: 93 handleTimeout(); 94 break; 95 } 96 } 97 98 private void handleTimeout() { 99 for (int i = 0; i < mPowerStatus.size(); ++i) { 100 int logicalAddress = mPowerStatus.keyAt(i); 101 updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, false); 102 } 103 mPowerStatus.clear(); 104 mState = STATE_WAIT_FOR_NEXT_MONITORING; 105 } 106 107 private void resetPowerStatus(List<HdmiDeviceInfo> deviceInfos) { 108 mPowerStatus.clear(); 109 for (HdmiDeviceInfo info : deviceInfos) { 110 mPowerStatus.append(info.getLogicalAddress(), info.getDevicePowerStatus()); 111 } 112 } 113 114 private void queryPowerStatus() { 115 List<HdmiDeviceInfo> deviceInfos = tv().getDeviceInfoList(false); 116 resetPowerStatus(deviceInfos); 117 for (HdmiDeviceInfo info : deviceInfos) { 118 final int logicalAddress = info.getLogicalAddress(); 119 sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), 120 logicalAddress), 121 new SendMessageCallback() { 122 @Override 123 public void onSendCompleted(int error) { 124 // If fails to send <Give Device Power Status>, 125 // update power status into UNKNOWN. 126 if (error != Constants.SEND_RESULT_SUCCESS) { 127 updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, true); 128 } 129 } 130 }); 131 } 132 133 mState = STATE_WAIT_FOR_REPORT_POWER_STATUS; 134 135 // Add both timers, monitoring and timeout. 136 addTimer(STATE_WAIT_FOR_NEXT_MONITORING, MONITIROING_INTERNAL_MS); 137 addTimer(STATE_WAIT_FOR_REPORT_POWER_STATUS, REPORT_POWER_STATUS_TIMEOUT_MS); 138 } 139 140 private void updatePowerStatus(int logicalAddress, int newStatus, boolean remove) { 141 tv().updateDevicePowerStatus(logicalAddress, newStatus); 142 143 if (remove) { 144 mPowerStatus.delete(logicalAddress); 145 } 146 } 147 } 148