blob: a209cd0cca73f0d0eabcedd08f036be2bd88933e [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 HdmiCecMessage command = HdmiCecMessageBuilder.buildSystemAudioModeRequest(
94 getSourceAddress(),
Jinsuk Kime6536022014-12-02 13:40:02 +090095 mAvrLogicalAddress, getSystemAudioModeRequestParam(), mTargetAudioStatus);
Yuncheol Heo63a2e062014-05-27 23:06:01 +090096 sendCommand(command, new HdmiControlService.SendMessageCallback() {
97 @Override
98 public void onSendCompleted(int error) {
Jungshik Janga7221ce2014-08-28 16:35:30 +090099 if (error != Constants.SEND_RESULT_SUCCESS) {
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900100 HdmiLogger.debug("Failed to send <System Audio Mode Request>:" + error);
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900101 setSystemAudioMode(false);
Yuncheol Heod05f67f2014-07-11 16:06:40 +0900102 finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900103 }
104 }
105 });
Jungshik Janga7221ce2014-08-28 16:35:30 +0900106 mState = STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE;
107 addTimer(mState, mTargetAudioStatus ? ON_TIMEOUT_MS : OFF_TIMEOUT_MS);
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900108 }
109
Jinsuk Kime6536022014-12-02 13:40:02 +0900110 private int getSystemAudioModeRequestParam() {
111 // <System Audio Mode Request> takes the physical address of the source device
112 // as a parameter. Get it from following candidates, in the order listed below:
113 // 1) physical address of the active source
114 // 2) active routing path
115 // 3) physical address of TV
116 if (tv().getActiveSource().isValid()) {
117 return tv().getActiveSource().physicalAddress;
118 }
119 int param = tv().getActivePath();
120 return param != Constants.INVALID_PHYSICAL_ADDRESS
121 ? param : Constants.PATH_INTERNAL;
122 }
123
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900124 private void handleSendSystemAudioModeRequestTimeout() {
125 if (!mTargetAudioStatus // Don't retry for Off case.
126 || mSendRetryCount++ >= MAX_SEND_RETRY_COUNT) {
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900127 HdmiLogger.debug("[T]:wait for <Set System Audio Mode>.");
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900128 setSystemAudioMode(false);
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900129 finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900130 return;
131 }
132 sendSystemAudioModeRequest();
133 }
134
135 protected void setSystemAudioMode(boolean mode) {
Jinsuk Kim7ecfbae2014-07-11 14:16:29 +0900136 tv().setSystemAudioMode(mode, true);
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900137 }
138
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900139 @Override
140 final boolean processCommand(HdmiCecMessage cmd) {
Jungshik Jang5352081c2014-09-22 15:14:49 +0900141 if (cmd.getSource() != mAvrLogicalAddress) {
142 return false;
143 }
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900144 switch (mState) {
145 case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE:
Yuncheol Heod05f67f2014-07-11 16:06:40 +0900146 if (cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT
Jungshik Jang339227d2014-08-25 15:37:20 +0900147 && (cmd.getParams()[0] & 0xFF)
148 == Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST) {
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900149 HdmiLogger.debug("Failed to start system audio mode request.");
Yuncheol Heod05f67f2014-07-11 16:06:40 +0900150 setSystemAudioMode(false);
151 finishWithCallback(HdmiControlManager.RESULT_EXCEPTION);
152 return true;
153 }
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900154 if (cmd.getOpcode() != Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900155 || !HdmiUtils.checkCommandSource(cmd, mAvrLogicalAddress, TAG)) {
156 return false;
157 }
158 boolean receivedStatus = HdmiUtils.parseCommandParamSystemAudioStatus(cmd);
159 if (receivedStatus == mTargetAudioStatus) {
160 setSystemAudioMode(receivedStatus);
Jungshik Jang16321b82014-06-19 19:39:27 +0900161 startAudioStatusAction();
162 return true;
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900163 } else {
Jungshik Jang2e8f1b62014-09-03 08:28:02 +0900164 HdmiLogger.debug("Unexpected system audio mode request:" + receivedStatus);
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900165 // Unexpected response, consider the request is newly initiated by AVR.
166 // To return 'false' will initiate new SystemAudioActionFromAvr by the control
167 // service.
Jinsuk Kimc0c20d02014-07-04 14:34:31 +0900168 finishWithCallback(HdmiControlManager.RESULT_EXCEPTION);
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900169 return false;
170 }
Jungshik Jang16321b82014-06-19 19:39:27 +0900171 default:
172 return false;
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900173 }
Jungshik Jang16321b82014-06-19 19:39:27 +0900174 }
175
176 protected void startAudioStatusAction() {
Jungshik Jangea67c182014-06-19 22:19:20 +0900177 addAndStartAction(new SystemAudioStatusAction(tv(), mAvrLogicalAddress, mCallback));
Jungshik Jang16321b82014-06-19 19:39:27 +0900178 finish();
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900179 }
180
181 protected void removeSystemAudioActionInProgress() {
Jungshik Jang79c58a42014-06-16 16:45:36 +0900182 removeActionExcept(SystemAudioActionFromTv.class, this);
183 removeActionExcept(SystemAudioActionFromAvr.class, this);
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900184 }
185
186 @Override
187 final void handleTimerEvent(int state) {
188 if (mState != state) {
189 return;
190 }
191 switch (mState) {
192 case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE:
193 handleSendSystemAudioModeRequestTimeout();
194 return;
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900195 }
196 }
Jungshik Jangea67c182014-06-19 22:19:20 +0900197
198 // TODO: if IHdmiControlCallback is general to other FeatureAction,
199 // move it into FeatureAction.
200 protected void finishWithCallback(int returnCode) {
201 if (mCallback != null) {
202 try {
203 mCallback.onComplete(returnCode);
204 } catch (RemoteException e) {
205 Slog.e(TAG, "Failed to invoke callback.", e);
206 }
207 }
208 finish();
209 }
Yuncheol Heo63a2e062014-05-27 23:06:01 +0900210}