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     public interface SetSystemAudioModeCallback {
67         /**
68          * Called when the input was changed.
69          *
70          * @param result the result of the set System Audio Mode
71          */
onComplete(int result)72         void onComplete(int result);
73     }
74 
75     /** @hide */
76     @Override
getDeviceType()77     public int getDeviceType() {
78         return HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
79     }
80 
81     /**
82      * Sends a Report Audio Status HDMI CEC command to TV devices when necessary.
83      *
84      * According to HDMI CEC specification, an audio system can report its audio status when System
85      * Audio Mode is on, so that the TV can display the audio status of external amplifier.
86      *
87      * @hide
88      */
sendReportAudioStatusCecCommand(boolean isMuteAdjust, int volume, int maxVolume, boolean isMute)89     public void sendReportAudioStatusCecCommand(boolean isMuteAdjust, int volume, int maxVolume,
90             boolean isMute) {
91         if (isMuteAdjust) {
92             // always report audio status when it's muted/unmuted
93             try {
94                 mService.reportAudioStatus(getDeviceType(), volume, maxVolume, isMute);
95             } catch (RemoteException e) {
96                 // do nothing. Reporting audio status is optional.
97             }
98             return;
99         }
100 
101         mLastVolume = volume;
102         mLastMaxVolume = maxVolume;
103         mLastIsMute = isMute;
104         if (mCanSendAudioStatus) {
105             try {
106                 mService.reportAudioStatus(getDeviceType(), volume, maxVolume, isMute);
107                 mCanSendAudioStatus = false;
108                 mHandler.postDelayed(new Runnable() {
109                     @Override
110                     public void run() {
111                         if (mPendingReportAudioStatus) {
112                             // report audio status if there is any pending message
113                             try {
114                                 mService.reportAudioStatus(getDeviceType(), mLastVolume,
115                                         mLastMaxVolume, mLastIsMute);
116                                 mHandler.postDelayed(this, REPORT_AUDIO_STATUS_INTERVAL_MS);
117                             }  catch (RemoteException e) {
118                                 mCanSendAudioStatus = true;
119                             } finally {
120                                 mPendingReportAudioStatus = false;
121                             }
122                         } else {
123                             mCanSendAudioStatus = true;
124                         }
125                     }
126                 }, REPORT_AUDIO_STATUS_INTERVAL_MS);
127             } catch (RemoteException e) {
128                 // do nothing. Reporting audio status is optional.
129             }
130         } else {
131             // if audio status cannot be sent, send it latter
132             mPendingReportAudioStatus = true;
133         }
134     }
135 
136     /**
137      * Set System Audio Mode on/off with audio system device.
138      *
139      * @param state true to set System Audio Mode on. False to set off.
140      * @param callback callback offer the setting result.
141      *
142      * @hide
143      */
setSystemAudioMode(boolean state, @NonNull SetSystemAudioModeCallback callback)144     public void setSystemAudioMode(boolean state, @NonNull SetSystemAudioModeCallback callback) {
145         // TODO(b/217509829): implement this when needed.
146     }
147 
148     /**
149      * When device is switching to an audio only source, this method is called to broadcast
150      * a setSystemAudioMode on message to the HDMI CEC system without querying Active Source or
151      * TV supporting System Audio Control or not. This is to get volume control passthrough
152      * from STB even if TV does not support it.
153      *
154      * @hide
155      */
setSystemAudioModeOnForAudioOnlySource()156     public void setSystemAudioModeOnForAudioOnlySource() {
157         try {
158             mService.setSystemAudioModeOnForAudioOnlySource();
159         } catch (RemoteException e) {
160             Log.d(TAG, "Failed to set System Audio Mode on for Audio Only source");
161         }
162     }
163 }
164