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