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