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.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 22 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.car.builtin.util.Slogf; 26 import android.hardware.automotive.audiocontrol.MutingInfo; 27 import android.hardware.automotive.audiocontrol.V2_0.IAudioControl; 28 import android.hardware.automotive.audiocontrol.V2_0.ICloseHandle; 29 import android.hardware.automotive.audiocontrol.V2_0.IFocusListener; 30 import android.os.RemoteException; 31 import android.util.Log; 32 33 import com.android.car.CarLog; 34 import com.android.car.audio.CarDuckingInfo; 35 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 36 import com.android.car.internal.annotation.AttributeUsage; 37 import com.android.car.internal.util.IndentingPrintWriter; 38 39 import java.util.List; 40 import java.util.NoSuchElementException; 41 import java.util.Objects; 42 43 /** 44 * Wrapper for IAudioControl@2.0. 45 */ 46 public final class AudioControlWrapperV2 implements AudioControlWrapper { 47 private static final String TAG = CarLog.tagFor(AudioControlWrapperV2.class); 48 49 private IAudioControl mAudioControlV2; 50 51 private AudioControlDeathRecipient mDeathRecipient; 52 private ICloseHandle mCloseHandle; 53 getService()54 static @Nullable IAudioControl getService() { 55 try { 56 return IAudioControl.getService(true); 57 } catch (RemoteException e) { 58 throw new IllegalStateException("Failed to get IAudioControl@2.0 service", e); 59 } catch (NoSuchElementException e) { 60 return null; 61 } 62 } 63 AudioControlWrapperV2(IAudioControl audioControlV2)64 AudioControlWrapperV2(IAudioControl audioControlV2) { 65 mAudioControlV2 = Objects.requireNonNull(audioControlV2); 66 } 67 68 @Override unregisterFocusListener()69 public void unregisterFocusListener() { 70 if (mCloseHandle != null) { 71 try { 72 mCloseHandle.close(); 73 } catch (RemoteException e) { 74 Slogf.e(TAG, "Failed to close focus listener", e); 75 } finally { 76 mCloseHandle = null; 77 } 78 } 79 } 80 81 @Override supportsFeature(int feature)82 public boolean supportsFeature(int feature) { 83 if (feature == AUDIOCONTROL_FEATURE_AUDIO_FOCUS) { 84 return true; 85 } 86 return false; 87 } 88 89 @Override registerFocusListener(HalFocusListener focusListener)90 public void registerFocusListener(HalFocusListener focusListener) { 91 Slogf.d(TAG, "Registering focus listener on AudioControl HAL"); 92 IFocusListener listenerWrapper = new FocusListenerWrapper(focusListener); 93 try { 94 mCloseHandle = mAudioControlV2.registerFocusListener(listenerWrapper); 95 } catch (RemoteException e) { 96 Slogf.e(TAG, "Failed to register focus listener"); 97 throw new IllegalStateException("IAudioControl#registerFocusListener failed", e); 98 } 99 } 100 101 @Override registerAudioGainCallback(HalAudioGainCallback gainCallback)102 public void registerAudioGainCallback(HalAudioGainCallback gainCallback) { 103 throw new UnsupportedOperationException( 104 "Audio Gain Callback is unsupported for IAudioControl@2.0"); 105 } 106 107 @Override unregisterAudioGainCallback()108 public void unregisterAudioGainCallback() { 109 throw new UnsupportedOperationException( 110 "Audio Gain Callback is unsupported for IAudioControl@2.0"); 111 } 112 113 @Override onAudioFocusChange(@ttributeUsage int usage, int zoneId, int focusChange)114 public void onAudioFocusChange(@AttributeUsage int usage, int zoneId, int focusChange) { 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("HAL ducking is unsupported for IAudioControl@2.0"); 162 } 163 164 @Override onDevicesToMuteChange(@onNull List<MutingInfo> carZonesMutingInfo)165 public void onDevicesToMuteChange(@NonNull List<MutingInfo> carZonesMutingInfo) { 166 throw new UnsupportedOperationException("HAL muting is unsupported for IAudioControl@2.0"); 167 } 168 169 @Override linkToDeath(@ullable AudioControlDeathRecipient deathRecipient)170 public void linkToDeath(@Nullable AudioControlDeathRecipient deathRecipient) { 171 try { 172 mAudioControlV2.linkToDeath(this::serviceDied, 0); 173 mDeathRecipient = deathRecipient; 174 } catch (RemoteException e) { 175 throw new IllegalStateException("Call to IAudioControl@2.0#linkToDeath failed", e); 176 } 177 } 178 179 @Override unlinkToDeath()180 public void unlinkToDeath() { 181 try { 182 mAudioControlV2.unlinkToDeath(this::serviceDied); 183 mDeathRecipient = null; 184 } catch (RemoteException e) { 185 throw new IllegalStateException("Call to IAudioControl@2.0#unlinkToDeath failed", e); 186 } 187 } 188 serviceDied(long cookie)189 private void serviceDied(long cookie) { 190 Slogf.w(TAG, "IAudioControl@2.0 died. Fetching new handle"); 191 mAudioControlV2 = AudioControlWrapperV2.getService(); 192 linkToDeath(mDeathRecipient); 193 if (mDeathRecipient != null) { 194 mDeathRecipient.serviceDied(); 195 } 196 } 197 198 private final class FocusListenerWrapper extends IFocusListener.Stub { 199 private final HalFocusListener mListener; 200 FocusListenerWrapper(HalFocusListener halFocusListener)201 FocusListenerWrapper(HalFocusListener halFocusListener) { 202 mListener = halFocusListener; 203 } 204 205 @Override requestAudioFocus(int usage, int zoneId, int focusGain)206 public void requestAudioFocus(int usage, int zoneId, int focusGain) throws RemoteException { 207 mListener.requestAudioFocus(usage, zoneId, focusGain); 208 } 209 210 @Override abandonAudioFocus(int usage, int zoneId)211 public void abandonAudioFocus(int usage, int zoneId) throws RemoteException { 212 mListener.abandonAudioFocus(usage, zoneId); 213 } 214 } 215 } 216