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