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 
     17 package com.android.server.hdmi;
     18 
     19 import android.annotation.Nullable;
     20 import android.hardware.hdmi.HdmiDeviceInfo;
     21 import android.hardware.hdmi.HdmiControlManager;
     22 import android.hardware.hdmi.IHdmiControlCallback;
     23 import android.os.RemoteException;
     24 import android.util.Slog;
     25 
     26 import java.util.List;
     27 
     28 /**
     29  * Base feature action class for SystemAudioActionFromTv and SystemAudioActionFromAvr.
     30  */
     31 abstract class SystemAudioAction extends HdmiCecFeatureAction {
     32     private static final String TAG = "SystemAudioAction";
     33 
     34     // Transient state to differentiate with STATE_NONE where the on-finished callback
     35     // will not be called.
     36     private static final int STATE_CHECK_ROUTING_IN_PRGRESS = 1;
     37 
     38     // State in which waits for <SetSystemAudioMode>.
     39     private static final int STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE = 2;
     40 
     41     private static final int MAX_SEND_RETRY_COUNT = 2;
     42 
     43     private static final int ON_TIMEOUT_MS = 5000;
     44     private static final int OFF_TIMEOUT_MS = HdmiConfig.TIMEOUT_MS;
     45 
     46     // Logical address of AV Receiver.
     47     protected final int mAvrLogicalAddress;
     48 
     49     // The target audio status of the action, whether to enable the system audio mode or not.
     50     protected boolean mTargetAudioStatus;
     51 
     52     @Nullable private final IHdmiControlCallback mCallback;
     53 
     54     private int mSendRetryCount = 0;
     55 
     56     /**
     57      * Constructor
     58      *
     59      * @param source {@link HdmiCecLocalDevice} instance
     60      * @param avrAddress logical address of AVR device
     61      * @param targetStatus Whether to enable the system audio mode or not
     62      * @param callback callback interface to be notified when it's done
     63      * @throw IllegalArugmentException if device type of sourceAddress and avrAddress is invalid
     64      */
     65     SystemAudioAction(HdmiCecLocalDevice source, int avrAddress, boolean targetStatus,
     66             IHdmiControlCallback callback) {
     67         super(source);
     68         HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
     69         mAvrLogicalAddress = avrAddress;
     70         mTargetAudioStatus = targetStatus;
     71         mCallback = callback;
     72     }
     73 
     74     // Seq #27
     75     protected void sendSystemAudioModeRequest() {
     76         List<RoutingControlAction> routingActions = getActions(RoutingControlAction.class);
     77         if (!routingActions.isEmpty()) {
     78             mState = STATE_CHECK_ROUTING_IN_PRGRESS;
     79             // Should have only one Routing Control Action
     80             RoutingControlAction routingAction = routingActions.get(0);
     81             routingAction.addOnFinishedCallback(this, new Runnable() {
     82                 @Override
     83                 public void run() {
     84                     sendSystemAudioModeRequestInternal();
     85                 }
     86             });
     87             return;
     88         }
     89         sendSystemAudioModeRequestInternal();
     90     }
     91 
     92     private void sendSystemAudioModeRequestInternal() {
     93         int avrPhysicalAddress = tv().getAvrDeviceInfo().getPhysicalAddress();
     94         HdmiCecMessage command = HdmiCecMessageBuilder.buildSystemAudioModeRequest(
     95                 getSourceAddress(),
     96                 mAvrLogicalAddress, avrPhysicalAddress, mTargetAudioStatus);
     97         sendCommand(command, new HdmiControlService.SendMessageCallback() {
     98             @Override
     99             public void onSendCompleted(int error) {
    100                 if (error != Constants.SEND_RESULT_SUCCESS) {
    101                     HdmiLogger.debug("Failed to send <System Audio Mode Request>:" + error);
    102                     setSystemAudioMode(false);
    103                     finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
    104                 }
    105             }
    106         });
    107         mState = STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE;
    108         addTimer(mState, mTargetAudioStatus ? ON_TIMEOUT_MS : OFF_TIMEOUT_MS);
    109     }
    110 
    111     private void handleSendSystemAudioModeRequestTimeout() {
    112         if (!mTargetAudioStatus  // Don't retry for Off case.
    113                 || mSendRetryCount++ >= MAX_SEND_RETRY_COUNT) {
    114             HdmiLogger.debug("[T]:wait for <Set System Audio Mode>.");
    115             setSystemAudioMode(false);
    116             finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
    117             return;
    118         }
    119         sendSystemAudioModeRequest();
    120     }
    121 
    122     protected void setSystemAudioMode(boolean mode) {
    123         tv().setSystemAudioMode(mode, true);
    124     }
    125 
    126     @Override
    127     final boolean processCommand(HdmiCecMessage cmd) {
    128         switch (mState) {
    129             case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE:
    130                 if (cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT
    131                         && (cmd.getParams()[0] & 0xFF)
    132                                 == Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST) {
    133                     HdmiLogger.debug("Failed to start system audio mode request.");
    134                     setSystemAudioMode(false);
    135                     finishWithCallback(HdmiControlManager.RESULT_EXCEPTION);
    136                     return true;
    137                 }
    138                 if (cmd.getOpcode() != Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE
    139                         || !HdmiUtils.checkCommandSource(cmd, mAvrLogicalAddress, TAG)) {
    140                     return false;
    141                 }
    142                 boolean receivedStatus = HdmiUtils.parseCommandParamSystemAudioStatus(cmd);
    143                 if (receivedStatus == mTargetAudioStatus) {
    144                     setSystemAudioMode(receivedStatus);
    145                     startAudioStatusAction();
    146                     return true;
    147                 } else {
    148                     HdmiLogger.debug("Unexpected system audio mode request:" + receivedStatus);
    149                     // Unexpected response, consider the request is newly initiated by AVR.
    150                     // To return 'false' will initiate new SystemAudioActionFromAvr by the control
    151                     // service.
    152                     finishWithCallback(HdmiControlManager.RESULT_EXCEPTION);
    153                     return false;
    154                 }
    155             default:
    156                 return false;
    157         }
    158     }
    159 
    160     protected void startAudioStatusAction() {
    161         addAndStartAction(new SystemAudioStatusAction(tv(), mAvrLogicalAddress, mCallback));
    162         finish();
    163     }
    164 
    165     protected void removeSystemAudioActionInProgress() {
    166         removeActionExcept(SystemAudioActionFromTv.class, this);
    167         removeActionExcept(SystemAudioActionFromAvr.class, this);
    168     }
    169 
    170     @Override
    171     final void handleTimerEvent(int state) {
    172         if (mState != state) {
    173             return;
    174         }
    175         switch (mState) {
    176             case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE:
    177                 handleSendSystemAudioModeRequestTimeout();
    178                 return;
    179         }
    180     }
    181 
    182     // TODO: if IHdmiControlCallback is general to other FeatureAction,
    183     //       move it into FeatureAction.
    184     protected void finishWithCallback(int returnCode) {
    185         if (mCallback != null) {
    186             try {
    187                 mCallback.onComplete(returnCode);
    188             } catch (RemoteException e) {
    189                 Slog.e(TAG, "Failed to invoke callback.", e);
    190             }
    191         }
    192         finish();
    193     }
    194 }
    195