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