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 com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.hardware.automotive.audiocontrol.DuckingInfo; 24 import android.hardware.automotive.audiocontrol.IAudioControl; 25 import android.hardware.automotive.audiocontrol.IFocusListener; 26 import android.hardware.automotive.audiocontrol.MutingInfo; 27 import android.media.AudioAttributes; 28 import android.media.AudioAttributes.AttributeUsage; 29 import android.os.Binder; 30 import android.os.IBinder; 31 import android.os.RemoteException; 32 import android.os.ServiceManager; 33 import android.util.IndentingPrintWriter; 34 import android.util.Log; 35 import android.util.Slog; 36 37 import com.android.car.CarLog; 38 import com.android.car.audio.CarDuckingInfo; 39 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 40 import com.android.internal.util.Preconditions; 41 42 import java.util.List; 43 import java.util.Objects; 44 45 /** 46 * Wrapper for AIDL interface for AudioControl HAL 47 */ 48 public final class AudioControlWrapperAidl implements AudioControlWrapper { 49 private static final String TAG = CarLog.tagFor(AudioControlWrapperAidl.class); 50 private static final String AUDIO_CONTROL_SERVICE = 51 "android.hardware.automotive.audiocontrol.IAudioControl/default"; 52 private IBinder mBinder; 53 private IAudioControl mAudioControl; 54 private boolean mListenerRegistered = false; 55 56 private AudioControlDeathRecipient mDeathRecipient; 57 getService()58 static @Nullable IBinder getService() { 59 return Binder.allowBlocking(ServiceManager.waitForDeclaredService( 60 AUDIO_CONTROL_SERVICE)); 61 } 62 AudioControlWrapperAidl(IBinder binder)63 AudioControlWrapperAidl(IBinder binder) { 64 mBinder = Objects.requireNonNull(binder); 65 mAudioControl = IAudioControl.Stub.asInterface(binder); 66 } 67 68 @Override unregisterFocusListener()69 public void unregisterFocusListener() { 70 // Focus listener will be unregistered by HAL automatically 71 } 72 73 @Override supportsFeature(int feature)74 public boolean supportsFeature(int feature) { 75 switch (feature) { 76 case AUDIOCONTROL_FEATURE_AUDIO_FOCUS: 77 case AUDIOCONTROL_FEATURE_AUDIO_DUCKING: 78 case AUDIOCONTROL_FEATURE_AUDIO_GROUP_MUTING: 79 return true; 80 default: 81 return false; 82 } 83 } 84 85 @Override registerFocusListener(HalFocusListener focusListener)86 public void registerFocusListener(HalFocusListener focusListener) { 87 if (Log.isLoggable(TAG, Log.DEBUG)) { 88 Slog.d(TAG, "Registering focus listener on AudioControl HAL"); 89 } 90 IFocusListener listenerWrapper = new FocusListenerWrapper(focusListener); 91 try { 92 mAudioControl.registerFocusListener(listenerWrapper); 93 } catch (RemoteException e) { 94 Slog.e(TAG, "Failed to register focus listener"); 95 throw new IllegalStateException("IAudioControl#registerFocusListener failed", e); 96 } 97 mListenerRegistered = true; 98 } 99 100 @Override onAudioFocusChange(@ttributeUsage int usage, int zoneId, int focusChange)101 public void onAudioFocusChange(@AttributeUsage int usage, int zoneId, int focusChange) { 102 if (Log.isLoggable(TAG, Log.DEBUG)) { 103 Slog.d(TAG, "onAudioFocusChange: usage " + AudioAttributes.usageToString(usage) 104 + ", zoneId " + zoneId + ", focusChange " + focusChange); 105 } 106 try { 107 String usageName = AudioAttributes.usageToXsdString(usage); 108 mAudioControl.onAudioFocusChange(usageName, zoneId, focusChange); 109 } catch (RemoteException e) { 110 throw new IllegalStateException("Failed to query IAudioControl#onAudioFocusChange", e); 111 } 112 } 113 114 @Override 115 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)116 public void dump(IndentingPrintWriter writer) { 117 writer.println("*AudioControlWrapperAidl*"); 118 writer.increaseIndent(); 119 writer.printf("Focus listener registered on HAL? %b\n", mListenerRegistered); 120 121 writer.println("Supported Features"); 122 writer.increaseIndent(); 123 writer.println("- AUDIOCONTROL_FEATURE_AUDIO_FOCUS"); 124 writer.println("- AUDIOCONTROL_FEATURE_AUDIO_DUCKING"); 125 writer.decreaseIndent(); 126 127 writer.decreaseIndent(); 128 } 129 130 @Override setFadeTowardFront(float value)131 public void setFadeTowardFront(float value) { 132 try { 133 mAudioControl.setFadeTowardFront(value); 134 } catch (RemoteException e) { 135 Slog.e(TAG, "setFadeTowardFront with " + value + " failed", e); 136 } 137 } 138 139 @Override setBalanceTowardRight(float value)140 public void setBalanceTowardRight(float value) { 141 try { 142 mAudioControl.setBalanceTowardRight(value); 143 } catch (RemoteException e) { 144 Slog.e(TAG, "setBalanceTowardRight with " + value + " failed", e); 145 } 146 } 147 148 @Override onDevicesToDuckChange(@onNull List<CarDuckingInfo> carDuckingInfos)149 public void onDevicesToDuckChange(@NonNull List<CarDuckingInfo> carDuckingInfos) { 150 Objects.requireNonNull(carDuckingInfos); 151 DuckingInfo[] duckingInfos = new DuckingInfo[carDuckingInfos.size()]; 152 for (int i = 0; i < carDuckingInfos.size(); i++) { 153 CarDuckingInfo info = Objects.requireNonNull(carDuckingInfos.get(i)); 154 duckingInfos[i] = info.generateDuckingInfo(); 155 } 156 157 try { 158 mAudioControl.onDevicesToDuckChange(duckingInfos); 159 } catch (RemoteException e) { 160 Slog.e(TAG, "onDevicesToDuckChange failed", e); 161 } 162 } 163 164 @Override onDevicesToMuteChange(@onNull List<MutingInfo> carZonesMutingInfo)165 public void onDevicesToMuteChange(@NonNull List<MutingInfo> carZonesMutingInfo) { 166 Objects.requireNonNull(carZonesMutingInfo, "Muting info can not be null"); 167 Preconditions.checkArgument(!carZonesMutingInfo.isEmpty(), "Muting info can not be empty"); 168 MutingInfo[] mutingInfoToHal = carZonesMutingInfo 169 .toArray(new MutingInfo[carZonesMutingInfo.size()]); 170 try { 171 mAudioControl.onDevicesToMuteChange(mutingInfoToHal); 172 } catch (RemoteException e) { 173 Slog.e(TAG, "onDevicesToMuteChange failed", e); 174 } 175 } 176 177 @Override linkToDeath(@ullable AudioControlDeathRecipient deathRecipient)178 public void linkToDeath(@Nullable AudioControlDeathRecipient deathRecipient) { 179 try { 180 mBinder.linkToDeath(this::binderDied, 0); 181 mDeathRecipient = deathRecipient; 182 } catch (RemoteException e) { 183 throw new IllegalStateException("Call to IAudioControl#linkToDeath failed", e); 184 } 185 } 186 187 @Override unlinkToDeath()188 public void unlinkToDeath() { 189 mBinder.unlinkToDeath(this::binderDied, 0); 190 mDeathRecipient = null; 191 } 192 binderDied()193 private void binderDied() { 194 Slog.w(TAG, "AudioControl HAL died. Fetching new handle"); 195 mListenerRegistered = false; 196 mBinder = AudioControlWrapperAidl.getService(); 197 mAudioControl = IAudioControl.Stub.asInterface(mBinder); 198 linkToDeath(mDeathRecipient); 199 if (mDeathRecipient != null) { 200 mDeathRecipient.serviceDied(); 201 } 202 } 203 204 private static final class FocusListenerWrapper extends IFocusListener.Stub { 205 private final HalFocusListener mListener; 206 FocusListenerWrapper(HalFocusListener halFocusListener)207 FocusListenerWrapper(HalFocusListener halFocusListener) { 208 mListener = halFocusListener; 209 } 210 211 @Override getInterfaceVersion()212 public int getInterfaceVersion() { 213 return this.VERSION; 214 } 215 216 @Override getInterfaceHash()217 public String getInterfaceHash() { 218 return this.HASH; 219 } 220 221 @Override requestAudioFocus(String usage, int zoneId, int focusGain)222 public void requestAudioFocus(String usage, int zoneId, int focusGain) { 223 @AttributeUsage int usageValue = AudioAttributes.xsdStringToUsage(usage); 224 mListener.requestAudioFocus(usageValue, zoneId, focusGain); 225 } 226 227 @Override abandonAudioFocus(String usage, int zoneId)228 public void abandonAudioFocus(String usage, int zoneId) { 229 @AttributeUsage int usageValue = AudioAttributes.xsdStringToUsage(usage); 230 mListener.abandonAudioFocus(usageValue, zoneId); 231 } 232 } 233 } 234