1 /* 2 * Copyright (C) 2020 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 17 package com.android.car.audio.hal; 18 19 import static android.car.builtin.media.AudioManagerHelper.usageToString; 20 21 import static com.android.car.audio.CarHalAudioUtils.usageToMetadata; 22 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 23 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.car.builtin.util.Slogf; 27 import android.hardware.audio.common.PlaybackTrackMetadata; 28 import android.hardware.automotive.audiocontrol.AudioDeviceConfiguration; 29 import android.hardware.automotive.audiocontrol.AudioZone; 30 import android.hardware.automotive.audiocontrol.MutingInfo; 31 import android.hardware.automotive.audiocontrol.V2_0.IAudioControl; 32 import android.hardware.automotive.audiocontrol.V2_0.ICloseHandle; 33 import android.hardware.automotive.audiocontrol.V2_0.IFocusListener; 34 import android.media.audio.common.AudioPort; 35 import android.os.RemoteException; 36 import android.util.Log; 37 38 import com.android.car.CarLog; 39 import com.android.car.audio.CarDuckingInfo; 40 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 41 import com.android.car.internal.util.IndentingPrintWriter; 42 43 import java.util.List; 44 import java.util.NoSuchElementException; 45 import java.util.Objects; 46 47 /** 48 * Wrapper for IAudioControl@2.0. 49 */ 50 public final class AudioControlWrapperV2 implements AudioControlWrapper { 51 private static final String TAG = CarLog.tagFor(AudioControlWrapperV2.class); 52 53 private IAudioControl mAudioControlV2; 54 55 private AudioControlDeathRecipient mDeathRecipient; 56 private ICloseHandle mCloseHandle; 57 getService()58 static @Nullable IAudioControl getService() { 59 try { 60 return IAudioControl.getService(true); 61 } catch (RemoteException e) { 62 throw new IllegalStateException("Failed to get IAudioControl@2.0 service", e); 63 } catch (NoSuchElementException e) { 64 return null; 65 } 66 } 67 AudioControlWrapperV2(IAudioControl audioControlV2)68 AudioControlWrapperV2(IAudioControl audioControlV2) { 69 mAudioControlV2 = Objects.requireNonNull(audioControlV2); 70 } 71 72 @Override unregisterFocusListener()73 public void unregisterFocusListener() { 74 if (mCloseHandle != null) { 75 try { 76 mCloseHandle.close(); 77 } catch (RemoteException e) { 78 Slogf.e(TAG, "Failed to close focus listener", e); 79 } finally { 80 mCloseHandle = null; 81 } 82 } 83 } 84 85 @Override supportsFeature(int feature)86 public boolean supportsFeature(int feature) { 87 return feature == AUDIOCONTROL_FEATURE_AUDIO_FOCUS; 88 } 89 90 @Override registerFocusListener(HalFocusListener focusListener)91 public void registerFocusListener(HalFocusListener focusListener) { 92 Slogf.d(TAG, "Registering focus listener on AudioControl HAL"); 93 IFocusListener listenerWrapper = new FocusListenerWrapper(focusListener); 94 try { 95 mCloseHandle = mAudioControlV2.registerFocusListener(listenerWrapper); 96 } catch (RemoteException e) { 97 Slogf.e(TAG, "Failed to register focus listener"); 98 throw new IllegalStateException("IAudioControl#registerFocusListener failed", e); 99 } 100 } 101 102 @Override registerAudioGainCallback(HalAudioGainCallback gainCallback)103 public void registerAudioGainCallback(HalAudioGainCallback gainCallback) { 104 throw new UnsupportedOperationException(getUnsupportedMessage("Audio Gain Callback")); 105 } 106 107 @Override unregisterAudioGainCallback()108 public void unregisterAudioGainCallback() { 109 throw new UnsupportedOperationException(getUnsupportedMessage("Audio Gain Callback")); 110 } 111 112 @Override onAudioFocusChange(PlaybackTrackMetadata metaData, int zoneId, int focusChange)113 public void onAudioFocusChange(PlaybackTrackMetadata metaData, int zoneId, int focusChange) { 114 int usage = metaData.usage; 115 if (Slogf.isLoggable(TAG, Log.DEBUG)) { 116 Slogf.d(TAG, "onAudioFocusChange: usage " + usageToString(usage) 117 + ", zoneId " + zoneId + ", focusChange " + focusChange); 118 } 119 try { 120 mAudioControlV2.onAudioFocusChange(usage, zoneId, focusChange); 121 } catch (RemoteException e) { 122 throw new IllegalStateException("Failed to query IAudioControl#onAudioFocusChange", e); 123 } 124 } 125 126 @Override 127 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)128 public void dump(IndentingPrintWriter writer) { 129 writer.println("*AudioControlWrapperV2*"); 130 writer.increaseIndent(); 131 writer.printf("Focus listener registered on HAL? %b\n", (mCloseHandle != null)); 132 133 writer.println("Supported Features"); 134 writer.increaseIndent(); 135 writer.println("- AUDIOCONTROL_FEATURE_AUDIO_FOCUS"); 136 writer.decreaseIndent(); 137 138 writer.decreaseIndent(); 139 } 140 141 @Override setFadeTowardFront(float value)142 public void setFadeTowardFront(float value) { 143 try { 144 mAudioControlV2.setFadeTowardFront(value); 145 } catch (RemoteException e) { 146 Slogf.e(TAG, "setFadeTowardFront failed", e); 147 } 148 } 149 150 @Override setBalanceTowardRight(float value)151 public void setBalanceTowardRight(float value) { 152 try { 153 mAudioControlV2.setBalanceTowardRight(value); 154 } catch (RemoteException e) { 155 Slogf.e(TAG, "setBalanceTowardRight failed", e); 156 } 157 } 158 159 @Override onDevicesToDuckChange(List<CarDuckingInfo> carDuckingInfos)160 public void onDevicesToDuckChange(List<CarDuckingInfo> carDuckingInfos) { 161 throw new UnsupportedOperationException(getUnsupportedMessage("HAL ducking")); 162 } 163 164 @Override onDevicesToMuteChange(@onNull List<MutingInfo> carZonesMutingInfo)165 public void onDevicesToMuteChange(@NonNull List<MutingInfo> carZonesMutingInfo) { 166 throw new UnsupportedOperationException(getUnsupportedMessage("HAL muting")); 167 } 168 169 @Override setModuleChangeCallback(HalAudioModuleChangeCallback moduleChangeCallback)170 public void setModuleChangeCallback(HalAudioModuleChangeCallback moduleChangeCallback) { 171 throw new UnsupportedOperationException(getUnsupportedMessage("Module change callback")); 172 } 173 174 @Override clearModuleChangeCallback()175 public void clearModuleChangeCallback() { 176 throw new UnsupportedOperationException(getUnsupportedMessage("Module change callback")); 177 } 178 179 @Override getAudioDeviceConfiguration()180 public AudioDeviceConfiguration getAudioDeviceConfiguration() { 181 throw new UnsupportedOperationException(getUnsupportedMessage("Audio device configs")); 182 } 183 184 @Override getOutputMirroringDevices()185 public List<AudioPort> getOutputMirroringDevices() { 186 throw new UnsupportedOperationException(getUnsupportedMessage("Output mirror devices")); 187 } 188 189 @Override getCarAudioZones()190 public List<AudioZone> getCarAudioZones() { 191 throw new UnsupportedOperationException(getUnsupportedMessage("Audio zones")); 192 } 193 getUnsupportedMessage(String operation)194 private static String getUnsupportedMessage(String operation) { 195 return operation + " is unsupported for IAudioControl@2.0"; 196 } 197 198 @Override linkToDeath(@ullable AudioControlDeathRecipient deathRecipient)199 public void linkToDeath(@Nullable AudioControlDeathRecipient deathRecipient) { 200 try { 201 mAudioControlV2.linkToDeath(this::serviceDied, 0); 202 mDeathRecipient = deathRecipient; 203 } catch (RemoteException e) { 204 throw new IllegalStateException("Call to IAudioControl@2.0#linkToDeath failed", e); 205 } 206 } 207 208 @Override unlinkToDeath()209 public void unlinkToDeath() { 210 try { 211 mAudioControlV2.unlinkToDeath(this::serviceDied); 212 mDeathRecipient = null; 213 } catch (RemoteException e) { 214 throw new IllegalStateException("Call to IAudioControl@2.0#unlinkToDeath failed", e); 215 } 216 } 217 serviceDied(long cookie)218 private void serviceDied(long cookie) { 219 Slogf.w(TAG, "IAudioControl@2.0 died. Fetching new handle"); 220 mAudioControlV2 = AudioControlWrapperV2.getService(); 221 linkToDeath(mDeathRecipient); 222 if (mDeathRecipient != null) { 223 mDeathRecipient.serviceDied(); 224 } 225 } 226 227 private static final class FocusListenerWrapper extends IFocusListener.Stub { 228 private final HalFocusListener mListener; 229 FocusListenerWrapper(HalFocusListener halFocusListener)230 FocusListenerWrapper(HalFocusListener halFocusListener) { 231 mListener = halFocusListener; 232 } 233 234 @Override requestAudioFocus(int usage, int zoneId, int focusGain)235 public void requestAudioFocus(int usage, int zoneId, int focusGain) throws RemoteException { 236 mListener.requestAudioFocus(usageToMetadata(usage), zoneId, focusGain); 237 } 238 239 @Override abandonAudioFocus(int usage, int zoneId)240 public void abandonAudioFocus(int usage, int zoneId) throws RemoteException { 241 mListener.abandonAudioFocus(usageToMetadata(usage), zoneId); 242 } 243 } 244 } 245