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.MutingInfo; 24 import android.hardware.automotive.audiocontrol.V2_0.IAudioControl; 25 import android.hardware.automotive.audiocontrol.V2_0.ICloseHandle; 26 import android.hardware.automotive.audiocontrol.V2_0.IFocusListener; 27 import android.media.AudioAttributes; 28 import android.media.AudioAttributes.AttributeUsage; 29 import android.os.RemoteException; 30 import android.util.IndentingPrintWriter; 31 import android.util.Log; 32 import android.util.Slog; 33 34 import com.android.car.CarLog; 35 import com.android.car.audio.CarDuckingInfo; 36 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 37 38 import java.util.List; 39 import java.util.NoSuchElementException; 40 import java.util.Objects; 41 42 /** 43 * Wrapper for IAudioControl@2.0. 44 */ 45 public final class AudioControlWrapperV2 implements AudioControlWrapper { 46 private static final String TAG = CarLog.tagFor(AudioControlWrapperV2.class); 47 48 private IAudioControl mAudioControlV2; 49 50 private AudioControlDeathRecipient mDeathRecipient; 51 private ICloseHandle mCloseHandle; 52 getService()53 static @Nullable IAudioControl getService() { 54 try { 55 return IAudioControl.getService(true); 56 } catch (RemoteException e) { 57 throw new IllegalStateException("Failed to get IAudioControl@2.0 service", e); 58 } catch (NoSuchElementException e) { 59 return null; 60 } 61 } 62 AudioControlWrapperV2(IAudioControl audioControlV2)63 AudioControlWrapperV2(IAudioControl audioControlV2) { 64 mAudioControlV2 = Objects.requireNonNull(audioControlV2); 65 } 66 67 @Override unregisterFocusListener()68 public void unregisterFocusListener() { 69 if (mCloseHandle != null) { 70 try { 71 mCloseHandle.close(); 72 } catch (RemoteException e) { 73 Slog.e(TAG, "Failed to close focus listener", e); 74 } finally { 75 mCloseHandle = null; 76 } 77 } 78 } 79 80 @Override supportsFeature(int feature)81 public boolean supportsFeature(int feature) { 82 if (feature == AUDIOCONTROL_FEATURE_AUDIO_FOCUS) { 83 return true; 84 } 85 return false; 86 } 87 88 @Override registerFocusListener(HalFocusListener focusListener)89 public void registerFocusListener(HalFocusListener focusListener) { 90 Slog.d(TAG, "Registering focus listener on AudioControl HAL"); 91 IFocusListener listenerWrapper = new FocusListenerWrapper(focusListener); 92 try { 93 mCloseHandle = mAudioControlV2.registerFocusListener(listenerWrapper); 94 } catch (RemoteException e) { 95 Slog.e(TAG, "Failed to register focus listener"); 96 throw new IllegalStateException("IAudioControl#registerFocusListener failed", e); 97 } 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 mAudioControlV2.onAudioFocusChange(usage, zoneId, focusChange); 108 } catch (RemoteException e) { 109 throw new IllegalStateException("Failed to query IAudioControl#onAudioFocusChange", e); 110 } 111 } 112 113 @Override 114 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)115 public void dump(IndentingPrintWriter writer) { 116 writer.println("*AudioControlWrapperV2*"); 117 writer.increaseIndent(); 118 writer.printf("Focus listener registered on HAL? %b\n", (mCloseHandle != null)); 119 120 writer.println("Supported Features"); 121 writer.increaseIndent(); 122 writer.println("- AUDIOCONTROL_FEATURE_AUDIO_FOCUS"); 123 writer.decreaseIndent(); 124 125 writer.decreaseIndent(); 126 } 127 128 @Override setFadeTowardFront(float value)129 public void setFadeTowardFront(float value) { 130 try { 131 mAudioControlV2.setFadeTowardFront(value); 132 } catch (RemoteException e) { 133 Slog.e(TAG, "setFadeTowardFront failed", e); 134 } 135 } 136 137 @Override setBalanceTowardRight(float value)138 public void setBalanceTowardRight(float value) { 139 try { 140 mAudioControlV2.setBalanceTowardRight(value); 141 } catch (RemoteException e) { 142 Slog.e(TAG, "setBalanceTowardRight failed", e); 143 } 144 } 145 146 @Override onDevicesToDuckChange(List<CarDuckingInfo> carDuckingInfos)147 public void onDevicesToDuckChange(List<CarDuckingInfo> carDuckingInfos) { 148 throw new UnsupportedOperationException("HAL ducking is unsupported for IAudioControl@2.0"); 149 } 150 151 @Override onDevicesToMuteChange(@onNull List<MutingInfo> carZonesMutingInfo)152 public void onDevicesToMuteChange(@NonNull List<MutingInfo> carZonesMutingInfo) { 153 throw new UnsupportedOperationException("HAL muting is unsupported for IAudioControl@2.0"); 154 } 155 156 @Override linkToDeath(@ullable AudioControlDeathRecipient deathRecipient)157 public void linkToDeath(@Nullable AudioControlDeathRecipient deathRecipient) { 158 try { 159 mAudioControlV2.linkToDeath(this::serviceDied, 0); 160 mDeathRecipient = deathRecipient; 161 } catch (RemoteException e) { 162 throw new IllegalStateException("Call to IAudioControl@2.0#linkToDeath failed", e); 163 } 164 } 165 166 @Override unlinkToDeath()167 public void unlinkToDeath() { 168 try { 169 mAudioControlV2.unlinkToDeath(this::serviceDied); 170 mDeathRecipient = null; 171 } catch (RemoteException e) { 172 throw new IllegalStateException("Call to IAudioControl@2.0#unlinkToDeath failed", e); 173 } 174 } 175 serviceDied(long cookie)176 private void serviceDied(long cookie) { 177 Slog.w(TAG, "IAudioControl@2.0 died. Fetching new handle"); 178 mAudioControlV2 = AudioControlWrapperV2.getService(); 179 linkToDeath(mDeathRecipient); 180 if (mDeathRecipient != null) { 181 mDeathRecipient.serviceDied(); 182 } 183 } 184 185 private final class FocusListenerWrapper extends IFocusListener.Stub { 186 private final HalFocusListener mListener; 187 FocusListenerWrapper(HalFocusListener halFocusListener)188 FocusListenerWrapper(HalFocusListener halFocusListener) { 189 mListener = halFocusListener; 190 } 191 192 @Override requestAudioFocus(int usage, int zoneId, int focusGain)193 public void requestAudioFocus(int usage, int zoneId, int focusGain) throws RemoteException { 194 mListener.requestAudioFocus(usage, zoneId, focusGain); 195 } 196 197 @Override abandonAudioFocus(int usage, int zoneId)198 public void abandonAudioFocus(int usage, int zoneId) throws RemoteException { 199 mListener.abandonAudioFocus(usage, zoneId); 200 } 201 } 202 } 203