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