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