OneTouchPlayAction.java revision 79c58a4b97f27ede6a1b680d2fece9c2a0edf7b7
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 */ 16package com.android.server.hdmi; 17 18import android.hardware.hdmi.IHdmiControlCallback; 19import android.hardware.hdmi.HdmiCec; 20import android.hardware.hdmi.HdmiCecMessage; 21import android.os.RemoteException; 22import android.util.Slog; 23 24/** 25 * Feature action that performs one touch play against TV/Display device. 26 * 27 * This action is initiated via {@link HdmiControlManager#oneTouchPlay()} from 28 * the Android system working as playback device to turn on the TV, and switch the input. 29 * 30 * <p>Package-private, accessed by {@link HdmiControlService} only. 31 */ 32 33final class OneTouchPlayAction extends FeatureAction { 34 private static final String TAG = "OneTouchPlayAction"; 35 36 // State in which the action is waiting for <Report Power Status>. In normal situation 37 // source device can simply send <Text|Image View On> and <Active Source> in succession 38 // since the standard requires that the TV/Display should buffer the <Active Source> 39 // if the TV is brought of out standby state. 40 // 41 // But there are TV's that fail to buffer the <Active Source> while getting out of 42 // standby mode, and do not accept the command until their power status becomes 'ON'. 43 // For a workaround, we send <Give Device Power Status> commands periodically to make sure 44 // the device switches its status to 'ON'. Then we send additional <Active Source>. 45 private static final int STATE_WAITING_FOR_REPORT_POWER_STATUS = 1; 46 47 // The maximum number of times we send <Give Device Power Status> before we give up. 48 // We wait up to RESPONSE_TIMEOUT_MS * LOOP_COUNTER_MAX = 20 seconds. 49 private static final int LOOP_COUNTER_MAX = 10; 50 51 private final int mTargetAddress; 52 private final IHdmiControlCallback mCallback; 53 54 private int mPowerStatusCounter = 0; 55 56 // Factory method. Ensures arguments are valid. 57 static OneTouchPlayAction create(HdmiCecLocalDevice source, 58 int targetAddress, IHdmiControlCallback callback) { 59 if (source == null || callback == null) { 60 Slog.e(TAG, "Wrong arguments"); 61 return null; 62 } 63 return new OneTouchPlayAction(source, targetAddress, 64 callback); 65 } 66 67 private OneTouchPlayAction(HdmiCecLocalDevice localDevice, int targetAddress, 68 IHdmiControlCallback callback) { 69 super(localDevice); 70 mTargetAddress = targetAddress; 71 mCallback = callback; 72 } 73 74 @Override 75 boolean start() { 76 sendCommand(HdmiCecMessageBuilder.buildTextViewOn(getSourceAddress(), mTargetAddress)); 77 broadcastActiveSource(); 78 queryDevicePowerStatus(); 79 mState = STATE_WAITING_FOR_REPORT_POWER_STATUS; 80 addTimer(mState, FeatureAction.TIMEOUT_MS); 81 return true; 82 } 83 84 private void broadcastActiveSource() { 85 sendCommand(HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(), getSourcePath())); 86 } 87 88 private void queryDevicePowerStatus() { 89 sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), 90 mTargetAddress)); 91 } 92 93 @Override 94 boolean processCommand(HdmiCecMessage cmd) { 95 if (mState != STATE_WAITING_FOR_REPORT_POWER_STATUS) { 96 return false; 97 } 98 if (cmd.getOpcode() == HdmiCec.MESSAGE_REPORT_POWER_STATUS) { 99 int status = cmd.getParams()[0]; 100 if (status == HdmiCec.POWER_STATUS_ON) { 101 broadcastActiveSource(); 102 invokeCallback(HdmiCec.RESULT_SUCCESS); 103 finish(); 104 } 105 return true; 106 } 107 return false; 108 } 109 110 @Override 111 void handleTimerEvent(int state) { 112 if (mState != state) { 113 return; 114 } 115 if (state == STATE_WAITING_FOR_REPORT_POWER_STATUS) { 116 if (mPowerStatusCounter++ < LOOP_COUNTER_MAX) { 117 queryDevicePowerStatus(); 118 addTimer(mState, FeatureAction.TIMEOUT_MS); 119 } else { 120 // Couldn't wake up the TV for whatever reason. Report failure. 121 invokeCallback(HdmiCec.RESULT_TIMEOUT); 122 finish(); 123 } 124 } 125 } 126 127 private void invokeCallback(int result) { 128 try { 129 mCallback.onComplete(result); 130 } catch (RemoteException e) { 131 Slog.e(TAG, "Callback failed:" + e); 132 } 133 } 134} 135