blob: 0871194b268ff1d20814af2750fa04aeaeedee0a [file] [log] [blame]
Yuncheol Heo63a2e062014-05-27 23:06:01 +09001/*
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
Jungshik Jangea67c182014-06-19 22:19:20 +090019import android.annotation.Nullable;
Jungshik Jang61f4fbd2014-08-06 19:21:12 +090020import android.hardware.hdmi.HdmiDeviceInfo;
Jinsuk Kimc0c20d02014-07-04 14:34:31 +090021import android.hardware.hdmi.HdmiControlManager;
Jungshik Jangea67c182014-06-19 22:19:20 +090022import android.hardware.hdmi.IHdmiControlCallback;
23import android.os.RemoteException;
24import android.util.Slog;
Yuncheol Heo63a2e062014-05-27 23:06:01 +090025
Yuncheol Heoc516d652014-07-11 18:23:24 +090026import java.util.List;
27
Yuncheol Heo63a2e062014-05-27 23:06:01 +090028/**
29 * Base feature action class for SystemAudioActionFromTv and SystemAudioActionFromAvr.
30 */
Jungshik Jangb509c2e2014-08-07 13:45:01 +090031abstract class SystemAudioAction extends HdmiCecFeatureAction {
Yuncheol Heo63a2e062014-05-27 23:06:01 +090032 private static final String TAG = "SystemAudioAction";
33
Yuncheol Heoc516d652014-07-11 18:23:24 +090034 // 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
Yuncheol Heo63a2e062014-05-27 23:06:01 +090038 // State in which waits for <SetSystemAudioMode>.
Yuncheol Heoc516d652014-07-11 18:23:24 +090039 private static final int STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE = 2;
Yuncheol Heo63a2e062014-05-27 23:06:01 +090040
Yuncheol Heo63a2e062014-05-27 23:06:01 +090041 private static final int MAX_SEND_RETRY_COUNT = 2;
42
43 private static final int ON_TIMEOUT_MS = 5000;
Jinsuk Kim5fba96d2014-07-11 11:51:34 +090044 private static final int OFF_TIMEOUT_MS = HdmiConfig.TIMEOUT_MS;
Yuncheol Heo63a2e062014-05-27 23:06:01 +090045
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
Jungshik Jangea67c182014-06-19 22:19:20 +090052 @Nullable private final IHdmiControlCallback mCallback;
53
Yuncheol Heo63a2e062014-05-27 23:06:01 +090054 private int mSendRetryCount = 0;
55
56 /**
57 * Constructor
58 *
Jungshik Jang79c58a42014-06-16 16:45:36 +090059 * @param source {@link HdmiCecLocalDevice} instance
Yuncheol Heo63a2e062014-05-27 23:06:01 +090060 * @param avrAddress logical address of AVR device
61 * @param targetStatus Whether to enable the system audio mode or not
Jungshik Jangea67c182014-06-19 22:19:20 +090062 * @param callback callback interface to be notified when it's done
Yuncheol Heo63a2e062014-05-27 23:06:01 +090063 * @throw IllegalArugmentException if device type of sourceAddress and avrAddress is invalid
64 */
Jungshik Jangea67c182014-06-19 22:19:20 +090065 SystemAudioAction(HdmiCecLocalDevice source, int avrAddress, boolean targetStatus,
66 IHdmiControlCallback callback) {
Jungshik Jang79c58a42014-06-16 16:45:36 +090067 super(source);
Jungshik Jang61f4fbd2014-08-06 19:21:12 +090068 HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
Yuncheol Heo63a2e062014-05-27 23:06:01 +090069 mAvrLogicalAddress = avrAddress;
70 mTargetAudioStatus = targetStatus;
Jungshik Jangea67c182014-06-19 22:19:20 +090071 mCallback = callback;
Yuncheol Heo63a2e062014-05-27 23:06:01 +090072 }
73
Yuncheol Heoc516d652014-07-11 18:23:24 +090074 // Seq #27
Yuncheol Heo63a2e062014-05-27 23:06:01 +090075 protected void sendSystemAudioModeRequest() {
Yuncheol Heoc516d652014-07-11 18:23:24 +090076 List<RoutingControlAction> routingActions = getActions(RoutingControlAction.class);
77 if (!routingActions.isEmpty()) {
Jungshik Janga7221ce2014-08-28 16:35:30 +090078 mState = STATE_CHECK_ROUTING_IN_PRGRESS;
Yuncheol Heoc516d652014-07-11 18:23:24 +090079 // 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() {
Jungshik Jang79c58a42014-06-16 16:45:36 +090093 int avrPhysicalAddress = tv().getAvrDeviceInfo().getPhysicalAddress();
94 HdmiCecMessage command = HdmiCecMessageBuilder.buildSystemAudioModeRequest(
95 getSourceAddress(),
Yuncheol Heo63a2e062014-05-27 23:06:01 +090096 mAvrLogicalAddress, avrPhysicalAddress, mTargetAudioStatus);
97 sendCommand(command, new HdmiControlService.SendMessageCallback() {
98 @Override
99 public void onSendCompleted(int error) {
Jungshik Janga7221ce2014-08-28 16:35:30 +0900100 if (error != Constants.SEND_RESULT_SUCCESS) {
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900101 HdmiLogger.debug("Failed to send <System Audio Mode Request>:" + error);
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900102 setSystemAudioMode(false);
Yuncheol Heod05f67f2014-07-11 16:06:40 +0900103 finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900104 }
105 }
106 });
Jungshik Janga7221ce2014-08-28 16:35:30 +0900107 mState = STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE;
108 addTimer(mState, mTargetAudioStatus ? ON_TIMEOUT_MS : OFF_TIMEOUT_MS);
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900109 }
110
111 private void handleSendSystemAudioModeRequestTimeout() {
112 if (!mTargetAudioStatus // Don't retry for Off case.
113 || mSendRetryCount++ >= MAX_SEND_RETRY_COUNT) {
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900114 HdmiLogger.debug("[T]:wait for <Set System Audio Mode>.");
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900115 setSystemAudioMode(false);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900116 finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900117 return;
118 }
119 sendSystemAudioModeRequest();
120 }
121
122 protected void setSystemAudioMode(boolean mode) {
Jinsuk Kim7ecfbae2014-07-11 14:16:29 +0900123 tv().setSystemAudioMode(mode, true);
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900124 }
125
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900126 @Override
127 final boolean processCommand(HdmiCecMessage cmd) {
128 switch (mState) {
129 case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE:
Yuncheol Heod05f67f2014-07-11 16:06:40 +0900130 if (cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT
Jungshik Jang339227d2014-08-25 15:37:20 +0900131 && (cmd.getParams()[0] & 0xFF)
132 == Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST) {
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900133 HdmiLogger.debug("Failed to start system audio mode request.");
Yuncheol Heod05f67f2014-07-11 16:06:40 +0900134 setSystemAudioMode(false);
135 finishWithCallback(HdmiControlManager.RESULT_EXCEPTION);
136 return true;
137 }
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900138 if (cmd.getOpcode() != Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900139 || !HdmiUtils.checkCommandSource(cmd, mAvrLogicalAddress, TAG)) {
140 return false;
141 }
142 boolean receivedStatus = HdmiUtils.parseCommandParamSystemAudioStatus(cmd);
143 if (receivedStatus == mTargetAudioStatus) {
144 setSystemAudioMode(receivedStatus);
Jungshik Jang16321b82014-06-19 19:39:27 +0900145 startAudioStatusAction();
146 return true;
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900147 } else {
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900148 HdmiLogger.debug("Unexpected system audio mode request:" + receivedStatus);
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900149 // Unexpected response, consider the request is newly initiated by AVR.
150 // To return 'false' will initiate new SystemAudioActionFromAvr by the control
151 // service.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900152 finishWithCallback(HdmiControlManager.RESULT_EXCEPTION);
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900153 return false;
154 }
Jungshik Jang16321b82014-06-19 19:39:27 +0900155 default:
156 return false;
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900157 }
Jungshik Jang16321b82014-06-19 19:39:27 +0900158 }
159
160 protected void startAudioStatusAction() {
Jungshik Jangea67c182014-06-19 22:19:20 +0900161 addAndStartAction(new SystemAudioStatusAction(tv(), mAvrLogicalAddress, mCallback));
Jungshik Jang16321b82014-06-19 19:39:27 +0900162 finish();
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900163 }
164
165 protected void removeSystemAudioActionInProgress() {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900166 removeActionExcept(SystemAudioActionFromTv.class, this);
167 removeActionExcept(SystemAudioActionFromAvr.class, this);
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900168 }
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;
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900179 }
180 }
Jungshik Jangea67c182014-06-19 22:19:20 +0900181
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 }
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900194}