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.hardware.tv.cec.V1_0.SendMessageResult; 22 import android.util.SparseIntArray; 23 import com.android.server.hdmi.HdmiControlService.SendMessageCallback; 24 import java.util.List; 25 26 /** 27 * Action that check each device's power status. 28 */ 29 public class PowerStatusMonitorAction extends HdmiCecFeatureAction { 30 private static final String TAG = "PowerStatusMonitorAction"; 31 32 // State that waits for <Report Power Status> once sending <Give Device Power Status> 33 // to all external devices. 34 private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 1; 35 // State that waits for next monitoring 36 private static final int STATE_WAIT_FOR_NEXT_MONITORING = 2; 37 38 private static final int INVALID_POWER_STATUS = POWER_STATUS_UNKNOWN - 1; 39 40 // Monitoring interval (60s) 41 private static final int MONITIROING_INTERNAL_MS = 60000; 42 43 // Timeout once sending <Give Device Power Status> 44 private static final int REPORT_POWER_STATUS_TIMEOUT_MS = 5000; 45 46 // Container for current power status of all external devices. 47 // The key is a logical address a device and the value is current power status of it 48 // Whenever the action receives <Report Power Status> from a device, 49 // it removes an entry of the given device. 50 // If this is non-empty when timeout for STATE_WAIT_FOR_REPORT_POWER_STATUS happens, 51 // updates power status of all remaining devices into POWER_STATUS_UNKNOWN. 52 private final SparseIntArray mPowerStatus = new SparseIntArray(); 53 54 PowerStatusMonitorAction(HdmiCecLocalDevice source) { 55 super(source); 56 } 57 58 @Override 59 boolean start() { 60 queryPowerStatus(); 61 return true; 62 } 63 64 @Override 65 boolean processCommand(HdmiCecMessage cmd) { 66 if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS 67 && cmd.getOpcode() == Constants.MESSAGE_REPORT_POWER_STATUS) { 68 return handleReportPowerStatus(cmd); 69 } 70 return false; 71 } 72 73 private boolean handleReportPowerStatus(HdmiCecMessage cmd) { 74 int sourceAddress = cmd.getSource(); 75 int oldStatus = mPowerStatus.get(sourceAddress, INVALID_POWER_STATUS); 76 if (oldStatus == INVALID_POWER_STATUS) { 77 // if no device exists for incoming message, hands it over to other actions. 78 return false; 79 } 80 int newStatus = cmd.getParams()[0] & 0xFF; 81 updatePowerStatus(sourceAddress, newStatus, true); 82 return true; 83 } 84 85 @Override 86 void handleTimerEvent(int state) { 87 switch (mState) { 88 case STATE_WAIT_FOR_NEXT_MONITORING: 89 queryPowerStatus(); 90 break; 91 case STATE_WAIT_FOR_REPORT_POWER_STATUS: 92 handleTimeout(); 93 break; 94 } 95 } 96 97 private void handleTimeout() { 98 for (int i = 0; i < mPowerStatus.size(); ++i) { 99 int logicalAddress = mPowerStatus.keyAt(i); 100 updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, false); 101 } 102 mPowerStatus.clear(); 103 mState = STATE_WAIT_FOR_NEXT_MONITORING; 104 } 105 106 private void resetPowerStatus(List<HdmiDeviceInfo> deviceInfos) { 107 mPowerStatus.clear(); 108 for (HdmiDeviceInfo info : deviceInfos) { 109 mPowerStatus.append(info.getLogicalAddress(), info.getDevicePowerStatus()); 110 } 111 } 112 113 private void queryPowerStatus() { 114 List<HdmiDeviceInfo> deviceInfos = tv().getDeviceInfoList(false); 115 resetPowerStatus(deviceInfos); 116 for (HdmiDeviceInfo info : deviceInfos) { 117 final int logicalAddress = info.getLogicalAddress(); 118 sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), 119 logicalAddress), 120 new SendMessageCallback() { 121 @Override 122 public void onSendCompleted(int error) { 123 // If fails to send <Give Device Power Status>, 124 // update power status into UNKNOWN. 125 if (error != SendMessageResult.SUCCESS) { 126 updatePowerStatus(logicalAddress, POWER_STATUS_UNKNOWN, true); 127 } 128 } 129 }); 130 } 131 132 mState = STATE_WAIT_FOR_REPORT_POWER_STATUS; 133 134 // Add both timers, monitoring and timeout. 135 addTimer(STATE_WAIT_FOR_NEXT_MONITORING, MONITIROING_INTERNAL_MS); 136 addTimer(STATE_WAIT_FOR_REPORT_POWER_STATUS, REPORT_POWER_STATUS_TIMEOUT_MS); 137 } 138 139 private void updatePowerStatus(int logicalAddress, int newStatus, boolean remove) { 140 tv().updateDevicePowerStatus(logicalAddress, newStatus); 141 142 if (remove) { 143 mPowerStatus.delete(logicalAddress); 144 } 145 } 146 } 147