1 /*
2  * Copyright (C) 2018 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 package android.hardware.hdmi;
17 
18 import android.annotation.NonNull;
19 import android.annotation.Nullable;
20 import android.os.Handler;
21 import android.os.Looper;
22 import android.os.RemoteException;
23 import android.util.Log;
24 
25 import com.android.internal.annotations.VisibleForTesting;
26 import com.android.internal.annotations.VisibleForTesting.Visibility;
27 
28 /**
29  * HdmiAudioSystemClient represents HDMI-CEC logical device of type Audio System in the Android
30  * system which acts as an audio system device such as sound bar.
31  *
32  * <p>HdmiAudioSystemClient provides methods that control, get information from TV/Display device
33  * connected through HDMI bus.
34  *
35  * @hide
36  */
37 public final class HdmiAudioSystemClient extends HdmiClient {
38     private static final String TAG = "HdmiAudioSystemClient";
39 
40     private static final int REPORT_AUDIO_STATUS_INTERVAL_MS = 500;
41 
42     private final Handler mHandler;
43     private boolean mCanSendAudioStatus = true;
44     private boolean mPendingReportAudioStatus;
45 
46     private int mLastVolume;
47     private int mLastMaxVolume;
48     private boolean mLastIsMute;
49 
50     @VisibleForTesting(visibility = Visibility.PACKAGE)
HdmiAudioSystemClient(IHdmiControlService service)51     public HdmiAudioSystemClient(IHdmiControlService service) {
52         this(service, null);
53     }
54 
55     @VisibleForTesting(visibility = Visibility.PACKAGE)
HdmiAudioSystemClient(IHdmiControlService service, @Nullable Handler handler)56     public HdmiAudioSystemClient(IHdmiControlService service, @Nullable Handler handler) {
57         super(service);
58         mHandler = handler == null ? new Handler(Looper.getMainLooper()) : handler;
59     }
60 
61     /**
62      * Callback interface used to get the set System Audio Mode result.
63      *
64      * @hide
65      */
66     // TODO(b/110094868): unhide and add @SystemApi for Q
67     public interface SetSystemAudioModeCallback {
68         /**
69          * Called when the input was changed.
70          *
71          * @param result the result of the set System Audio Mode
72          */
onComplete(int result)73         void onComplete(int result);
74     }
75 
76     /** @hide */
77     // TODO(b/110094868): unhide and add @SystemApi for Q
78     @Override
getDeviceType()79     public int getDeviceType() {
80         return HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
81     }
82 
83     /**
84      * Sends a Report Audio Status HDMI CEC command to TV devices when necessary.
85      *
86      * According to HDMI CEC specification, an audio system can report its audio status when System
87      * Audio Mode is on, so that the TV can display the audio status of external amplifier.
88      *
89      * @hide
90      */
sendReportAudioStatusCecCommand(boolean isMuteAdjust, int volume, int maxVolume, boolean isMute)91     public void sendReportAudioStatusCecCommand(boolean isMuteAdjust, int volume, int maxVolume,
92             boolean isMute) {
93         if (isMuteAdjust) {
94             // always report audio status when it's muted/unmuted
95             try {
96                 mService.reportAudioStatus(getDeviceType(), volume, maxVolume, isMute);
97             } catch (RemoteException e) {
98                 // do nothing. Reporting audio status is optional.
99             }
100             return;
101         }
102 
103         mLastVolume = volume;
104         mLastMaxVolume = maxVolume;
105         mLastIsMute = isMute;
106         if (mCanSendAudioStatus) {
107             try {
108                 mService.reportAudioStatus(getDeviceType(), volume, maxVolume, isMute);
109                 mCanSendAudioStatus = false;
110                 mHandler.postDelayed(new Runnable() {
111                     @Override
112                     public void run() {
113                         if (mPendingReportAudioStatus) {
114                             // report audio status if there is any pending message
115                             try {
116                                 mService.reportAudioStatus(getDeviceType(), mLastVolume,
117                                         mLastMaxVolume, mLastIsMute);
118                                 mHandler.postDelayed(this, REPORT_AUDIO_STATUS_INTERVAL_MS);
119                             }  catch (RemoteException e) {
120                                 mCanSendAudioStatus = true;
121                             } finally {
122                                 mPendingReportAudioStatus = false;
123                             }
124                         } else {
125                             mCanSendAudioStatus = true;
126                         }
127                     }
128                 }, REPORT_AUDIO_STATUS_INTERVAL_MS);
129             } catch (RemoteException e) {
130                 // do nothing. Reporting audio status is optional.
131             }
132         } else {
133             // if audio status cannot be sent, send it latter
134             mPendingReportAudioStatus = true;
135         }
136     }
137 
138     /**
139      * Set System Audio Mode on/off with audio system device.
140      *
141      * @param state true to set System Audio Mode on. False to set off.
142      * @param callback callback offer the setting result.
143      *
144      * @hide
145      */
146     // TODO(b/110094868): unhide and add @SystemApi for Q
setSystemAudioMode(boolean state, @NonNull SetSystemAudioModeCallback callback)147     public void setSystemAudioMode(boolean state, @NonNull SetSystemAudioModeCallback callback) {
148         // TODO(amyjojo): implement this when needed.
149     }
150 
151     /**
152      * When device is switching to an audio only source, this method is called to broadcast
153      * a setSystemAudioMode on message to the HDMI CEC system without querying Active Source or
154      * TV supporting System Audio Control or not. This is to get volume control passthrough
155      * from STB even if TV does not support it.
156      *
157      * @hide
158      */
159     // TODO(b/110094868): unhide and add @SystemApi for Q
setSystemAudioModeOnForAudioOnlySource()160     public void setSystemAudioModeOnForAudioOnlySource() {
161         try {
162             mService.setSystemAudioModeOnForAudioOnlySource();
163         } catch (RemoteException e) {
164             Log.d(TAG, "Failed to set System Audio Mode on for Audio Only source");
165         }
166     }
167 }
168