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        sendUserControlPressedAndReleased(mAvrAddress,
72                HdmiCecKeycode.getMuteKey(!tv().isSystemAudioActivated()));
73
74        // Still return SUCCESS to callback.
75        finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
76    }
77
78    @Override
79    boolean processCommand(HdmiCecMessage cmd) {
80        if (mState != STATE_WAIT_FOR_REPORT_AUDIO_STATUS || mAvrAddress != cmd.getSource()) {
81            return false;
82        }
83
84        switch (cmd.getOpcode()) {
85            case Constants.MESSAGE_REPORT_AUDIO_STATUS:
86                handleReportAudioStatus(cmd);
87                return true;
88        }
89
90        return false;
91    }
92
93    private void handleReportAudioStatus(HdmiCecMessage cmd) {
94        byte[] params = cmd.getParams();
95        boolean mute = (params[0] & 0x80) == 0x80;
96        int volume = params[0] & 0x7F;
97        tv().setAudioStatus(mute, volume);
98
99        if (!(tv().isSystemAudioActivated() ^ mute)) {
100            // Toggle AVR's mute status to match with the system audio status.
101            sendUserControlPressedAndReleased(mAvrAddress, HdmiCecKeycode.CEC_KEYCODE_MUTE);
102        }
103        finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
104    }
105
106    private void finishWithCallback(int returnCode) {
107        if (mCallback != null) {
108            try {
109                mCallback.onComplete(returnCode);
110            } catch (RemoteException e) {
111                Slog.e(TAG, "Failed to invoke callback.", e);
112            }
113        }
114        finish();
115    }
116
117    @Override
118    void handleTimerEvent(int state) {
119        if (mState != state) {
120            return;
121        }
122
123        handleSendGiveAudioStatusFailure();
124    }
125}
126