DeviceSelectAction.java revision 5352081c662299b618335bf3024058fa04ef2dfd
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 17package com.android.server.hdmi; 18 19import android.hardware.hdmi.HdmiDeviceInfo; 20import android.hardware.hdmi.HdmiControlManager; 21import android.hardware.hdmi.HdmiTvClient; 22import android.hardware.hdmi.IHdmiControlCallback; 23import android.os.RemoteException; 24import android.util.Slog; 25 26import com.android.server.hdmi.HdmiControlService.SendMessageCallback; 27 28/** 29 * Handles an action that selects a logical device as a new active source. 30 * 31 * Triggered by {@link HdmiTvClient}, attempts to select the given target device 32 * for a new active source. It does its best to wake up the target in standby mode 33 * before issuing the command >Set Stream path<. 34 */ 35final class DeviceSelectAction extends HdmiCecFeatureAction { 36 private static final String TAG = "DeviceSelect"; 37 38 // Time in milliseconds we wait for the device power status to switch to 'Standby' 39 private static final int TIMEOUT_TRANSIT_TO_STANDBY_MS = 5 * 1000; 40 41 // Time in milliseconds we wait for the device power status to turn to 'On'. 42 private static final int TIMEOUT_POWER_ON_MS = 5 * 1000; 43 44 // The number of times we try to wake up the target device before we give up 45 // and just send <Set Stream Path>. 46 private static final int LOOP_COUNTER_MAX = 20; 47 48 // State in which we wait for <Report Power Status> to come in response to the command 49 // <Give Device Power Status> we have sent. 50 private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 1; 51 52 // State in which we wait for the device power status to switch to 'Standby'. 53 // We wait till the status becomes 'Standby' before we send <Set Stream Path> 54 // to wake up the device again. 55 private static final int STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY = 2; 56 57 // State in which we wait for the device power status to switch to 'on'. We wait 58 // maximum 100 seconds (20 * 5) before we give up and just send <Set Stream Path>. 59 private static final int STATE_WAIT_FOR_DEVICE_POWER_ON = 3; 60 61 private final HdmiDeviceInfo mTarget; 62 private final IHdmiControlCallback mCallback; 63 private final HdmiCecMessage mGivePowerStatus; 64 65 private int mPowerStatusCounter = 0; 66 67 /** 68 * Constructor. 69 * 70 * @param source {@link HdmiCecLocalDevice} instance 71 * @param target target logical device that will be a new active source 72 * @param callback callback object 73 */ 74 public DeviceSelectAction(HdmiCecLocalDeviceTv source, 75 HdmiDeviceInfo target, IHdmiControlCallback callback) { 76 super(source); 77 mCallback = callback; 78 mTarget = target; 79 mGivePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( 80 getSourceAddress(), getTargetAddress()); 81 } 82 83 int getTargetAddress() { 84 return mTarget.getLogicalAddress(); 85 } 86 87 @Override 88 public boolean start() { 89 // Seq #9 90 queryDevicePowerStatus(); 91 return true; 92 } 93 94 private void queryDevicePowerStatus() { 95 sendCommand(mGivePowerStatus, new SendMessageCallback() { 96 @Override 97 public void onSendCompleted(int error) { 98 if (error != Constants.SEND_RESULT_SUCCESS) { 99 invokeCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED); 100 finish(); 101 return; 102 } 103 } 104 }); 105 mState = STATE_WAIT_FOR_REPORT_POWER_STATUS; 106 addTimer(mState, HdmiConfig.TIMEOUT_MS); 107 } 108 109 @Override 110 public boolean processCommand(HdmiCecMessage cmd) { 111 if (cmd.getSource() != getTargetAddress()) { 112 return false; 113 } 114 int opcode = cmd.getOpcode(); 115 byte[] params = cmd.getParams(); 116 117 switch (mState) { 118 case STATE_WAIT_FOR_REPORT_POWER_STATUS: 119 if (opcode == Constants.MESSAGE_REPORT_POWER_STATUS) { 120 return handleReportPowerStatus(params[0]); 121 } 122 return false; 123 default: 124 break; 125 } 126 return false; 127 } 128 129 private boolean handleReportPowerStatus(int powerStatus) { 130 switch (powerStatus) { 131 case HdmiControlManager.POWER_STATUS_ON: 132 sendSetStreamPath(); 133 return true; 134 case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY: 135 if (mPowerStatusCounter < 4) { 136 mState = STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY; 137 addTimer(mState, TIMEOUT_TRANSIT_TO_STANDBY_MS); 138 } else { 139 sendSetStreamPath(); 140 } 141 return true; 142 case HdmiControlManager.POWER_STATUS_STANDBY: 143 if (mPowerStatusCounter == 0) { 144 turnOnDevice(); 145 } else { 146 sendSetStreamPath(); 147 } 148 return true; 149 case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON: 150 if (mPowerStatusCounter < LOOP_COUNTER_MAX) { 151 mState = STATE_WAIT_FOR_DEVICE_POWER_ON; 152 addTimer(mState, TIMEOUT_POWER_ON_MS); 153 } else { 154 sendSetStreamPath(); 155 } 156 return true; 157 } 158 return false; 159 } 160 161 private void turnOnDevice() { 162 sendUserControlPressedAndReleased(mTarget.getLogicalAddress(), 163 HdmiCecKeycode.CEC_KEYCODE_POWER); 164 sendUserControlPressedAndReleased(mTarget.getLogicalAddress(), 165 HdmiCecKeycode.CEC_KEYCODE_POWER_ON_FUNCTION); 166 mState = STATE_WAIT_FOR_DEVICE_POWER_ON; 167 addTimer(mState, TIMEOUT_POWER_ON_MS); 168 } 169 170 private void sendSetStreamPath() { 171 sendCommand(HdmiCecMessageBuilder.buildSetStreamPath( 172 getSourceAddress(), mTarget.getPhysicalAddress())); 173 invokeCallback(HdmiControlManager.RESULT_SUCCESS); 174 finish(); 175 } 176 177 @Override 178 public void handleTimerEvent(int timeoutState) { 179 if (mState != timeoutState) { 180 Slog.w(TAG, "Timer in a wrong state. Ignored."); 181 return; 182 } 183 switch (mState) { 184 case STATE_WAIT_FOR_REPORT_POWER_STATUS: 185 if (tv().isPowerStandbyOrTransient()) { 186 invokeCallback(HdmiControlManager.RESULT_INCORRECT_MODE); 187 finish(); 188 return; 189 } 190 sendSetStreamPath(); 191 break; 192 case STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY: 193 case STATE_WAIT_FOR_DEVICE_POWER_ON: 194 mPowerStatusCounter++; 195 queryDevicePowerStatus(); 196 break; 197 } 198 } 199 200 private void invokeCallback(int result) { 201 if (mCallback == null) { 202 return; 203 } 204 try { 205 mCallback.onComplete(result); 206 } catch (RemoteException e) { 207 Slog.e(TAG, "Callback failed:" + e); 208 } 209 } 210} 211