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
17package com.android.server.hdmi;
18
19import android.annotation.Nullable;
20import android.hardware.hdmi.HdmiControlManager;
21import android.hardware.hdmi.IHdmiControlCallback;
22import android.os.RemoteException;
23import android.util.Slog;
24
25import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
26
27/**
28 * Action to update audio status (volume or mute) of audio amplifier
29 */
30final class SystemAudioStatusAction extends HdmiCecFeatureAction {
31    private static final String TAG = "SystemAudioStatusAction";
32
33    // State that waits for <ReportAudioStatus>.
34    private static final int STATE_WAIT_FOR_REPORT_AUDIO_STATUS = 1;
35
36    private final int mAvrAddress;
37    @Nullable private final IHdmiControlCallback mCallback;
38
39    SystemAudioStatusAction(HdmiCecLocalDevice source, int avrAddress,
40            IHdmiControlCallback callback) {
41        super(source);
42        mAvrAddress = avrAddress;
43        mCallback = callback;
44    }
45
46    @Override
47    boolean start() {
48        mState = STATE_WAIT_FOR_REPORT_AUDIO_STATUS;
49        addTimer(mState, HdmiConfig.TIMEOUT_MS);
50        sendGiveAudioStatus();
51        return true;
52    }
53
54    private void sendGiveAudioStatus() {
55        sendCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(getSourceAddress(), mAvrAddress),
56                new SendMessageCallback() {
57            @Override
58            public void onSendCompleted(int error) {
59                if (error != Constants.SEND_RESULT_SUCCESS) {
60                    handleSendGiveAudioStatusFailure();
61                }
62            }
63        });
64    }
65
66    private void handleSendGiveAudioStatusFailure() {
67        // Inform to all application that the audio status (volumn, mute) of
68        // the audio amplifier is unknown.
69        tv().setAudioStatus(false, Constants.UNKNOWN_VOLUME);
70
71        int uiCommand = tv().isSystemAudioActivated()
72                ? HdmiCecKeycode.CEC_KEYCODE_RESTORE_VOLUME_FUNCTION  // SystemAudioMode: ON
73                : HdmiCecKeycode.CEC_KEYCODE_MUTE_FUNCTION;           // SystemAudioMode: OFF
74        sendUserControlPressedAndReleased(mAvrAddress, uiCommand);
75
76        // Still return SUCCESS to callback.
77        finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
78    }
79
80    @Override
81    boolean processCommand(HdmiCecMessage cmd) {
82        if (mState != STATE_WAIT_FOR_REPORT_AUDIO_STATUS || mAvrAddress != cmd.getSource()) {
83            return false;
84        }
85
86        switch (cmd.getOpcode()) {
87            case Constants.MESSAGE_REPORT_AUDIO_STATUS:
88                handleReportAudioStatus(cmd);
89                return true;
90        }
91
92        return false;
93    }
94
95    private void handleReportAudioStatus(HdmiCecMessage cmd) {
96        byte[] params = cmd.getParams();
97        boolean mute = (params[0] & 0x80) == 0x80;
98        int volume = params[0] & 0x7F;
99        tv().setAudioStatus(mute, volume);
100
101        if (!(tv().isSystemAudioActivated() ^ mute)) {
102            // Toggle AVR's mute status to match with the system audio status.
103            sendUserControlPressedAndReleased(mAvrAddress, HdmiCecKeycode.CEC_KEYCODE_MUTE);
104        }
105        finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
106    }
107
108    private void finishWithCallback(int returnCode) {
109        if (mCallback != null) {
110            try {
111                mCallback.onComplete(returnCode);
112            } catch (RemoteException e) {
113                Slog.e(TAG, "Failed to invoke callback.", e);
114            }
115        }
116        finish();
117    }
118
119    @Override
120    void handleTimerEvent(int state) {
121        if (mState != state) {
122            return;
123        }
124
125        handleSendGiveAudioStatusFailure();
126    }
127}
128