1 /* 2 * Copyright (C) 2015 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 com.android.car.audio; 17 18 import static android.car.builtin.media.AudioManagerHelper.UNDEFINED_STREAM_TYPE; 19 import static android.car.feature.Flags.asyncAudioServiceInit; 20 import static android.car.feature.Flags.carAudioFadeManagerConfiguration; 21 import static android.car.media.CarAudioManager.AUDIO_FEATURE_AUDIO_MIRRORING; 22 import static android.car.media.CarAudioManager.AUDIO_FEATURE_DYNAMIC_ROUTING; 23 import static android.car.media.CarAudioManager.AUDIO_FEATURE_MIN_MAX_ACTIVATION_VOLUME; 24 import static android.car.media.CarAudioManager.AUDIO_FEATURE_OEM_AUDIO_SERVICE; 25 import static android.car.media.CarAudioManager.AUDIO_FEATURE_PERSIST_FADE_BALANCE_VALUES; 26 import static android.car.media.CarAudioManager.AUDIO_FEATURE_VOLUME_GROUP_EVENTS; 27 import static android.car.media.CarAudioManager.AUDIO_FEATURE_VOLUME_GROUP_MUTING; 28 import static android.car.media.CarAudioManager.CONFIG_STATUS_CHANGED; 29 import static android.car.media.CarAudioManager.CarAudioFeature; 30 import static android.car.media.CarAudioManager.INVALID_REQUEST_ID; 31 import static android.car.media.CarAudioManager.INVALID_VOLUME_GROUP_ID; 32 import static android.car.media.CarAudioManager.PRIMARY_AUDIO_ZONE; 33 import static android.car.media.CarVolumeGroupEvent.EVENT_TYPE_MUTE_CHANGED; 34 import static android.car.media.CarVolumeGroupEvent.EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED; 35 import static android.car.media.CarVolumeGroupEvent.EXTRA_INFO_ATTENUATION_ACTIVATION; 36 import static android.car.media.CarVolumeGroupEvent.EXTRA_INFO_SHOW_UI; 37 import static android.media.AudioAttributes.USAGE_MEDIA; 38 import static android.media.AudioManager.ADJUST_LOWER; 39 import static android.media.AudioManager.ADJUST_RAISE; 40 import static android.media.AudioManager.ADJUST_SAME; 41 import static android.media.AudioManager.ADJUST_TOGGLE_MUTE; 42 import static android.media.AudioManager.FLAG_FROM_KEY; 43 import static android.media.AudioManager.FLAG_PLAY_SOUND; 44 import static android.media.AudioManager.FLAG_SHOW_UI; 45 import static android.media.audiopolicy.Flags.enableFadeManagerConfiguration; 46 import static android.view.KeyEvent.ACTION_DOWN; 47 import static android.view.KeyEvent.KEYCODE_VOLUME_DOWN; 48 import static android.view.KeyEvent.KEYCODE_VOLUME_MUTE; 49 import static android.view.KeyEvent.KEYCODE_VOLUME_UP; 50 51 import static com.android.car.audio.CarAudioUtils.convertVolumeChangeToEvent; 52 import static com.android.car.audio.CarAudioUtils.convertVolumeChangesToEvents; 53 import static com.android.car.audio.CarAudioUtils.excludesDynamicDevices; 54 import static com.android.car.audio.CarAudioUtils.generateCarAudioDeviceInfos; 55 import static com.android.car.audio.CarAudioUtils.getDynamicDevicesInConfig; 56 import static com.android.car.audio.hal.AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_CONFIGURATION; 57 import static com.android.car.audio.hal.AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_DUCKING; 58 import static com.android.car.audio.hal.AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_FOCUS; 59 import static com.android.car.audio.hal.AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_GAIN_CALLBACK; 60 import static com.android.car.audio.hal.AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_MODULE_CALLBACK; 61 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DEBUGGING_CODE; 62 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DEPRECATED_CODE; 63 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 64 import static com.android.car.internal.common.CommonConstants.EMPTY_INT_ARRAY; 65 66 import static java.util.Collections.EMPTY_LIST; 67 68 import android.annotation.NonNull; 69 import android.annotation.Nullable; 70 import android.annotation.UserIdInt; 71 import android.car.Car; 72 import android.car.CarOccupantZoneManager; 73 import android.car.ICarOccupantZoneCallback; 74 import android.car.builtin.media.AudioManagerHelper; 75 import android.car.builtin.media.AudioManagerHelper.AudioPatchInfo; 76 import android.car.builtin.media.AudioManagerHelper.VolumeAndMuteReceiver; 77 import android.car.builtin.os.TraceHelper; 78 import android.car.builtin.os.UserManagerHelper; 79 import android.car.builtin.util.Slogf; 80 import android.car.builtin.util.TimingsTraceLog; 81 import android.car.feature.Flags; 82 import android.car.media.AudioZonesMirrorStatusCallback; 83 import android.car.media.CarAudioManager; 84 import android.car.media.CarAudioPatchHandle; 85 import android.car.media.CarAudioZoneConfigInfo; 86 import android.car.media.CarVolumeGroupEvent; 87 import android.car.media.CarVolumeGroupInfo; 88 import android.car.media.IAudioZoneConfigurationsChangeCallback; 89 import android.car.media.IAudioZonesMirrorStatusCallback; 90 import android.car.media.ICarAudio; 91 import android.car.media.ICarVolumeCallback; 92 import android.car.media.ICarVolumeEventCallback; 93 import android.car.media.IMediaAudioRequestStatusCallback; 94 import android.car.media.IPrimaryZoneMediaAudioRequestCallback; 95 import android.car.media.ISwitchAudioZoneConfigCallback; 96 import android.car.oem.CarAudioFadeConfiguration; 97 import android.car.oem.CarAudioFeaturesInfo; 98 import android.content.Context; 99 import android.content.pm.PackageManager; 100 import android.media.AudioAttributes; 101 import android.media.AudioDeviceAttributes; 102 import android.media.AudioDeviceInfo; 103 import android.media.AudioFocusInfo; 104 import android.media.AudioManager; 105 import android.media.AudioManager.AudioServerStateCallback; 106 import android.media.FadeManagerConfiguration; 107 import android.media.audiopolicy.AudioPolicy; 108 import android.os.Binder; 109 import android.os.Handler; 110 import android.os.HandlerThread; 111 import android.os.IBinder; 112 import android.os.RemoteCallbackList; 113 import android.os.RemoteException; 114 import android.os.SystemClock; 115 import android.os.SystemProperties; 116 import android.os.UserHandle; 117 import android.telephony.SubscriptionManager; 118 import android.telephony.TelephonyManager; 119 import android.text.TextUtils; 120 import android.util.ArraySet; 121 import android.util.SparseArray; 122 import android.util.SparseIntArray; 123 import android.util.proto.ProtoOutputStream; 124 import android.view.KeyEvent; 125 126 import com.android.car.CarInputService; 127 import com.android.car.CarInputService.KeyEventListener; 128 import com.android.car.CarLocalServices; 129 import com.android.car.CarLog; 130 import com.android.car.CarOccupantZoneService; 131 import com.android.car.CarServiceBase; 132 import com.android.car.CarServiceUtils; 133 import com.android.car.R; 134 import com.android.car.audio.CarAudioContext.AudioContext; 135 import com.android.car.audio.CarAudioDumpProto.AudioZoneToOccupantZone; 136 import com.android.car.audio.CarAudioDumpProto.CarAudioConfiguration; 137 import com.android.car.audio.CarAudioDumpProto.CarAudioState; 138 import com.android.car.audio.CarAudioDumpProto.UidToAudioZone; 139 import com.android.car.audio.CarAudioDumpProto.UserIdToAudioZone; 140 import com.android.car.audio.CarAudioPolicyVolumeCallback.AudioPolicyVolumeCallbackInternal; 141 import com.android.car.audio.hal.AudioControlFactory; 142 import com.android.car.audio.hal.AudioControlWrapper; 143 import com.android.car.audio.hal.AudioControlWrapperV1; 144 import com.android.car.audio.hal.HalAudioDeviceInfo; 145 import com.android.car.audio.hal.HalAudioFocus; 146 import com.android.car.audio.hal.HalAudioGainCallback; 147 import com.android.car.audio.hal.HalAudioModuleChangeCallback; 148 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 149 import com.android.car.internal.annotation.AttributeUsage; 150 import com.android.car.internal.os.HandlerExecutor; 151 import com.android.car.internal.util.ArrayUtils; 152 import com.android.car.internal.util.IndentingPrintWriter; 153 import com.android.car.internal.util.LocalLog; 154 import com.android.car.oem.CarOemProxyService; 155 import com.android.internal.annotations.GuardedBy; 156 import com.android.internal.annotations.VisibleForTesting; 157 import com.android.internal.util.Preconditions; 158 159 import org.xmlpull.v1.XmlPullParserException; 160 161 import java.io.BufferedInputStream; 162 import java.io.File; 163 import java.io.FileInputStream; 164 import java.io.IOException; 165 import java.io.InputStream; 166 import java.util.ArrayList; 167 import java.util.Arrays; 168 import java.util.Collections; 169 import java.util.HashMap; 170 import java.util.HashSet; 171 import java.util.List; 172 import java.util.Map; 173 import java.util.Objects; 174 import java.util.Set; 175 import java.util.concurrent.CountDownLatch; 176 import java.util.concurrent.Executor; 177 import java.util.concurrent.TimeUnit; 178 import java.util.stream.Collectors; 179 180 /** 181 * Service responsible for interaction with car's audio system. 182 */ 183 public final class CarAudioService extends ICarAudio.Stub implements CarServiceBase { 184 185 static final String TAG = CarLog.TAG_AUDIO; 186 private static final String MIRROR_COMMAND_SEPARATOR = ";"; 187 private static final String MIRROR_COMMAND_DESTINATION_SEPARATOR = ","; 188 private static final String MIRROR_COMMAND_SOURCE = "mirroring_src="; 189 private static final String MIRROR_COMMAND_DESTINATION = "mirroring_dst="; 190 private static final String DISABLE_AUDIO_MIRRORING = "mirroring=off"; 191 192 private static final int RELEASE_TIMEOUT_MS = 10_000; 193 194 static final AudioAttributes CAR_DEFAULT_AUDIO_ATTRIBUTE = 195 CarAudioContext.getAudioAttributeFromUsage(USAGE_MEDIA); 196 197 private static final String PROPERTY_RO_ENABLE_AUDIO_PATCH = 198 "ro.android.car.audio.enableaudiopatch"; 199 200 // CarAudioService reads configuration from the following paths respectively. 201 // If the first one is found, all others are ignored. 202 // If no one is found, it fallbacks to car_volume_groups.xml resource file. 203 private static final String[] AUDIO_CONFIGURATION_PATHS = new String[] { 204 "/vendor/etc/car_audio_configuration.xml", 205 "/system/etc/car_audio_configuration.xml" 206 }; 207 208 private static final String FADE_CONFIGURATION_PATH = 209 "/vendor/etc/car_audio_fade_configuration.xml"; 210 211 private static final List<Integer> KEYCODES_OF_INTEREST = List.of( 212 KEYCODE_VOLUME_DOWN, 213 KEYCODE_VOLUME_UP, 214 KEYCODE_VOLUME_MUTE 215 ); 216 private static final AudioAttributes MEDIA_AUDIO_ATTRIBUTE = 217 CarAudioContext.getAudioAttributeFromUsage(USAGE_MEDIA); 218 private static final int EVENT_LOGGER_QUEUE_SIZE = 50; 219 220 private final HandlerThread mHandlerThread = CarServiceUtils.getHandlerThread( 221 CarAudioService.class.getSimpleName()); 222 private final Handler mHandler = new Handler(mHandlerThread.getLooper()); 223 224 private final Object mImplLock = new Object(); 225 226 private final Context mContext; 227 private final TelephonyManager mTelephonyManager; 228 private final AudioManagerWrapper mAudioManagerWrapper; 229 private final boolean mUseDynamicRouting; 230 private final boolean mUseCarVolumeGroupEvents; 231 private final boolean mUseMinMaxActivationVolume; 232 private final boolean mUseIsolatedFocusForDynamicDevices; 233 private final boolean mUseKeyEventsForDynamicDevices; 234 private final @CarVolume.CarVolumeListVersion int mAudioVolumeAdjustmentContextsVersion; 235 private final boolean mPersistMasterMuteState; 236 private final boolean mUseFadeManagerConfiguration; 237 private final boolean mPersistFadeBalanceLevels; 238 private final CarAudioSettings mCarAudioSettings; 239 private final int mKeyEventTimeoutMs; 240 private final MediaRequestHandler mMediaRequestHandler = new MediaRequestHandler(); 241 private final CarAudioMirrorRequestHandler mCarAudioMirrorRequestHandler = 242 new CarAudioMirrorRequestHandler(); 243 private final CarVolumeEventHandler mCarVolumeEventHandler = new CarVolumeEventHandler(); 244 private final AudioServerStateCallback mAudioServerStateCallback; 245 246 private final LocalLog mServiceEventLogger = new LocalLog(EVENT_LOGGER_QUEUE_SIZE); 247 248 @GuardedBy("mImplLock") 249 private boolean mInitCompleted; 250 @GuardedBy("mImplLock") 251 private boolean mInitSuccess; 252 @GuardedBy("mImplLock") 253 private @Nullable AudioControlWrapper mAudioControlWrapper; 254 private CarDucking mCarDucking; 255 private CarVolumeGroupMuting mCarVolumeGroupMuting; 256 @GuardedBy("mImplLock") 257 private @Nullable HalAudioFocus mHalAudioFocus; 258 259 private @Nullable CarAudioGainMonitor mCarAudioGainMonitor; 260 @GuardedBy("mImplLock") 261 private @Nullable CoreAudioVolumeGroupCallback mCoreAudioVolumeGroupCallback; 262 @GuardedBy("mImplLock") 263 private CarAudioDeviceCallback mAudioDeviceInfoCallback; 264 265 @GuardedBy("mImplLock") 266 private CarAudioModuleChangeMonitor mCarAudioModuleChangeMonitor; 267 @GuardedBy("mImplLock") 268 private @Nullable CarAudioPlaybackMonitor mCarAudioPlaybackMonitor; 269 @GuardedBy("mImplLock") 270 private boolean mIsAudioServerDown; 271 @GuardedBy("mImplLock") 272 private boolean mUseCoreAudioVolume; 273 @GuardedBy("mImplLock") 274 private boolean mUseCoreAudioRouting; 275 @GuardedBy("mImplLock") 276 private boolean mUseCarVolumeGroupMuting; 277 @GuardedBy("mImplLock") 278 private boolean mUseHalDuckingSignals; 279 @GuardedBy("mImplLock") 280 private boolean mCarAudioControlHalConfig; 281 282 283 /** 284 * Simulates {@link ICarVolumeCallback} when it's running in legacy mode. 285 * This receiver assumes the intent is sent to {@link CarAudioManager#PRIMARY_AUDIO_ZONE}. 286 */ 287 private final VolumeAndMuteReceiver mLegacyVolumeChangedHelper = 288 new AudioManagerHelper.VolumeAndMuteReceiver() { 289 @Override 290 public void onVolumeChanged(int streamType) { 291 if (streamType == UNDEFINED_STREAM_TYPE) { 292 Slogf.w(TAG, "Invalid stream type: %d", streamType); 293 } 294 int groupId = getVolumeGroupIdForStreamType(streamType); 295 if (groupId == INVALID_VOLUME_GROUP_ID) { 296 Slogf.w(TAG, "Unknown stream type: %d", streamType); 297 } else { 298 callbackGroupVolumeChange(PRIMARY_AUDIO_ZONE, groupId, 299 FLAG_FROM_KEY | FLAG_SHOW_UI); 300 } 301 } 302 303 @Override 304 public void onMuteChanged() { 305 callbackMasterMuteChange(PRIMARY_AUDIO_ZONE, FLAG_FROM_KEY | FLAG_SHOW_UI); 306 } 307 }; 308 309 private final KeyEventListener mCarKeyEventListener = new KeyEventListener() { 310 @Override 311 public void onKeyEvent(KeyEvent event, int displayType, int seat) { 312 Slogf.i(TAG, "On key event for audio with display type: %d and seat %d", displayType, 313 seat); 314 if (event.getAction() != ACTION_DOWN) { 315 return; 316 } 317 CarOccupantZoneService carOccupantZoneService = getCarOccupantZoneService(); 318 int audioZoneId = carOccupantZoneService.getAudioZoneIdForOccupant( 319 carOccupantZoneService.getOccupantZoneIdForSeat(seat)); 320 if (!isAudioZoneIdValid(audioZoneId)) { 321 Slogf.e(TAG, "Audio zone is invalid for event %s, displayType %d, and seat %d", 322 event, displayType, seat); 323 return; 324 } 325 int adjustment; 326 switch (event.getKeyCode()) { 327 case KEYCODE_VOLUME_DOWN: 328 adjustment = ADJUST_LOWER; 329 break; 330 case KEYCODE_VOLUME_UP: 331 adjustment = ADJUST_RAISE; 332 break; 333 case KEYCODE_VOLUME_MUTE: 334 adjustment = ADJUST_TOGGLE_MUTE; 335 break; 336 default: 337 adjustment = ADJUST_SAME; 338 break; 339 } 340 synchronized (mImplLock) { 341 if (mCarAudioPolicyVolumeCallback == null) { 342 return; 343 } 344 mCarAudioPolicyVolumeCallback.onVolumeAdjustment(adjustment, audioZoneId); 345 } 346 } 347 }; 348 349 @GuardedBy("mImplLock") 350 @Nullable private AudioPolicy mVolumeControlAudioPolicy; 351 @GuardedBy("mImplLock") 352 @Nullable private AudioPolicy mFocusControlAudioPolicy; 353 @GuardedBy("mImplLock") 354 @Nullable private AudioPolicy mRoutingAudioPolicy; 355 @GuardedBy("mImplLock") 356 @Nullable private AudioPolicy mFadeManagerConfigAudioPolicy; 357 private CarZonesAudioFocus mFocusHandler; 358 private String mCarAudioConfigurationPath; 359 private String mCarAudioFadeConfigurationPath; 360 private CarAudioFadeConfigurationHelper mCarAudioFadeConfigurationHelper; 361 private SparseIntArray mAudioZoneIdToOccupantZoneIdMapping; 362 @GuardedBy("mImplLock") 363 private SparseArray<CarAudioZone> mCarAudioZones; 364 @GuardedBy("mImplLock") 365 private CarVolume mCarVolume; 366 @GuardedBy("mImplLock") 367 private CarAudioContext mCarAudioContext; 368 private final CarVolumeCallbackHandler mCarVolumeCallbackHandler; 369 private final SparseIntArray mAudioZoneIdToUserIdMapping; 370 private final SystemClockWrapper mClock = new SystemClockWrapper(); 371 372 @GuardedBy("mImplLock") 373 private final SparseArray<DeathRecipient> 374 mUserAssignedToPrimaryZoneToCallbackDeathRecipient = new SparseArray<>(); 375 376 private final RemoteCallbackList<IAudioZoneConfigurationsChangeCallback> mConfigsCallbacks = 377 new RemoteCallbackList<>(); 378 379 // TODO do not store uid mapping here instead use the uid 380 // device affinity in audio policy when available 381 private Map<Integer, Integer> mUidToZoneMap; 382 private CarAudioPlaybackCallback mCarAudioPlaybackCallback; 383 private CarAudioPowerListener mCarAudioPowerListener; 384 private CarInputService mCarInputService; 385 386 private final HalAudioGainCallback mHalAudioGainCallback = 387 new HalAudioGainCallback() { 388 @Override 389 public void onAudioDeviceGainsChanged( 390 List<Integer> halReasons, List<CarAudioGainConfigInfo> gains) { 391 synchronized (mImplLock) { 392 handleAudioDeviceGainsChangedLocked(halReasons, gains); 393 } 394 } 395 }; 396 397 private final ICarOccupantZoneCallback mOccupantZoneCallback = 398 new ICarOccupantZoneCallback.Stub() { 399 @Override 400 public void onOccupantZoneConfigChanged(int flags) { 401 Slogf.d(TAG, "onOccupantZoneConfigChanged(%d)", flags); 402 if (((flags & CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER) 403 != CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER) 404 && ((flags & CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_DISPLAY) 405 != CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_DISPLAY)) { 406 return; 407 } 408 handleOccupantZoneUserChanged(); 409 } 410 }; 411 @GuardedBy("mImplLock") 412 private @Nullable CarAudioPolicyVolumeCallback mCarAudioPolicyVolumeCallback; 413 414 private final HalAudioModuleChangeCallback mHalAudioModuleChangeCallback = 415 new HalAudioModuleChangeCallback() { 416 @Override 417 public void onAudioPortsChanged(List<HalAudioDeviceInfo> deviceInfos) { 418 synchronized (mImplLock) { 419 handleAudioPortsChangedLocked(deviceInfos); 420 } 421 } 422 }; 423 CarAudioService(Context context)424 public CarAudioService(Context context) { 425 this(context, /* audioManagerWrapper = */ null, getAudioConfigurationPath(), 426 new CarVolumeCallbackHandler(), getAudioFadeConfigurationPath()); 427 } 428 429 @VisibleForTesting CarAudioService(Context context, @Nullable AudioManagerWrapper audioManagerWrapper, @Nullable String audioConfigurationPath, CarVolumeCallbackHandler carVolumeCallbackHandler, @Nullable String audioFadeConfigurationPath)430 CarAudioService(Context context, @Nullable AudioManagerWrapper audioManagerWrapper, 431 @Nullable String audioConfigurationPath, 432 CarVolumeCallbackHandler carVolumeCallbackHandler, 433 @Nullable String audioFadeConfigurationPath) { 434 mContext = Objects.requireNonNull(context, 435 "Context to create car audio service can not be null"); 436 mCarAudioConfigurationPath = audioConfigurationPath; 437 mCarAudioFadeConfigurationPath = audioFadeConfigurationPath; 438 mTelephonyManager = mContext.getSystemService(TelephonyManager.class); 439 mAudioManagerWrapper = audioManagerWrapper == null 440 ? new AudioManagerWrapper(mContext.getSystemService(AudioManager.class)) 441 : audioManagerWrapper; 442 mUseDynamicRouting = mContext.getResources().getBoolean(R.bool.audioUseDynamicRouting); 443 mUseCoreAudioVolume = mContext.getResources().getBoolean(R.bool.audioUseCoreVolume); 444 mUseCoreAudioRouting = mContext.getResources().getBoolean(R.bool.audioUseCoreRouting); 445 mKeyEventTimeoutMs = 446 mContext.getResources().getInteger(R.integer.audioVolumeKeyEventTimeoutMs); 447 mUseHalDuckingSignals = mContext.getResources().getBoolean( 448 R.bool.audioUseHalDuckingSignals); 449 450 mUidToZoneMap = new HashMap<>(); 451 mCarVolumeCallbackHandler = carVolumeCallbackHandler; 452 mCarAudioSettings = new CarAudioSettings(mContext); 453 mAudioZoneIdToUserIdMapping = new SparseIntArray(); 454 mAudioVolumeAdjustmentContextsVersion = 455 mContext.getResources().getInteger(R.integer.audioVolumeAdjustmentContextsVersion); 456 boolean useCarVolumeGroupMuting = !runInLegacyMode() && mContext.getResources().getBoolean( 457 R.bool.audioUseCarVolumeGroupMuting); 458 mUseCarVolumeGroupEvents = !runInLegacyMode() && mContext.getResources().getBoolean( 459 R.bool.audioUseCarVolumeGroupEvent); 460 mUseCarVolumeGroupMuting = useCarVolumeGroupMuting; 461 mPersistMasterMuteState = mContext.getResources().getBoolean( 462 R.bool.audioPersistMasterMuteState); 463 mUseFadeManagerConfiguration = enableFadeManagerConfiguration() 464 && carAudioFadeManagerConfiguration() 465 && mContext.getResources().getBoolean(R.bool.audioUseFadeManagerConfiguration); 466 mUseMinMaxActivationVolume = Flags.carAudioMinMaxActivationVolume() && !runInLegacyMode() 467 && mContext.getResources().getBoolean(R.bool.audioUseMinMaxActivationVolume); 468 mUseIsolatedFocusForDynamicDevices = Flags.carAudioDynamicDevices() && !runInLegacyMode() 469 && mContext.getResources().getBoolean( 470 R.bool.audioUseIsolatedAudioFocusForDynamicDevices); 471 mUseKeyEventsForDynamicDevices = Flags.carAudioDynamicDevices() && !runInLegacyMode() 472 && mContext.getResources().getBoolean( 473 R.bool.audioEnableVolumeKeyEventsToDynamicDevices); 474 mPersistFadeBalanceLevels = Flags.audioFadeBalanceGetterApis() && !runInLegacyMode() 475 && mContext.getResources().getBoolean(R.bool.audioPersistFadeBalanceLevels); 476 validateFeatureFlagSettings(); 477 mAudioServerStateCallback = new CarAudioServerStateCallback(this); 478 } 479 validateFeatureFlagSettings()480 private void validateFeatureFlagSettings() { 481 Preconditions.checkArgument(!(runInLegacyMode() && mUseFadeManagerConfiguration), 482 "Fade manager configuration feature can not be enabled in legacy mode"); 483 } 484 485 /** 486 * Starts the initiation for car audio service. 487 * 488 * <p>If {@code asyncAudioServiceInit} is {@code true}, the initiation is async, returning does 489 * not mean the intiation is complete. Caller needs to use {@link waitForInitComplete} to wait 490 * for initiation complete. 491 * 492 * <p>Dynamic routing and volume groups are set only if 493 * {@link #runInLegacyMode} is {@code false}. Otherwise, this service runs in legacy mode. 494 */ 495 @Override init()496 public void init() { 497 if (!asyncAudioServiceInit()) { 498 initCarAudioService(); 499 return; 500 } 501 mHandler.post(() -> { 502 try { 503 initCarAudioService(); 504 } catch (RuntimeException e) { 505 // Multiple functions called within the init process might throw 506 // IllegalStateException or RuntimeException. 507 Slogf.e(TAG, "car audio service initialization failed", e); 508 } finally { 509 synchronized (mImplLock) { 510 mInitCompleted = true; 511 mImplLock.notifyAll(); 512 } 513 } 514 }); 515 } 516 517 /** 518 * Waits for async initialization to complete. 519 * 520 * @param timeoutInMs Timeout in ms. If <=0, wait forever. 521 * @return Whether initialization is completed before timeout. 522 */ waitForInitComplete(int timeoutInMs)523 public boolean waitForInitComplete(int timeoutInMs) throws InterruptedException { 524 if (!asyncAudioServiceInit()) { 525 return true; 526 } 527 TimingsTraceLog log = new TimingsTraceLog(TAG, TraceHelper.TRACE_TAG_CAR_SERVICE); 528 log.traceBegin("audio-waitForInitComplete"); 529 530 boolean result; 531 synchronized (mImplLock) { 532 if (timeoutInMs > 0) { 533 long timeoutUptimeMs = SystemClock.uptimeMillis() + timeoutInMs; 534 while (!mInitCompleted) { 535 long currentUptimeMs = SystemClock.uptimeMillis(); 536 if (currentUptimeMs > timeoutUptimeMs) { 537 break; 538 } 539 mImplLock.wait(timeoutUptimeMs - currentUptimeMs); 540 } 541 } else { 542 while (!mInitCompleted) { 543 mImplLock.wait(); 544 } 545 } 546 result = mInitCompleted && mInitSuccess; 547 } 548 log.traceEnd(); 549 return result; 550 } 551 initCarAudioService()552 private void initCarAudioService() { 553 boolean isAudioServerDown = !mAudioManagerWrapper.isAudioServerRunning(); 554 var executor = new HandlerExecutor(mHandler); 555 mAudioManagerWrapper.setAudioServerStateCallback(executor, mAudioServerStateCallback); 556 synchronized (mImplLock) { 557 mCarInputService = CarLocalServices.getService(CarInputService.class); 558 mIsAudioServerDown = isAudioServerDown; 559 if (mIsAudioServerDown) { 560 mServiceEventLogger.log("Audio server is down at init"); 561 Slogf.e(TAG, "Audio server is down at init, will wait for server state callback" 562 + " to initialize"); 563 synchronized (mImplLock) { 564 mInitSuccess = true; 565 } 566 return; 567 } else if (!runInLegacyMode()) { 568 // Must be called before setting up policies or audio control hal 569 loadAndInitCarAudioZonesLocked(); 570 setupCarAudioPlaybackMonitorLocked(); 571 setupAudioControlDuckingAndVolumeControlLocked(); 572 setupControlAndRoutingAudioPoliciesLocked(); 573 setupFadeManagerConfigAudioPolicyLocked(); 574 setupHalAudioFocusListenerLocked(); 575 setupHalAudioGainCallbackLocked(); 576 setupHalAudioModuleChangeCallbackLocked(); 577 setupAudioConfigurationCallbackLocked(); 578 setupPowerPolicyListener(); 579 mCarInputService.registerKeyEventListener(mCarKeyEventListener, 580 KEYCODES_OF_INTEREST); 581 setupAudioDeviceInfoCallbackLocked(); 582 } else { 583 Slogf.i(TAG, "Audio dynamic routing not enabled, run in legacy mode"); 584 setupLegacyVolumeChangedListener(); 585 } 586 } 587 setSupportedUsages(); 588 restoreMasterMuteState(); 589 590 synchronized (mImplLock) { 591 mInitSuccess = true; 592 } 593 } 594 setSupportedUsages()595 private void setSupportedUsages() { 596 mAudioManagerWrapper.setSupportedSystemUsages(CarAudioContext.getSystemUsages()); 597 } 598 599 @GuardedBy("mImplLock") setupAudioDeviceInfoCallbackLocked()600 private void setupAudioDeviceInfoCallbackLocked() { 601 if (!Flags.carAudioDynamicDevices()) { 602 return; 603 } 604 mAudioDeviceInfoCallback = new CarAudioDeviceCallback(this); 605 mAudioManagerWrapper.registerAudioDeviceCallback(mAudioDeviceInfoCallback, mHandler); 606 } 607 608 @GuardedBy("mImplLock") releaseAudioDeviceInfoCallbackLocked()609 private void releaseAudioDeviceInfoCallbackLocked() { 610 if (!Flags.carAudioDynamicDevices()) { 611 return; 612 } 613 mAudioManagerWrapper.unregisterAudioDeviceCallback(mAudioDeviceInfoCallback); 614 mAudioDeviceInfoCallback = null; 615 } 616 setupPowerPolicyListener()617 private void setupPowerPolicyListener() { 618 mCarAudioPowerListener = CarAudioPowerListener.newCarAudioPowerListener(this); 619 mCarAudioPowerListener.startListeningForPolicyChanges(); 620 } 621 restoreMasterMuteState()622 private void restoreMasterMuteState() { 623 if (useCarVolumeGroupMuting()) { 624 return; 625 } 626 // Restore master mute state if applicable 627 if (persistMasterMuteState()) { 628 boolean storedMasterMute = mCarAudioSettings.isMasterMute(); 629 setMasterMute(storedMasterMute, 0); 630 } 631 } 632 releaseCarAudioService()633 private void releaseCarAudioService() { 634 mAudioManagerWrapper.clearAudioServerStateCallback(); 635 synchronized (mImplLock) { 636 releaseAudioCallbacksLocked(/* isAudioServerDown= */ false); 637 mCarVolumeCallbackHandler.release(); 638 // Reset mInitCompleted so that we could re-init. 639 mInitSuccess = false; 640 mInitCompleted = false; 641 } 642 } 643 644 @Override release()645 public void release() { 646 if (!asyncAudioServiceInit()) { 647 releaseCarAudioService(); 648 return; 649 } 650 // Make sure all tasks currently in the handler finishes, which includes async init 651 // task. 652 CountDownLatch cd = new CountDownLatch(1); 653 mHandler.post(() -> { 654 try { 655 releaseCarAudioService(); 656 } finally { 657 cd.countDown(); 658 } 659 }); 660 try { 661 boolean result = cd.await(RELEASE_TIMEOUT_MS, TimeUnit.MILLISECONDS); 662 if (!result) { 663 Slogf.e(CarLog.TAG_AUDIO, 664 "timeout after %d ms waiting for releaseCarAudioService", 665 RELEASE_TIMEOUT_MS); 666 } 667 } catch (InterruptedException e) { 668 Thread.currentThread().interrupt(); 669 Slogf.w(CarLog.TAG_AUDIO, 670 "interrupted while waiting for car audio service to be released"); 671 } 672 } 673 releaseAudioCallbacks(boolean isAudioServerDown)674 void releaseAudioCallbacks(boolean isAudioServerDown) { 675 synchronized (mImplLock) { 676 releaseAudioCallbacksLocked(isAudioServerDown); 677 } 678 } 679 680 @GuardedBy("mImplLock") releaseAudioCallbacksLocked(boolean isAudioServerDown)681 void releaseAudioCallbacksLocked(boolean isAudioServerDown) { 682 mIsAudioServerDown = isAudioServerDown; 683 releaseLegacyVolumeAndMuteReceiverLocked(); 684 // If the audio server is down prevent from unregistering the audio policy 685 // otherwise car audio service may run into a lock contention with the audio server 686 // until it fully recovers 687 releaseAudioPoliciesLocked(!isAudioServerDown); 688 releaseAudioPlaybackCallbackLocked(); 689 // There is an inherent dependency from HAL audio focus (AFH) 690 // to audio control HAL (ACH), since AFH holds a reference to ACH 691 releaseHalAudioFocusLocked(); 692 releaseCoreVolumeGroupCallbackLocked(); 693 releaseAudioPlaybackMonitorLocked(); 694 releasePowerListenerLocked(); 695 releaseAudioDeviceInfoCallbackLocked(); 696 releaseHalAudioModuleChangeCallbackLocked(); 697 CarOccupantZoneService occupantZoneService = getCarOccupantZoneService(); 698 occupantZoneService.unregisterCallback(mOccupantZoneCallback); 699 mCarInputService.unregisterKeyEventListener(mCarKeyEventListener); 700 // Audio control may be running in the same process as audio server. 701 // Thus we can not release the audio control wrapper for now 702 if (mIsAudioServerDown) { 703 return; 704 } 705 // Audio control wrapper must be released last 706 releaseAudioControlWrapperLocked(); 707 } 708 getCarOccupantZoneService()709 private CarOccupantZoneService getCarOccupantZoneService() { 710 return CarLocalServices.getService(CarOccupantZoneService.class); 711 } 712 713 @GuardedBy("mImplLock") releaseLegacyVolumeAndMuteReceiverLocked()714 private void releaseLegacyVolumeAndMuteReceiverLocked() { 715 if (!runInLegacyMode()) { 716 return; 717 } 718 AudioManagerHelper.unregisterVolumeAndMuteReceiver(mContext, mLegacyVolumeChangedHelper); 719 } 720 721 @GuardedBy("mImplLock") releasePowerListenerLocked()722 private void releasePowerListenerLocked() { 723 if (mCarAudioPowerListener == null) { 724 return; 725 } 726 mCarAudioPowerListener.stopListeningForPolicyChanges(); 727 mCarAudioPowerListener = null; 728 } 729 730 @GuardedBy("mImplLock") releaseAudioPlaybackMonitorLocked()731 private void releaseAudioPlaybackMonitorLocked() { 732 if (mCarAudioPlaybackMonitor == null) { 733 return; 734 } 735 mCarAudioPlaybackMonitor.reset(); 736 mCarAudioPlaybackMonitor = null; 737 } 738 739 @GuardedBy("mImplLock") releaseCoreVolumeGroupCallbackLocked()740 private void releaseCoreVolumeGroupCallbackLocked() { 741 if (mCoreAudioVolumeGroupCallback == null) { 742 return; 743 } 744 mCoreAudioVolumeGroupCallback.release(); 745 mCoreAudioVolumeGroupCallback = null; 746 } 747 748 @GuardedBy("mImplLock") releaseAudioControlWrapperLocked()749 private void releaseAudioControlWrapperLocked() { 750 if (mAudioControlWrapper != null) { 751 mAudioControlWrapper.unlinkToDeath(); 752 mAudioControlWrapper = null; 753 } 754 } 755 756 @GuardedBy("mImplLock") releaseHalAudioFocusLocked()757 private void releaseHalAudioFocusLocked() { 758 if (mHalAudioFocus == null) { 759 return; 760 } 761 mHalAudioFocus.unregisterFocusListener(); 762 mHalAudioFocus = null; 763 } 764 765 @GuardedBy("mImplLock") releaseAudioPlaybackCallbackLocked()766 private void releaseAudioPlaybackCallbackLocked() { 767 if (mCarAudioPlaybackCallback == null) { 768 return; 769 } 770 mAudioManagerWrapper.unregisterAudioPlaybackCallback(mCarAudioPlaybackCallback); 771 mCarAudioPlaybackCallback = null; 772 } 773 774 @GuardedBy("mImplLock") releaseAudioPoliciesLocked(boolean unregisterRoutingPolicy)775 private void releaseAudioPoliciesLocked(boolean unregisterRoutingPolicy) { 776 if (unregisterRoutingPolicy) { 777 releaseAudioRoutingPolicyLocked(); 778 } 779 releaseVolumeControlAudioPolicyLocked(); 780 releaseFocusControlAudioPolicyLocked(); 781 releaseFadeManagerConfigAudioPolicyLocked(); 782 } 783 784 @GuardedBy("mImplLock") releaseVolumeControlAudioPolicyLocked()785 private void releaseVolumeControlAudioPolicyLocked() { 786 if (mVolumeControlAudioPolicy == null) { 787 return; 788 } 789 mAudioManagerWrapper.unregisterAudioPolicy(mVolumeControlAudioPolicy); 790 mVolumeControlAudioPolicy = null; 791 mCarAudioPolicyVolumeCallback = null; 792 } 793 794 @GuardedBy("mImplLock") releaseFocusControlAudioPolicyLocked()795 private void releaseFocusControlAudioPolicyLocked() { 796 if (mFocusControlAudioPolicy == null) { 797 return; 798 } 799 mAudioManagerWrapper.unregisterAudioPolicy(mFocusControlAudioPolicy); 800 mFocusControlAudioPolicy = null; 801 mFocusHandler.setOwningPolicy(null, null); 802 mFocusHandler = null; 803 } 804 805 @GuardedBy("mImplLock") releaseAudioRoutingPolicyLocked()806 private void releaseAudioRoutingPolicyLocked() { 807 if (mRoutingAudioPolicy == null) { 808 return; 809 } 810 mAudioManagerWrapper.unregisterAudioPolicyAsync(mRoutingAudioPolicy); 811 mRoutingAudioPolicy = null; 812 } 813 814 @GuardedBy("mImplLock") releaseFadeManagerConfigAudioPolicyLocked()815 private void releaseFadeManagerConfigAudioPolicyLocked() { 816 if (!mUseFadeManagerConfiguration || mFadeManagerConfigAudioPolicy == null) { 817 return; 818 } 819 820 mAudioManagerWrapper.unregisterAudioPolicy(mFadeManagerConfigAudioPolicy); 821 mFadeManagerConfigAudioPolicy = null; 822 } 823 824 @Override 825 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)826 public void dump(IndentingPrintWriter writer) { 827 synchronized (mImplLock) { 828 writer.println("*CarAudioService*"); 829 writer.increaseIndent(); 830 831 writer.println("Configurations:"); 832 writer.increaseIndent(); 833 writer.printf("Run in legacy mode? %b\n", runInLegacyMode()); 834 writer.printf("Rely on core audio for volume? %b\n", mUseCoreAudioVolume); 835 writer.printf("Rely on core audio for routing? %b\n", mUseCoreAudioRouting); 836 writer.printf("Configured using audio control? %b\n", mCarAudioControlHalConfig); 837 writer.printf("Audio Patch APIs enabled? %b\n", areAudioPatchAPIsEnabled()); 838 writer.printf("Persist master mute state? %b\n", persistMasterMuteState()); 839 writer.printf("Use hal ducking signals? %b\n", mUseHalDuckingSignals); 840 writer.printf("Volume key event timeout ms: %d\n", mKeyEventTimeoutMs); 841 if (mCarAudioConfigurationPath != null) { 842 writer.printf("Car audio configuration path: %s\n", mCarAudioConfigurationPath); 843 } 844 writer.printf("Persist fade and balance levels? %b\n", mPersistFadeBalanceLevels); 845 writer.decreaseIndent(); 846 writer.println(); 847 848 writer.println("Current State:"); 849 writer.increaseIndent(); 850 writer.printf("Master muted? %b\n", mAudioManagerWrapper.isMasterMuted()); 851 if (mCarAudioPowerListener != null) { 852 writer.printf("Audio enabled? %b\n", mCarAudioPowerListener.isAudioEnabled()); 853 } 854 if (asyncAudioServiceInit()) { 855 writer.printf("Async init completed? %b\n", mInitCompleted); 856 writer.printf("Async init succeeded? %b\n", mInitSuccess); 857 } 858 writer.decreaseIndent(); 859 writer.println(); 860 861 if (!runInLegacyMode()) { 862 writer.printf("Volume Group Mute Enabled? %b\n", mUseCarVolumeGroupMuting); 863 writer.printf("Volume Group Events Enabled? %b\n", mUseCarVolumeGroupEvents); 864 writer.printf("Use fade manager configuration? %b\n", mUseFadeManagerConfiguration); 865 writer.printf("Use min/max activation volume? %b\n", mUseMinMaxActivationVolume); 866 writer.printf("Use isolated focus for dynamic devices? %b\n", 867 mUseIsolatedFocusForDynamicDevices); 868 writer.printf("Allow key events to dynamic devices? %b\n", 869 mUseKeyEventsForDynamicDevices); 870 writer.println(); 871 mCarVolume.dump(writer); 872 writer.println(); 873 mCarAudioContext.dump(writer); 874 writer.println(); 875 for (int i = 0; i < mCarAudioZones.size(); i++) { 876 CarAudioZone zone = mCarAudioZones.valueAt(i); 877 zone.dump(writer); 878 } 879 880 writer.println(); 881 writer.println("UserId to Zone Mapping:"); 882 writer.increaseIndent(); 883 for (int index = 0; index < mAudioZoneIdToUserIdMapping.size(); index++) { 884 int audioZoneId = mAudioZoneIdToUserIdMapping.keyAt(index); 885 writer.printf("UserId %d mapped to zone %d\n", 886 mAudioZoneIdToUserIdMapping.get(audioZoneId), 887 audioZoneId); 888 } 889 writer.decreaseIndent(); 890 writer.println(); 891 writer.println("Audio Zone to Occupant Zone Mapping:"); 892 writer.increaseIndent(); 893 for (int index = 0; index < mAudioZoneIdToOccupantZoneIdMapping.size(); index++) { 894 int audioZoneId = mAudioZoneIdToOccupantZoneIdMapping.keyAt(index); 895 writer.printf("AudioZoneId %d mapped to OccupantZoneId %d\n", audioZoneId, 896 mAudioZoneIdToOccupantZoneIdMapping.get(audioZoneId)); 897 } 898 writer.decreaseIndent(); 899 writer.println(); 900 writer.println("UID to Zone Mapping:"); 901 writer.increaseIndent(); 902 for (int callingId : mUidToZoneMap.keySet()) { 903 writer.printf("UID %d mapped to zone %d\n", 904 callingId, 905 mUidToZoneMap.get(callingId)); 906 } 907 writer.decreaseIndent(); 908 909 writer.println(); 910 mFocusHandler.dump(writer); 911 912 writer.println(); 913 getAudioControlWrapperLocked().dump(writer); 914 915 if (mHalAudioFocus != null) { 916 writer.println(); 917 mHalAudioFocus.dump(writer); 918 } else { 919 writer.println("No HalAudioFocus instance\n"); 920 } 921 if (mCarDucking != null) { 922 writer.println(); 923 mCarDucking.dump(writer); 924 } 925 if (mCarVolumeGroupMuting != null) { 926 mCarVolumeGroupMuting.dump(writer); 927 } 928 if (mCarAudioPlaybackCallback != null) { 929 mCarAudioPlaybackCallback.dump(writer); 930 } 931 932 mCarAudioMirrorRequestHandler.dump(writer); 933 mMediaRequestHandler.dump(writer); 934 writer.printf("Number of car audio configs callback registered: %d\n", 935 mConfigsCallbacks.getRegisteredCallbackCount()); 936 writer.printf("Car audio fade configurations available? %b\n", 937 mCarAudioFadeConfigurationHelper != null); 938 if (mCarAudioFadeConfigurationHelper != null) { 939 mCarAudioFadeConfigurationHelper.dump(writer); 940 } 941 } 942 943 writer.println("Service Events:"); 944 writer.increaseIndent(); 945 mServiceEventLogger.dump(writer); 946 writer.decreaseIndent(); 947 948 writer.decreaseIndent(); 949 } 950 } 951 952 @Override 953 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dumpProto(ProtoOutputStream proto)954 public void dumpProto(ProtoOutputStream proto) { 955 synchronized (mImplLock) { 956 long currentStateToken = proto.start(CarAudioDumpProto.CURRENT_STATE); 957 proto.write(CarAudioState.MASTER_MUTED, mAudioManagerWrapper.isMasterMuted()); 958 if (mCarAudioPowerListener != null) { 959 proto.write(CarAudioState.AUDIO_ENABLED, mCarAudioPowerListener.isAudioEnabled()); 960 } 961 proto.end(currentStateToken); 962 963 long configurationToken = proto.start(CarAudioDumpProto.CONFIGURATION); 964 proto.write(CarAudioConfiguration.USE_DYNAMIC_ROUTING, !runInLegacyMode()); 965 proto.write(CarAudioConfiguration.USE_CORE_AUDIO_VOLUME, mUseCoreAudioVolume); 966 proto.write(CarAudioConfiguration.USE_CORE_AUDIO_ROUTING, mUseCoreAudioRouting); 967 proto.write(CarAudioConfiguration.PATCH_API_ENABLED, areAudioPatchAPIsEnabled()); 968 proto.write(CarAudioConfiguration.PERSIST_MASTER_MUTE_STATE, persistMasterMuteState()); 969 proto.write(CarAudioConfiguration.USE_HAL_DUCKING_SIGNALS, mUseHalDuckingSignals); 970 proto.write(CarAudioConfiguration.KEY_EVENT_TIMEOUT_MS, mKeyEventTimeoutMs); 971 if (mCarAudioConfigurationPath != null) { 972 proto.write(CarAudioConfiguration.CAR_AUDIO_CONFIGURATION_PATH, 973 mCarAudioConfigurationPath); 974 } 975 if (runInLegacyMode()) { 976 proto.end(configurationToken); 977 return; 978 } 979 proto.write(CarAudioConfiguration.USE_CAR_VOLUME_GROUP_MUTING, 980 mUseCarVolumeGroupMuting); 981 proto.write(CarAudioConfiguration.USE_CAR_VOLUME_GROUP_EVENTS, 982 mUseCarVolumeGroupEvents); 983 proto.write(CarAudioConfiguration.USE_FADE_MANAGER_CONFIGURATION, 984 mUseFadeManagerConfiguration); 985 proto.write(CarAudioConfiguration.USE_MIN_MAX_ACTIVATION_VOLUME, 986 mUseMinMaxActivationVolume); 987 proto.write(CarAudioConfiguration.USE_ISOLATED_FOCUS_FOR_DYNAMIC_DEVICES, 988 mUseIsolatedFocusForDynamicDevices); 989 proto.end(configurationToken); 990 991 mCarVolume.dumpProto(proto); 992 mCarAudioContext.dumpProto(proto); 993 994 for (int i = 0; i < mCarAudioZones.size(); i++) { 995 CarAudioZone zone = mCarAudioZones.valueAt(i); 996 zone.dumpProto(proto); 997 } 998 999 for (int index = 0; index < mAudioZoneIdToUserIdMapping.size(); index++) { 1000 long audioZoneIdToUserIdMappingToken = proto.start(CarAudioDumpProto 1001 .USER_ID_TO_AUDIO_ZONE_MAPPINGS); 1002 int audioZoneId = mAudioZoneIdToUserIdMapping.keyAt(index); 1003 proto.write(UserIdToAudioZone.USER_ID, 1004 mAudioZoneIdToUserIdMapping.get(audioZoneId)); 1005 proto.write(UserIdToAudioZone.AUDIO_ZONE_ID, audioZoneId); 1006 proto.end(audioZoneIdToUserIdMappingToken); 1007 } 1008 1009 for (int index = 0; index < mAudioZoneIdToOccupantZoneIdMapping.size(); index++) { 1010 long audioZoneIdToOccupantZoneIdMappingToken = proto.start( 1011 CarAudioDumpProto.AUDIO_ZONE_TO_OCCUPANT_ZONE_MAPPINGS); 1012 int audioZoneId = mAudioZoneIdToOccupantZoneIdMapping.keyAt(index); 1013 proto.write(AudioZoneToOccupantZone.AUDIO_ZONE_ID, audioZoneId); 1014 proto.write(AudioZoneToOccupantZone.OCCUPANT_ZONE_ID, 1015 mAudioZoneIdToOccupantZoneIdMapping.get(audioZoneId)); 1016 proto.end(audioZoneIdToOccupantZoneIdMappingToken); 1017 } 1018 1019 for (int callingId : mUidToZoneMap.keySet()) { 1020 long uidToZoneMapToken = proto.start(CarAudioDumpProto.UID_TO_AUDIO_ZONE_MAPPINGS); 1021 proto.write(UidToAudioZone.UID, callingId); 1022 proto.write(UidToAudioZone.AUDIO_ZONE_ID, mUidToZoneMap.get(callingId)); 1023 proto.end(uidToZoneMapToken); 1024 } 1025 1026 mFocusHandler.dumpProto(proto); 1027 1028 if (mHalAudioFocus != null) { 1029 mHalAudioFocus.dumpProto(proto); 1030 } 1031 if (mCarDucking != null) { 1032 mCarDucking.dumpProto(proto); 1033 } 1034 if (mCarVolumeGroupMuting != null) { 1035 mCarVolumeGroupMuting.dumpProto(proto); 1036 } 1037 if (mCarAudioPlaybackCallback != null) { 1038 mCarAudioPlaybackCallback.dumpProto(proto); 1039 } 1040 1041 mCarAudioMirrorRequestHandler.dumpProto(proto); 1042 mMediaRequestHandler.dumpProto(proto); 1043 } 1044 } 1045 1046 @Override isAudioFeatureEnabled(@arAudioFeature int audioFeatureType)1047 public boolean isAudioFeatureEnabled(@CarAudioFeature int audioFeatureType) { 1048 switch (audioFeatureType) { 1049 case AUDIO_FEATURE_DYNAMIC_ROUTING: 1050 return !runInLegacyMode(); 1051 case AUDIO_FEATURE_VOLUME_GROUP_MUTING: 1052 return useCarVolumeGroupMuting(); 1053 case AUDIO_FEATURE_OEM_AUDIO_SERVICE: 1054 return isAnyOemFeatureEnabled(); 1055 case AUDIO_FEATURE_VOLUME_GROUP_EVENTS: 1056 return mUseCarVolumeGroupEvents; 1057 case AUDIO_FEATURE_AUDIO_MIRRORING: 1058 return mCarAudioMirrorRequestHandler.isMirrorAudioEnabled(); 1059 case AUDIO_FEATURE_MIN_MAX_ACTIVATION_VOLUME: 1060 return mUseMinMaxActivationVolume; 1061 case AUDIO_FEATURE_PERSIST_FADE_BALANCE_VALUES: 1062 return mPersistFadeBalanceLevels; 1063 default: 1064 throw new IllegalArgumentException("Unknown Audio Feature type: " 1065 + audioFeatureType); 1066 } 1067 } 1068 useCarVolumeGroupMuting()1069 private boolean useCarVolumeGroupMuting() { 1070 synchronized (mImplLock) { 1071 return mUseCarVolumeGroupMuting; 1072 } 1073 } 1074 persistMasterMuteState()1075 private boolean persistMasterMuteState() { 1076 return !useCarVolumeGroupMuting() && mPersistMasterMuteState; 1077 } 1078 isAnyOemFeatureEnabled()1079 private boolean isAnyOemFeatureEnabled() { 1080 CarOemProxyService proxy = CarLocalServices.getService(CarOemProxyService.class); 1081 1082 return proxy != null && proxy.isOemServiceEnabled() 1083 && (proxy.getCarOemAudioFocusService() != null 1084 || proxy.getCarOemAudioVolumeService() != null 1085 || proxy.getCarOemAudioDuckingService() != null); 1086 } 1087 1088 /** 1089 * {@link android.car.media.CarAudioManager#setGroupVolume(int, int, int, int)} 1090 */ 1091 @Override setGroupVolume(int zoneId, int groupId, int index, int flags)1092 public void setGroupVolume(int zoneId, int groupId, int index, int flags) { 1093 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 1094 callbackGroupVolumeChange(zoneId, groupId, flags); 1095 int callbackFlags = flags; 1096 int eventTypes = EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED; 1097 // For legacy stream type based volume control 1098 boolean wasMute; 1099 if (runInLegacyMode()) { 1100 mAudioManagerWrapper.setStreamVolume( 1101 CarAudioDynamicRouting.STREAM_TYPES[groupId], index, flags); 1102 return; 1103 } 1104 synchronized (mImplLock) { 1105 CarVolumeGroup group = getCarVolumeGroupLocked(zoneId, groupId); 1106 wasMute = group.isMuted(); 1107 group.setCurrentGainIndex(index); 1108 } 1109 if (wasMute) { 1110 handleMuteChanged(zoneId, groupId, flags); 1111 eventTypes |= EVENT_TYPE_MUTE_CHANGED; 1112 } 1113 1114 if (!runInLegacyMode() && !isPlaybackOnVolumeGroupActive(zoneId, groupId)) { 1115 callbackFlags |= FLAG_PLAY_SOUND; 1116 } 1117 callbackVolumeGroupEvent(List.of(convertVolumeChangeToEvent( 1118 getVolumeGroupInfo(zoneId, groupId), callbackFlags, eventTypes))); 1119 } 1120 handleActivationVolumeWithActivationInfos( List<CarAudioPlaybackMonitor.ActivationInfo> activationInfoList, int zoneId, int zoneConfigId)1121 void handleActivationVolumeWithActivationInfos( 1122 List<CarAudioPlaybackMonitor.ActivationInfo> activationInfoList, int zoneId, 1123 int zoneConfigId) { 1124 ArrayList<Integer> groupIdList = new ArrayList<>(); 1125 synchronized (mImplLock) { 1126 if (mCarAudioZones.get(zoneId).getCurrentCarAudioZoneConfig().getZoneConfigId() 1127 != zoneConfigId) { 1128 Slogf.w(CarLog.TAG_AUDIO, "Zone configuration for zone %d is changed, no " 1129 + "activation volume is invoked", zoneId); 1130 return; 1131 } 1132 for (int i = 0; i < activationInfoList.size(); i++) { 1133 int volumeGroupId = activationInfoList.get(i) 1134 .mGroupId; 1135 CarVolumeGroup volumeGroup = mCarAudioZones.get(zoneId) 1136 .getCurrentVolumeGroup(volumeGroupId); 1137 if (!volumeGroup.handleActivationVolume( 1138 activationInfoList.get(i).mInvocationType)) { 1139 continue; 1140 } 1141 groupIdList.add(volumeGroup.getId()); 1142 } 1143 } 1144 handleActivationVolumeCallback(groupIdList, zoneId); 1145 } 1146 handleActivationVolumeCallback(List<Integer> groupIdList, int zoneId)1147 private void handleActivationVolumeCallback(List<Integer> groupIdList, int zoneId) { 1148 if (groupIdList.isEmpty()) { 1149 return; 1150 } 1151 List<CarVolumeGroupInfo> volumeGroupInfoList = new ArrayList<>(groupIdList.size()); 1152 for (int i = 0; i < groupIdList.size(); i++) { 1153 int groupId = groupIdList.get(i); 1154 callbackGroupVolumeChange(zoneId, groupId, FLAG_SHOW_UI); 1155 volumeGroupInfoList.add(getVolumeGroupInfo(zoneId, groupId)); 1156 } 1157 callbackVolumeGroupEvent(List.of(convertVolumeChangesToEvents(volumeGroupInfoList, 1158 EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED, List.of(EXTRA_INFO_ATTENUATION_ACTIVATION, 1159 EXTRA_INFO_SHOW_UI)))); 1160 } 1161 1162 @GuardedBy("mImplLock") resetActivationTypeLocked(int zoneId)1163 private void resetActivationTypeLocked(int zoneId) { 1164 if (mCarAudioPlaybackMonitor == null) { 1165 return; 1166 } 1167 mCarAudioPlaybackMonitor.resetActivationTypesForZone(zoneId); 1168 } 1169 handleMuteChanged(int zoneId, int groupId, int flags)1170 private void handleMuteChanged(int zoneId, int groupId, int flags) { 1171 if (!useCarVolumeGroupMuting()) { 1172 return; 1173 } 1174 callbackGroupMuteChanged(zoneId, groupId, flags); 1175 mCarVolumeGroupMuting.carMuteChanged(); 1176 } 1177 callbackGroupVolumeChange(int zoneId, int groupId, int flags)1178 private void callbackGroupVolumeChange(int zoneId, int groupId, int flags) { 1179 int callbackFlags = flags; 1180 if (!runInLegacyMode() && !isPlaybackOnVolumeGroupActive(zoneId, groupId)) { 1181 callbackFlags |= FLAG_PLAY_SOUND; 1182 } 1183 mCarVolumeCallbackHandler.onVolumeGroupChange(zoneId, groupId, callbackFlags); 1184 } 1185 callbackGroupMuteChanged(int zoneId, int groupId, int flags)1186 private void callbackGroupMuteChanged(int zoneId, int groupId, int flags) { 1187 mCarVolumeCallbackHandler.onGroupMuteChange(zoneId, groupId, flags); 1188 } 1189 setMasterMute(boolean mute, int flags)1190 void setMasterMute(boolean mute, int flags) { 1191 mAudioManagerWrapper.setMasterMute(mute, flags); 1192 1193 // Master Mute only applies to primary zone 1194 callbackMasterMuteChange(PRIMARY_AUDIO_ZONE, flags); 1195 } 1196 callbackMasterMuteChange(int zoneId, int flags)1197 void callbackMasterMuteChange(int zoneId, int flags) { 1198 mCarVolumeCallbackHandler.onMasterMuteChanged(zoneId, flags); 1199 1200 // Persists master mute state if applicable 1201 if (persistMasterMuteState()) { 1202 mCarAudioSettings.storeMasterMute(mAudioManagerWrapper.isMasterMuted()); 1203 } 1204 } 1205 callbackVolumeGroupEvent(List<CarVolumeGroupEvent> events)1206 void callbackVolumeGroupEvent(List<CarVolumeGroupEvent> events) { 1207 if (events.isEmpty()) { 1208 Slogf.w(TAG, "Callback not initiated for empty events list"); 1209 return; 1210 } 1211 mCarVolumeEventHandler.onVolumeGroupEvent(events); 1212 } 1213 1214 /** 1215 * {@link android.car.media.CarAudioManager#getGroupMaxVolume(int, int)} 1216 */ 1217 @Override getGroupMaxVolume(int zoneId, int groupId)1218 public int getGroupMaxVolume(int zoneId, int groupId) { 1219 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 1220 1221 if (runInLegacyMode()) { 1222 return mAudioManagerWrapper.getStreamMaxVolume( 1223 CarAudioDynamicRouting.STREAM_TYPES[groupId]); 1224 } 1225 1226 synchronized (mImplLock) { 1227 CarVolumeGroup group = getCarVolumeGroupLocked(zoneId, groupId); 1228 return group.getMaxGainIndex(); 1229 } 1230 } 1231 1232 /** 1233 * {@link android.car.media.CarAudioManager#getGroupMinVolume(int, int)} 1234 */ 1235 @Override getGroupMinVolume(int zoneId, int groupId)1236 public int getGroupMinVolume(int zoneId, int groupId) { 1237 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 1238 1239 if (runInLegacyMode()) { 1240 return mAudioManagerWrapper.getStreamMinVolume( 1241 CarAudioDynamicRouting.STREAM_TYPES[groupId]); 1242 } 1243 1244 synchronized (mImplLock) { 1245 CarVolumeGroup group = getCarVolumeGroupLocked(zoneId, groupId); 1246 return group.getMinGainIndex(); 1247 } 1248 } 1249 1250 /** 1251 * {@link android.car.media.CarAudioManager#getGroupVolume(int, int)} 1252 */ 1253 @Override getGroupVolume(int zoneId, int groupId)1254 public int getGroupVolume(int zoneId, int groupId) { 1255 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 1256 1257 // For legacy stream type based volume control 1258 if (runInLegacyMode()) { 1259 return mAudioManagerWrapper.getStreamVolume( 1260 CarAudioDynamicRouting.STREAM_TYPES[groupId]); 1261 } 1262 1263 synchronized (mImplLock) { 1264 CarVolumeGroup group = getCarVolumeGroupLocked(zoneId, groupId); 1265 return group.getCurrentGainIndex(); 1266 } 1267 } 1268 1269 /** 1270 * {@link android.car.media.CarAudioManager#setPrimaryZoneMediaAudioRequestCallback()} 1271 */ 1272 @Override registerPrimaryZoneMediaAudioRequestCallback( IPrimaryZoneMediaAudioRequestCallback callback)1273 public boolean registerPrimaryZoneMediaAudioRequestCallback( 1274 IPrimaryZoneMediaAudioRequestCallback callback) { 1275 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1276 requireNonLegacyRouting(); 1277 return mMediaRequestHandler.registerPrimaryZoneMediaAudioRequestCallback(callback); 1278 } 1279 1280 /** 1281 * {@link android.car.media.CarAudioManager#clearPrimaryZoneMediaAudioRequestCallback()} 1282 */ 1283 @Override unregisterPrimaryZoneMediaAudioRequestCallback( IPrimaryZoneMediaAudioRequestCallback callback)1284 public void unregisterPrimaryZoneMediaAudioRequestCallback( 1285 IPrimaryZoneMediaAudioRequestCallback callback) { 1286 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1287 requireNonLegacyRouting(); 1288 List<Long> ownedRequests = mMediaRequestHandler.getRequestsOwnedByApprover(callback); 1289 for (int index = 0; index < ownedRequests.size(); index++) { 1290 long requestId = ownedRequests.get(index); 1291 handleUnassignAudioFromUserIdOnPrimaryAudioZone(requestId); 1292 } 1293 if (!mMediaRequestHandler.unregisterPrimaryZoneMediaAudioRequestCallback(callback)) { 1294 Slogf.e(TAG, 1295 "unregisterPrimaryZoneMediaAudioRequestCallback could not remove callback"); 1296 } 1297 } 1298 1299 /** 1300 * {@link android.car.media.CarAudioManager#requestMediaAudioOnPrimaryZone( 1301 * MediaAudioRequest)} 1302 */ 1303 @Override requestMediaAudioOnPrimaryZone(IMediaAudioRequestStatusCallback callback, CarOccupantZoneManager.OccupantZoneInfo info)1304 public long requestMediaAudioOnPrimaryZone(IMediaAudioRequestStatusCallback callback, 1305 CarOccupantZoneManager.OccupantZoneInfo info) { 1306 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1307 requireNonLegacyRouting(); 1308 Objects.requireNonNull(callback, "Media audio request callback can not be null"); 1309 Objects.requireNonNull(info, "Occupant zone info can not be null"); 1310 1311 int audioZoneId = getCarOccupantZoneService().getAudioZoneIdForOccupant(info.zoneId); 1312 if (audioZoneId == PRIMARY_AUDIO_ZONE) { 1313 throw new IllegalArgumentException("Occupant " + info 1314 + " already owns the primary audio zone"); 1315 } 1316 1317 verifyMirrorNotEnabledForZone(/* runIfFailed= */ null, "request", audioZoneId); 1318 1319 synchronized (mImplLock) { 1320 int index = mAudioZoneIdToUserIdMapping.indexOfKey(audioZoneId); 1321 if (index < 0) { 1322 Slogf.w(TAG, "Audio zone id %d is not mapped to any user id", audioZoneId); 1323 return INVALID_REQUEST_ID; 1324 } 1325 } 1326 1327 return mMediaRequestHandler.requestMediaAudioOnPrimaryZone(callback, info); 1328 } 1329 verifyMirrorNotEnabledForZone(Runnable runIfFailed, String requestType, int audioZoneId)1330 private void verifyMirrorNotEnabledForZone(Runnable runIfFailed, String requestType, 1331 int audioZoneId) { 1332 if (mCarAudioMirrorRequestHandler.isMirrorEnabledForZone(audioZoneId)) { 1333 long mirrorId = mCarAudioMirrorRequestHandler.getRequestIdForAudioZone(audioZoneId); 1334 CarOccupantZoneManager.OccupantZoneInfo info = 1335 getCarOccupantZoneService().getOccupantForAudioZoneId(audioZoneId); 1336 if (runIfFailed != null) { 1337 runIfFailed.run(); 1338 } 1339 throw new IllegalStateException("Can not " + requestType + " audio share to primary " 1340 + "zone for occupant " + info + ", as occupant is currently mirroring audio " 1341 + "in mirroring id " + mirrorId); 1342 } 1343 } 1344 1345 /** 1346 * {@link android.car.media.CarAudioManager#allowMediaAudioOnPrimaryZone( 1347 * android.car.media.CarAudioManager.MediaRequestToken, long, boolean)} 1348 */ 1349 @Override allowMediaAudioOnPrimaryZone(IBinder token, long requestId, boolean allow)1350 public boolean allowMediaAudioOnPrimaryZone(IBinder token, long requestId, boolean allow) { 1351 Objects.requireNonNull(token, "Media request token must not be null"); 1352 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1353 requireNonLegacyRouting(); 1354 1355 boolean canApprove = mMediaRequestHandler.isAudioMediaCallbackRegistered(token); 1356 if (!allow || !canApprove) { 1357 if (!canApprove) { 1358 Slogf.w(TAG, "allowMediaAudioOnPrimaryZone Request %d can not be approved by " 1359 + "token %s", requestId, token); 1360 } 1361 return mMediaRequestHandler.rejectMediaAudioRequest(requestId); 1362 } 1363 1364 CarOccupantZoneManager.OccupantZoneInfo info = 1365 mMediaRequestHandler.getOccupantForRequest(requestId); 1366 1367 if (info == null) { 1368 Slogf.w(TAG, "allowMediaAudioOnPrimaryZone Request %d is no longer present", 1369 requestId); 1370 return false; 1371 } 1372 1373 CarOccupantZoneService carOccupantZoneService = getCarOccupantZoneService(); 1374 int audioZoneId = carOccupantZoneService.getAudioZoneIdForOccupant(info.zoneId); 1375 1376 verifyMirrorNotEnabledForZone(() -> mMediaRequestHandler 1377 .rejectMediaAudioRequest(requestId), "allow", audioZoneId); 1378 1379 int userId = carOccupantZoneService.getUserForOccupant(info.zoneId); 1380 synchronized (mImplLock) { 1381 return handleAssignAudioFromUserIdToPrimaryAudioZoneLocked(token, 1382 userId, audioZoneId, requestId); 1383 } 1384 } 1385 1386 /** 1387 * {@link android.car.media.CarAudioManager#isMediaAudioAllowedInPrimaryZone( 1388 * CarOccupantZoneManager.OccupantZoneInfo)} 1389 */ 1390 @Override isMediaAudioAllowedInPrimaryZone(CarOccupantZoneManager.OccupantZoneInfo info)1391 public boolean isMediaAudioAllowedInPrimaryZone(CarOccupantZoneManager.OccupantZoneInfo info) { 1392 Objects.requireNonNull(info, "Occupant zone info can not be null"); 1393 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1394 requireNonLegacyRouting(); 1395 1396 return mMediaRequestHandler.isMediaAudioAllowedInPrimaryZone(info); 1397 } 1398 1399 /** 1400 * {@link android.car.media.CarAudioManager#resetMediaAudioOnPrimaryZone( 1401 * CarOccupantZoneManager.OccupantZoneInfo)} 1402 */ 1403 @Override resetMediaAudioOnPrimaryZone(CarOccupantZoneManager.OccupantZoneInfo info)1404 public boolean resetMediaAudioOnPrimaryZone(CarOccupantZoneManager.OccupantZoneInfo info) { 1405 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1406 requireNonLegacyRouting(); 1407 1408 long requestId = mMediaRequestHandler.getRequestIdForOccupant(info); 1409 if (requestId == INVALID_REQUEST_ID) { 1410 Slogf.w(TAG, "resetMediaAudioOnPrimaryZone no request id for occupant %s", info); 1411 return false; 1412 } 1413 return handleUnassignAudioFromUserIdOnPrimaryAudioZone(requestId); 1414 } 1415 1416 /** 1417 * {@link android.car.media.CarAudioManager#cancelMediaAudioOnPrimaryZone(long)} 1418 */ 1419 @Override cancelMediaAudioOnPrimaryZone(long requestId)1420 public boolean cancelMediaAudioOnPrimaryZone(long requestId) { 1421 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1422 requireNonLegacyRouting(); 1423 1424 CarOccupantZoneManager.OccupantZoneInfo info = 1425 mMediaRequestHandler.getOccupantForRequest(requestId); 1426 if (info == null) { 1427 Slogf.w(TAG, "cancelMediaAudioOnPrimaryZone no occupant for request %d", 1428 requestId); 1429 return false; 1430 } 1431 1432 if (!mMediaRequestHandler.isMediaAudioAllowedInPrimaryZone(info)) { 1433 return mMediaRequestHandler.cancelMediaAudioOnPrimaryZone(requestId); 1434 } 1435 1436 return handleUnassignAudioFromUserIdOnPrimaryAudioZone(requestId); 1437 } 1438 1439 /** 1440 * {@link CarAudioManager#setAudioZoneMirrorStatusCallback(Executor, 1441 * AudioZonesMirrorStatusCallback)} 1442 */ 1443 @Override registerAudioZonesMirrorStatusCallback( IAudioZonesMirrorStatusCallback callback)1444 public boolean registerAudioZonesMirrorStatusCallback( 1445 IAudioZonesMirrorStatusCallback callback) { 1446 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1447 requireNonLegacyRouting(); 1448 requireAudioMirroring(); 1449 1450 return mCarAudioMirrorRequestHandler.registerAudioZonesMirrorStatusCallback(callback); 1451 } 1452 1453 /** 1454 * {@link CarAudioManager#clearAudioZonesMirrorStatusCallback()} 1455 */ 1456 @Override unregisterAudioZonesMirrorStatusCallback(IAudioZonesMirrorStatusCallback callback)1457 public void unregisterAudioZonesMirrorStatusCallback(IAudioZonesMirrorStatusCallback callback) { 1458 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1459 requireNonLegacyRouting(); 1460 requireAudioMirroring(); 1461 1462 if (!mCarAudioMirrorRequestHandler.unregisterAudioZonesMirrorStatusCallback(callback)) { 1463 Slogf.w(TAG, "Could not unregister audio zones mirror status callback ," 1464 + "callback could have died before unregister was called."); 1465 } 1466 } 1467 1468 /** 1469 * {@link CarAudioManager#canEnableAudioMirror()} 1470 */ 1471 @Override canEnableAudioMirror()1472 public int canEnableAudioMirror() { 1473 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1474 requireNonLegacyRouting(); 1475 requireAudioMirroring(); 1476 1477 return mCarAudioMirrorRequestHandler.canEnableAudioMirror(); 1478 } 1479 1480 /** 1481 * {@link CarAudioManager#enableMirrorForAudioZones(List)} 1482 */ 1483 @Override enableMirrorForAudioZones(int[] audioZones)1484 public long enableMirrorForAudioZones(int[] audioZones) { 1485 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1486 requireNonLegacyRouting(); 1487 requireAudioMirroring(); 1488 verifyCanMirrorToAudioZones(audioZones, /* forExtension= */ false); 1489 1490 long requestId = mCarAudioMirrorRequestHandler.getUniqueRequestIdAndAssignMirrorDevice(); 1491 1492 if (requestId == INVALID_REQUEST_ID) { 1493 Slogf.e(TAG, "enableMirrorForAudioZones failed," 1494 + " audio mirror not allowed, no more audio mirroring devices available"); 1495 throw new IllegalStateException("Out of available mirror output devices"); 1496 } 1497 1498 mHandler.post(() -> handleEnableAudioMirrorForZones(audioZones, requestId)); 1499 1500 return requestId; 1501 } 1502 1503 /** 1504 * {@link CarAudioManager#extendAudioMirrorRequest(long, List)} 1505 */ 1506 @Override extendAudioMirrorRequest(long mirrorId, int[] audioZones)1507 public void extendAudioMirrorRequest(long mirrorId, int[] audioZones) { 1508 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1509 requireNonLegacyRouting(); 1510 requireAudioMirroring(); 1511 verifyCanMirrorToAudioZones(audioZones, /* forExtension= */ true); 1512 mCarAudioMirrorRequestHandler.verifyValidRequestId(mirrorId); 1513 1514 mHandler.post(() -> handleEnableAudioMirrorForZones(audioZones, mirrorId)); 1515 } 1516 1517 /** 1518 * {@link CarAudioManager#disableAudioMirrorForZone(int)} 1519 */ 1520 @Override disableAudioMirrorForZone(int zoneId)1521 public void disableAudioMirrorForZone(int zoneId) { 1522 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1523 requireNonLegacyRouting(); 1524 requireAudioMirroring(); 1525 checkAudioZoneId(zoneId); 1526 long requestId = mCarAudioMirrorRequestHandler.getRequestIdForAudioZone(zoneId); 1527 if (requestId == INVALID_REQUEST_ID) { 1528 Slogf.w(TAG, "Could not disable audio mirror for zone %d, zone was not mirroring", 1529 zoneId); 1530 return; 1531 } 1532 1533 mHandler.post(() -> handleDisableAudioMirrorForZonesInConfig(new int[]{zoneId}, requestId)); 1534 } 1535 1536 /** 1537 * {@link CarAudioManager#disableAudioMirror(long)}} 1538 */ 1539 @Override disableAudioMirror(long mirrorId)1540 public void disableAudioMirror(long mirrorId) { 1541 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1542 requireNonLegacyRouting(); 1543 requireAudioMirroring(); 1544 Preconditions.checkArgument(mirrorId != INVALID_REQUEST_ID, 1545 "Mirror id can not be INVALID_REQUEST_ID"); 1546 1547 int[] config = mCarAudioMirrorRequestHandler.getMirrorAudioZonesForRequest(mirrorId); 1548 if (config == null) { 1549 Slogf.w(TAG, "disableAudioMirror mirror id %d no longer exist", 1550 mirrorId); 1551 return; 1552 } 1553 1554 mHandler.post(() -> handleDisableAudioMirrorForZonesInConfig(config, mirrorId)); 1555 } 1556 1557 /** 1558 * {@link CarAudioManager#getMirrorAudioZonesForAudioZone(int)} 1559 */ 1560 @Override getMirrorAudioZonesForAudioZone(int zoneId)1561 public int[] getMirrorAudioZonesForAudioZone(int zoneId) { 1562 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1563 requireNonLegacyRouting(); 1564 requireAudioMirroring(); 1565 long requestId = mCarAudioMirrorRequestHandler.getRequestIdForAudioZone(zoneId); 1566 1567 if (requestId == INVALID_REQUEST_ID) { 1568 return EMPTY_INT_ARRAY; 1569 } 1570 int[] config = mCarAudioMirrorRequestHandler.getMirrorAudioZonesForRequest(requestId); 1571 return config == null ? new int[0] : config; 1572 } 1573 1574 /** 1575 * {@link CarAudioManager#getMirrorAudioZonesForMirrorRequest(long)} 1576 */ 1577 @Override getMirrorAudioZonesForMirrorRequest(long mirrorId)1578 public int[] getMirrorAudioZonesForMirrorRequest(long mirrorId) { 1579 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1580 requireNonLegacyRouting(); 1581 requireAudioMirroring(); 1582 Preconditions.checkArgument(mirrorId != INVALID_REQUEST_ID, 1583 "Mirror request id can not be INVALID_REQUEST_ID"); 1584 1585 int[] config = mCarAudioMirrorRequestHandler.getMirrorAudioZonesForRequest(mirrorId); 1586 return config == null ? new int[0] : config; 1587 } 1588 1589 @GuardedBy("mImplLock") getCarVolumeGroupLocked(int zoneId, int groupId)1590 private CarVolumeGroup getCarVolumeGroupLocked(int zoneId, int groupId) { 1591 return getCarAudioZoneLocked(zoneId).getCurrentVolumeGroup(groupId); 1592 } 1593 1594 @GuardedBy("mImplLock") 1595 @Nullable getCarVolumeGroupLocked(int zoneId, String groupName)1596 private CarVolumeGroup getCarVolumeGroupLocked(int zoneId, String groupName) { 1597 return getCarAudioZoneLocked(zoneId).getCurrentVolumeGroup(groupName); 1598 } 1599 verifyCanMirrorToAudioZones(int[] audioZones, boolean forExtension)1600 private void verifyCanMirrorToAudioZones(int[] audioZones, boolean forExtension) { 1601 Objects.requireNonNull(audioZones, "Mirror audio zones can not be null"); 1602 int minSize = 2; 1603 if (forExtension) { 1604 minSize = 1; 1605 } 1606 Preconditions.checkArgument(audioZones.length >= minSize, 1607 "Mirror audio zones needs to have at least " + minSize + " zones"); 1608 ArraySet<Integer> zones = CarServiceUtils.toIntArraySet(audioZones); 1609 1610 if (zones.size() != audioZones.length) { 1611 throw new IllegalArgumentException( 1612 "Audio zones in mirror configuration must be unique " 1613 + Arrays.toString(audioZones)); 1614 } 1615 1616 if (zones.contains(PRIMARY_AUDIO_ZONE)) { 1617 throw new IllegalArgumentException( 1618 "Audio mirroring not allowed for primary audio zone"); 1619 } 1620 1621 for (int c = 0; c < audioZones.length; c++) { 1622 int zoneId = audioZones[c]; 1623 1624 checkAudioZoneId(zoneId); 1625 1626 int userId = getUserIdForZone(zoneId); 1627 if (userId == UserManagerHelper.USER_NULL) { 1628 throw new IllegalStateException( 1629 "Audio zone must have an active user to allow mirroring"); 1630 } 1631 1632 CarOccupantZoneManager.OccupantZoneInfo info = getCarOccupantZoneService() 1633 .getOccupantForAudioZoneId(zoneId); 1634 1635 if (mMediaRequestHandler.isMediaAudioAllowedInPrimaryZone(info)) { 1636 throw new IllegalStateException( 1637 "Occupant " + info + " in audio zone " + zoneId 1638 + " is currently sharing to primary zone, " 1639 + "undo audio sharing in primary zone before setting up mirroring"); 1640 } 1641 1642 long zoneRequestId = mCarAudioMirrorRequestHandler.getRequestIdForAudioZone(zoneId); 1643 1644 if (zoneRequestId == INVALID_REQUEST_ID) { 1645 continue; 1646 } 1647 1648 throw new IllegalStateException( 1649 "Audio zone " + zoneId + " is already mirroring"); 1650 } 1651 } 1652 handleEnableAudioMirrorForZones(int[] audioZoneIds, long requestId)1653 private void handleEnableAudioMirrorForZones(int[] audioZoneIds, long requestId) { 1654 AudioDeviceAttributes mirrorDevice = 1655 mCarAudioMirrorRequestHandler.getAudioDevice(requestId); 1656 if (mirrorDevice == null) { 1657 Slogf.e(TAG, "handleEnableAudioMirrorForZones failed," 1658 + " audio mirror not allowed as there are no more mirror devices available"); 1659 mCarAudioMirrorRequestHandler.rejectMirrorForZones(requestId, audioZoneIds); 1660 return; 1661 } 1662 int[] config = mCarAudioMirrorRequestHandler.getMirrorAudioZonesForRequest(requestId); 1663 // Check it is same configuration as requested, order is preserved as it is assumed 1664 // that the first zone id is the source and other zones are the receiver of the audio 1665 // mirror 1666 if (Arrays.equals(audioZoneIds, config)) { 1667 Slogf.i(TAG, "handleEnableAudioMirrorForZones audio mirror already set for zones %s", 1668 Arrays.toString(audioZoneIds)); 1669 mCarAudioMirrorRequestHandler.enableMirrorForZones(requestId, audioZoneIds); 1670 return; 1671 } 1672 1673 ArrayList<Integer> zones = new ArrayList<>(); 1674 if (config != null) { 1675 zones.addAll(CarServiceUtils.asList(config)); 1676 } 1677 1678 for (int index = 0; index < audioZoneIds.length; index++) { 1679 int audioZoneId = audioZoneIds[index]; 1680 1681 int userId = getUserIdForZone(audioZoneId); 1682 if (userId == UserManagerHelper.USER_NULL) { 1683 Slogf.w(TAG, "handleEnableAudioMirrorForZones failed," 1684 + " audio mirror not allowed for unassigned audio zone %d", audioZoneId); 1685 mCarAudioMirrorRequestHandler.rejectMirrorForZones(requestId, audioZoneIds); 1686 return; 1687 } 1688 1689 long zoneRequestId = mCarAudioMirrorRequestHandler.getRequestIdForAudioZone( 1690 audioZoneId); 1691 1692 if (zoneRequestId != INVALID_REQUEST_ID && zoneRequestId != requestId) { 1693 Slogf.w(TAG, "handleEnableAudioMirrorForZones failed," 1694 + " audio mirror not allowed for already mirroring audio zone %d", 1695 audioZoneId); 1696 mCarAudioMirrorRequestHandler.rejectMirrorForZones(requestId, audioZoneIds); 1697 return; 1698 } 1699 1700 CarOccupantZoneManager.OccupantZoneInfo info = getCarOccupantZoneService() 1701 .getOccupantForAudioZoneId(audioZoneId); 1702 1703 if (mMediaRequestHandler.isMediaAudioAllowedInPrimaryZone(info)) { 1704 Slogf.w(TAG, "handleEnableAudioMirrorForZones failed," 1705 + " audio mirror not allowed for audio zone %d sharing to primary zone", 1706 audioZoneId); 1707 mCarAudioMirrorRequestHandler.rejectMirrorForZones(requestId, audioZoneIds); 1708 return; 1709 } 1710 zones.add(audioZoneId); 1711 } 1712 1713 int[] audioZoneIdsToAdd = CarServiceUtils.toIntArray(zones); 1714 1715 TimingsTraceLog t = new TimingsTraceLog(TAG, TraceHelper.TRACE_TAG_CAR_SERVICE); 1716 t.traceBegin("audio-mirror-" + Arrays.toString(audioZoneIdsToAdd)); 1717 synchronized (mImplLock) { 1718 List<AudioFocusStackRequest> mediaFocusStacks = new ArrayList<>(); 1719 t.traceBegin("audio-mirror-focus-loss-" + Arrays.toString(audioZoneIdsToAdd)); 1720 transientlyLoseFocusForMirrorLocked(audioZoneIdsToAdd, t, mediaFocusStacks); 1721 t.traceEnd(); 1722 1723 t.traceBegin("audio-mirror-routing-" + Arrays.toString(audioZoneIdsToAdd)); 1724 if (!setupAudioRoutingForUserInMirrorDeviceLocked(audioZoneIdsToAdd, mirrorDevice)) { 1725 for (int index = 0; index < mediaFocusStacks.size(); index++) { 1726 AudioFocusStackRequest request = mediaFocusStacks.get(index); 1727 mFocusHandler.regainMediaAudioFocusInZone(request.mStack, 1728 request.mOriginalZoneId); 1729 } 1730 mCarAudioMirrorRequestHandler.rejectMirrorForZones(requestId, audioZoneIdsToAdd); 1731 return; 1732 } 1733 t.traceEnd(); 1734 1735 // TODO(b/268383539): Implement multi zone focus for mirror 1736 // Currently only selecting the source zone as focus manager 1737 t.traceBegin("audio-mirror-focus-gain-" + Arrays.toString(audioZoneIdsToAdd)); 1738 int zoneId = audioZoneIdsToAdd[0]; 1739 for (int index = 0; index < mediaFocusStacks.size(); index++) { 1740 AudioFocusStackRequest request = mediaFocusStacks.get(index); 1741 t.traceBegin("audio-mirror-focus-gain-" + index + "-zone-" + zoneId); 1742 mFocusHandler.regainMediaAudioFocusInZone(request.mStack, zoneId); 1743 t.traceEnd(); 1744 } 1745 t.traceEnd(); 1746 } 1747 t.traceEnd(); 1748 sendMirrorInfoToAudioHal(mirrorDevice.getAddress(), audioZoneIdsToAdd); 1749 mCarAudioMirrorRequestHandler.enableMirrorForZones(requestId, audioZoneIdsToAdd); 1750 } 1751 sendMirrorInfoToAudioHal(String mirrorSource, int[] audioZoneIds)1752 private void sendMirrorInfoToAudioHal(String mirrorSource, int[] audioZoneIds) { 1753 StringBuilder builder = new StringBuilder(); 1754 builder.append(MIRROR_COMMAND_SOURCE); 1755 builder.append(mirrorSource); 1756 builder.append(MIRROR_COMMAND_SEPARATOR); 1757 1758 builder.append(MIRROR_COMMAND_DESTINATION); 1759 for (int index = 0; index < audioZoneIds.length; index++) { 1760 int zoneId = audioZoneIds[index]; 1761 String zoneMediaAddress = getOutputDeviceAddressForUsageInternal(zoneId, USAGE_MEDIA); 1762 builder.append(zoneMediaAddress); 1763 builder.append(index < audioZoneIds.length - 1 1764 ? MIRROR_COMMAND_DESTINATION_SEPARATOR : ""); 1765 } 1766 builder.append(MIRROR_COMMAND_SEPARATOR); 1767 1768 Slogf.i(TAG, "Sending mirror command to audio HAL: %s", builder); 1769 mAudioManagerWrapper.setParameters(builder.toString()); 1770 } 1771 1772 private String getAudioMirroringOffCommand(String mirrorSource) { 1773 return new StringBuilder().append(MIRROR_COMMAND_SOURCE).append(mirrorSource) 1774 .append(MIRROR_COMMAND_SEPARATOR).append(DISABLE_AUDIO_MIRRORING) 1775 .append(MIRROR_COMMAND_SEPARATOR).toString(); 1776 } 1777 1778 private String getOutputDeviceAddressForUsageInternal(int zoneId, int usage) { 1779 int contextForUsage = getCarAudioContext() 1780 .getContextForAudioAttribute(CarAudioContext.getAudioAttributeFromUsage(usage)); 1781 return getCarAudioZone(zoneId).getAddressForContext(contextForUsage); 1782 } 1783 1784 @GuardedBy("mImplLock") 1785 private void transientlyLoseFocusForMirrorLocked(int[] audioZoneIdsToAdd, 1786 TimingsTraceLog traceLog, List<AudioFocusStackRequest> mediaFocusStacks) { 1787 for (int index = 0; index < audioZoneIdsToAdd.length; index++) { 1788 int zoneId = audioZoneIdsToAdd[index]; 1789 traceLog.traceBegin("audio-mirror-focus-loss-zone-" + zoneId); 1790 mediaFocusStacks.add(new AudioFocusStackRequest(mFocusHandler 1791 .transientlyLoseAudioFocusForZone(zoneId), zoneId)); 1792 traceLog.traceEnd(); 1793 } 1794 } 1795 1796 private void handleDisableAudioMirrorForZonesInConfig(int[] audioZoneIds, long requestId) { 1797 AudioDeviceAttributes mirrorDevice = 1798 mCarAudioMirrorRequestHandler.getAudioDevice(requestId); 1799 if (mirrorDevice == null) { 1800 Slogf.e(TAG, "handleDisableAudioMirrorForZonesInConfig failed," 1801 + " audio mirror not allowed as there are no more mirror devices available"); 1802 mCarAudioMirrorRequestHandler.rejectMirrorForZones(requestId, audioZoneIds); 1803 return; 1804 } 1805 1806 int[] oldConfigs = mCarAudioMirrorRequestHandler.getMirrorAudioZonesForRequest(requestId); 1807 if (oldConfigs == null) { 1808 Slogf.w(TAG, "Could not disable audio mirror for zones %s," 1809 + " %d request id was no longer mirroring", 1810 Arrays.toString(audioZoneIds), requestId); 1811 return; 1812 } 1813 for (int index = 0; index < audioZoneIds.length; index++) { 1814 int zoneId = audioZoneIds[index]; 1815 1816 if (!mCarAudioMirrorRequestHandler.isMirrorEnabledForZone(zoneId)) { 1817 Slogf.w(TAG, "Could not disable audio mirror for zone %d," 1818 + " zone was no longer mirroring", 1819 zoneId); 1820 return; 1821 } 1822 1823 long currentRequestId = mCarAudioMirrorRequestHandler.getRequestIdForAudioZone(zoneId); 1824 1825 // The configuration to remove must be the same for the zones 1826 if (currentRequestId != requestId) { 1827 Slogf.w(TAG, "Could not disable audio mirror for zone %d," 1828 + " found non matching configuration", 1829 zoneId); 1830 return; 1831 } 1832 } 1833 1834 int[] newConfig = mCarAudioMirrorRequestHandler 1835 .calculateAudioConfigurationAfterRemovingZonesFromRequestId(requestId, audioZoneIds 1836 ); 1837 1838 if (newConfig == null) { 1839 Slogf.w(TAG, " handleDisableAudioMirrorForZone could not disable audio " 1840 + "mirror for zones %s, configuration not found", 1841 Arrays.toString(audioZoneIds)); 1842 return; 1843 } 1844 1845 // If there are less than two zones mirroring, remove all the zones 1846 if (newConfig.length < 2) { 1847 newConfig = EMPTY_INT_ARRAY; 1848 } 1849 1850 modifyAudioMirrorForZones(oldConfigs, newConfig); 1851 1852 // If there are no more zones mirroring then turn it off at HAL 1853 if (newConfig.length == 0) { 1854 Slogf.i(TAG, "Sending mirror off command to audio HAL for address %s", 1855 mirrorDevice.getAddress()); 1856 mAudioManagerWrapper.setParameters( 1857 getAudioMirroringOffCommand(mirrorDevice.getAddress())); 1858 } 1859 1860 //Send the signal to current listeners at the end 1861 mCarAudioMirrorRequestHandler.updateRemoveMirrorConfigurationForZones(requestId, newConfig); 1862 } 1863 1864 private void modifyAudioMirrorForZones(int[] audioZoneIds, int[] newConfig) { 1865 TimingsTraceLog t = new TimingsTraceLog(TAG, TraceHelper.TRACE_TAG_CAR_SERVICE); 1866 ArraySet<Integer> newConfigSet = CarServiceUtils.toIntArraySet(newConfig); 1867 int focusZoneId = audioZoneIds[0]; 1868 List<AudioFocusStackRequest> mediaFocusStacks = new ArrayList<>(); 1869 ArrayList<Integer> zonesToUndoRouting = new ArrayList<>(audioZoneIds.length 1870 - newConfig.length); 1871 t.traceBegin("audio-remove-mirror-" + Arrays.toString(audioZoneIds)); 1872 synchronized (mImplLock) { 1873 t.traceBegin("audio-remove-mirror-focus-loss-" + Arrays.toString(audioZoneIds)); 1874 for (int index = 0; index < audioZoneIds.length; index++) { 1875 int zoneId = audioZoneIds[index]; 1876 int newFocusZoneId = newConfig.length > 0 ? newConfig[0] : zoneId; 1877 // Focus for zones not in the new config remove focus and routing 1878 if (!newConfigSet.contains(zoneId)) { 1879 newFocusZoneId = zoneId; 1880 zonesToUndoRouting.add(zoneId); 1881 } 1882 t.traceBegin("audio-remove-mirror-focus-loss-zone-" + zoneId); 1883 mediaFocusStacks.add(new AudioFocusStackRequest(mFocusHandler 1884 .transientlyLoseAudioFocusForZone(focusZoneId), 1885 newFocusZoneId)); 1886 t.traceEnd(); 1887 } 1888 t.traceEnd(); 1889 1890 t.traceBegin("audio-remove-mirror-routing-" + zonesToUndoRouting); 1891 setupAudioRoutingForUsersZoneLocked(zonesToUndoRouting); 1892 t.traceEnd(); 1893 1894 t.traceBegin("audio-remove-mirror-focus-gain-" + Arrays.toString(audioZoneIds)); 1895 for (int index = 0; index < mediaFocusStacks.size(); index++) { 1896 AudioFocusStackRequest request = mediaFocusStacks.get(index); 1897 t.traceBegin("audio-remove-mirror-focus-gain-" + index + "-zone-" 1898 + request.mOriginalZoneId); 1899 mFocusHandler.regainMediaAudioFocusInZone(request.mStack, request.mOriginalZoneId); 1900 t.traceEnd(); 1901 } 1902 t.traceEnd(); 1903 } 1904 t.traceEnd(); 1905 } 1906 1907 @GuardedBy("mImplLock") 1908 private void setupAudioRoutingForUsersZoneLocked(ArrayList<Integer> audioZoneIds) { 1909 for (int index = 0; index < audioZoneIds.size(); index++) { 1910 int zoneId = audioZoneIds.get(index); 1911 int userId = getUserIdForZone(zoneId); 1912 if (userId == UserManagerHelper.USER_NULL) { 1913 continue; 1914 } 1915 CarAudioZone audioZone = getCarAudioZone(zoneId); 1916 setUserIdDeviceAffinitiesLocked(audioZone, userId, zoneId); 1917 } 1918 } 1919 1920 @GuardedBy("mImplLock") 1921 private boolean setupAudioRoutingForUserInMirrorDeviceLocked(int[] audioZones, 1922 AudioDeviceAttributes mirrorDevice) { 1923 int index; 1924 boolean succeeded = true; 1925 for (index = 0; index < audioZones.length; index++) { 1926 int zoneId = audioZones[index]; 1927 int userId = getUserIdForZone(zoneId); 1928 CarAudioZone audioZone = getCarAudioZone(zoneId); 1929 boolean enabled = setupMirrorDeviceForUserIdLocked(userId, audioZone, mirrorDevice); 1930 if (!enabled) { 1931 succeeded = false; 1932 Slogf.w(TAG, "setupAudioRoutingForUserInMirrorDeviceLocked failed for zone " 1933 + "id %d and user id %d", zoneId, userId); 1934 break; 1935 } 1936 } 1937 1938 if (succeeded) { 1939 return true; 1940 } 1941 1942 // Attempt to reset user id routing for other mirror zones 1943 for (int count = 0; count < index; count++) { 1944 int zoneId = audioZones[count]; 1945 int userId = getUserIdForZone(zoneId); 1946 CarAudioZone audioZone = getCarAudioZone(zoneId); 1947 setUserIdDeviceAffinitiesLocked(audioZone, userId, zoneId); 1948 } 1949 1950 return false; 1951 } 1952 1953 private void setupLegacyVolumeChangedListener() { 1954 AudioManagerHelper.registerVolumeAndMuteReceiver(mContext, mLegacyVolumeChangedHelper); 1955 } 1956 1957 private AudioDeviceInfo[] getAllInputDevices() { 1958 return mAudioManagerWrapper.getDevices( 1959 AudioManager.GET_DEVICES_INPUTS); 1960 } 1961 1962 @GuardedBy("mImplLock") 1963 private SparseArray<CarAudioZone> loadCarAudioConfigurationLocked( 1964 List<CarAudioDeviceInfo> carAudioDeviceInfos, AudioDeviceInfo[] inputDevices) { 1965 1966 try (InputStream fileStream = new FileInputStream(mCarAudioConfigurationPath); 1967 InputStream inputStream = new BufferedInputStream(fileStream)) { 1968 CarAudioZonesHelperImpl zonesHelper = new CarAudioZonesHelperImpl(mAudioManagerWrapper, 1969 mCarAudioSettings, inputStream, carAudioDeviceInfos, inputDevices, 1970 mServiceEventLogger, mUseCarVolumeGroupMuting, mUseCoreAudioVolume, 1971 mUseCoreAudioRouting, mUseFadeManagerConfiguration, 1972 mCarAudioFadeConfigurationHelper); 1973 SparseArray<CarAudioZone> zones = zonesHelper.loadAudioZones(); 1974 setupAudioDeviceConfigurationLocked(zonesHelper); 1975 return zones; 1976 } catch (IOException | XmlPullParserException e) { 1977 throw new RuntimeException("Failed to parse audio zone configuration", e); 1978 } 1979 } 1980 1981 1982 private SparseIntArray getValidAudioZoneIdToOccupantZoneId( 1983 SparseIntArray carAudioZoneIdToOccupantZoneIdMapping) { 1984 if (!Flags.audioVendorFreezeImprovements()) { 1985 return carAudioZoneIdToOccupantZoneIdMapping; 1986 } 1987 1988 SparseArray<CarOccupantZoneManager.OccupantZoneInfo> occupants = 1989 getCarOccupantZoneService().getOccupantsConfig(); 1990 1991 SparseIntArray validAudioZoneIdToOccupantZoneId = new SparseIntArray(); 1992 for (int index = 0; index < carAudioZoneIdToOccupantZoneIdMapping.size(); index++) { 1993 int occupantZoneId = carAudioZoneIdToOccupantZoneIdMapping.valueAt(index); 1994 int audioZoneId = carAudioZoneIdToOccupantZoneIdMapping.keyAt(index); 1995 if (!occupants.contains(occupantZoneId)) { 1996 mServiceEventLogger.log("Occupant zone id " + occupantZoneId 1997 + " is assigned to audio zone id " + audioZoneId 1998 + " but the occupant zone id does not exist," 1999 + " skipping audio zone configuration."); 2000 continue; 2001 } 2002 validAudioZoneIdToOccupantZoneId.put(audioZoneId, occupantZoneId); 2003 } 2004 return validAudioZoneIdToOccupantZoneId; 2005 } 2006 2007 @GuardedBy("mImplLock") 2008 private void updateConfigValueFromZoneHelperLocked(CarAudioZonesHelper zonesHelper) { 2009 mCarAudioContext = zonesHelper.getCarAudioContext(); 2010 mUseCoreAudioRouting = zonesHelper.useCoreAudioRouting(); 2011 mUseCoreAudioVolume = zonesHelper.useCoreAudioVolume(); 2012 mUseCarVolumeGroupMuting = zonesHelper.useVolumeGroupMuting(); 2013 mUseHalDuckingSignals = zonesHelper.useHalDuckingSignalOrDefault(mUseHalDuckingSignals); 2014 } 2015 2016 @GuardedBy("mImplLock") 2017 private CarAudioFadeConfigurationHelper loadCarAudioFadeConfigurationLocked() { 2018 if (mCarAudioFadeConfigurationPath == null) { 2019 String message = "Car audio fade configuration xml file expected, but not found at: " 2020 + FADE_CONFIGURATION_PATH; 2021 Slogf.w(TAG, message); 2022 mServiceEventLogger.log(message); 2023 return null; 2024 } 2025 try (InputStream fileStream = new FileInputStream(mCarAudioFadeConfigurationPath); 2026 InputStream inputStream = new BufferedInputStream(fileStream)) { 2027 return new CarAudioFadeConfigurationHelper(inputStream); 2028 } catch (IOException | XmlPullParserException e) { 2029 throw new RuntimeException("Failed to parse audio fade configuration", e); 2030 } 2031 } 2032 2033 @GuardedBy("mImplLock") 2034 @ExcludeFromCodeCoverageGeneratedReport(reason = DEPRECATED_CODE) 2035 private SparseArray<CarAudioZone> loadVolumeGroupConfigurationWithAudioControlLocked( 2036 List<CarAudioDeviceInfo> carAudioDeviceInfos, AudioDeviceInfo[] inputDevices) { 2037 AudioControlWrapper audioControlWrapper = getAudioControlWrapperLocked(); 2038 if (!(audioControlWrapper instanceof AudioControlWrapperV1)) { 2039 throw new IllegalStateException( 2040 "Updated version of IAudioControl no longer supports CarAudioZonesHelperLegacy." 2041 + " Please provide car_audio_configuration.xml."); 2042 } 2043 mCarAudioContext = new CarAudioContext(CarAudioContext.getAllContextsInfo(), 2044 mUseCoreAudioVolume); 2045 CarAudioZonesHelperLegacy legacyHelper = new CarAudioZonesHelperLegacy(mContext, 2046 mCarAudioContext, R.xml.car_volume_groups, carAudioDeviceInfos, 2047 (AudioControlWrapperV1) audioControlWrapper, 2048 mCarAudioSettings, inputDevices); 2049 return legacyHelper.loadAudioZones(); 2050 } 2051 2052 // Required to be called before setting up audio routing, volume management, focus management 2053 @GuardedBy("mImplLock") 2054 private void loadAndInitCarAudioZonesLocked() { 2055 if (mUseFadeManagerConfiguration) { 2056 mCarAudioFadeConfigurationHelper = loadCarAudioFadeConfigurationLocked(); 2057 } 2058 2059 List<CarAudioDeviceInfo> carAudioDeviceInfos = 2060 generateCarAudioDeviceInfos(mAudioManagerWrapper); 2061 AudioDeviceInfo[] inputDevices = getAllInputDevices(); 2062 2063 mCarAudioZones = loadAudioZonesUsingAudioControlLocked(); 2064 2065 if (mCarAudioZones == null && mCarAudioConfigurationPath != null) { 2066 mCarAudioZones = loadCarAudioConfigurationLocked(carAudioDeviceInfos, inputDevices); 2067 } else if (mCarAudioZones == null) { 2068 mCarAudioZones = loadVolumeGroupConfigurationWithAudioControlLocked(carAudioDeviceInfos, 2069 inputDevices); 2070 } 2071 2072 CarAudioZonesValidator.validate(mCarAudioZones, mUseCoreAudioRouting); 2073 2074 for (int i = 0; i < mCarAudioZones.size(); i++) { 2075 CarAudioZone zone = mCarAudioZones.valueAt(i); 2076 // Ensure HAL gets our initial value 2077 zone.init(); 2078 Slogf.v(TAG, "Processed audio zone: %s", zone); 2079 } 2080 } 2081 2082 @GuardedBy("mImplLock") 2083 @Nullable 2084 private SparseArray<CarAudioZone> loadAudioZonesUsingAudioControlLocked() { 2085 mCarAudioControlHalConfig = false; 2086 if (!Flags.audioControlHalConfiguration()) { 2087 return null; 2088 } 2089 var audioControl = getAudioControlWrapperLocked(); 2090 if (!audioControl.supportsFeature(AUDIOCONTROL_FEATURE_AUDIO_CONFIGURATION)) { 2091 return null; 2092 } 2093 TimingsTraceLog log = new TimingsTraceLog(TAG, TraceHelper.TRACE_TAG_CAR_SERVICE); 2094 log.traceBegin("Load audio control HAL"); 2095 var audioControlZonesHelper = new CarAudioZonesHelperAudioControlHAL(mAudioControlWrapper, 2096 mAudioManagerWrapper, mCarAudioSettings, mServiceEventLogger, 2097 mUseFadeManagerConfiguration); 2098 try { 2099 var audioZones = audioControlZonesHelper.loadAudioZones(); 2100 if (audioZones.size() == 0) { 2101 log.traceEnd(); 2102 return null; 2103 } 2104 setupAudioDeviceConfigurationLocked(audioControlZonesHelper); 2105 mCarAudioControlHalConfig = true; 2106 log.traceEnd(); 2107 return audioZones; 2108 } catch (Exception exception) { 2109 log.traceEnd(); 2110 String message = "Failed to configure car audio service using audio control HAL"; 2111 Slogf.e(TAG, message); 2112 mServiceEventLogger.log(message); 2113 } 2114 return null; 2115 } 2116 2117 @GuardedBy("mImplLock") 2118 private void setupAudioDeviceConfigurationLocked(CarAudioZonesHelper audioZonesHelper) { 2119 mAudioZoneIdToOccupantZoneIdMapping = getValidAudioZoneIdToOccupantZoneId( 2120 audioZonesHelper.getCarAudioZoneIdToOccupantZoneIdMapping()); 2121 mCarAudioMirrorRequestHandler.setMirrorDeviceInfos(audioZonesHelper.getMirrorDeviceInfos()); 2122 updateConfigValueFromZoneHelperLocked(audioZonesHelper); 2123 } 2124 2125 @GuardedBy("mImplLock") 2126 private void setupCarAudioPlaybackMonitorLocked() { 2127 if (!mUseMinMaxActivationVolume) { 2128 return; 2129 } 2130 int telephonyDefaultDataSubscriptionId = SubscriptionManager 2131 .getDefaultDataSubscriptionId(); 2132 mCarAudioPlaybackMonitor = new CarAudioPlaybackMonitor(this, mCarAudioZones, 2133 mTelephonyManager.createForSubscriptionId(telephonyDefaultDataSubscriptionId)); 2134 } 2135 2136 @GuardedBy("mImplLock") 2137 private void setupControlAndRoutingAudioPoliciesLocked() { 2138 setupVolumeControlAudioPolicyLocked(); 2139 setupFocusControlAudioPolicyLocked(); 2140 mRoutingAudioPolicy = setupRoutingAudioPolicyLocked(); 2141 setupOccupantZoneInfoLocked(); 2142 setupCoreAudioVolumeCallback(); 2143 } 2144 2145 @GuardedBy("mImplLock") 2146 private void setupAudioControlDuckingAndVolumeControlLocked() { 2147 AudioControlWrapper audioControlWrapper = getAudioControlWrapperLocked(); 2148 if (mUseHalDuckingSignals) { 2149 if (audioControlWrapper.supportsFeature(AUDIOCONTROL_FEATURE_AUDIO_DUCKING)) { 2150 mCarDucking = new CarDucking(mCarAudioZones, audioControlWrapper); 2151 } 2152 } 2153 2154 if (mUseCarVolumeGroupMuting) { 2155 mCarVolumeGroupMuting = new CarVolumeGroupMuting(mCarAudioZones, audioControlWrapper); 2156 } 2157 } 2158 2159 @GuardedBy("mImplLock") 2160 private void setupCoreAudioVolumeCallback() { 2161 if (!mUseCoreAudioVolume) { 2162 Slogf.i(TAG, "Not using core volume, core volume callback not setup"); 2163 return; 2164 } 2165 var executor = new HandlerExecutor(mHandler); 2166 mCoreAudioVolumeGroupCallback = new CoreAudioVolumeGroupCallback( 2167 new CarVolumeInfoWrapper(this), mAudioManagerWrapper, executor); 2168 mCoreAudioVolumeGroupCallback.init(); 2169 } 2170 2171 /** 2172 * @throws IllegalStateException if audio routing policy registration failed. 2173 */ 2174 @GuardedBy("mImplLock") 2175 private AudioPolicy setupRoutingAudioPolicyLocked() { 2176 if (!mUseDynamicRouting) { 2177 Slogf.i(TAG, "Not using dynamic audio routing, routing audio policy not setup"); 2178 return null; 2179 } 2180 TimingsTraceLog log = new TimingsTraceLog(TAG, TraceHelper.TRACE_TAG_CAR_SERVICE); 2181 log.traceBegin("routing-policy"); 2182 AudioPolicy.Builder builder = new AudioPolicy.Builder(mContext); 2183 builder.setLooper(mHandlerThread.getLooper()); 2184 2185 // Mirror policy has to be set before general audio policy 2186 log.traceBegin("routing-policy-setup"); 2187 setupMirrorDevicePolicyLocked(builder); 2188 CarAudioDynamicRouting.setupAudioDynamicRouting(mCarAudioContext, mAudioManagerWrapper, 2189 builder, mCarAudioZones); 2190 log.traceEnd(); 2191 2192 AudioPolicy routingAudioPolicy = builder.build(); 2193 log.traceBegin("routing-policy-register"); 2194 int r = mAudioManagerWrapper.registerAudioPolicy(routingAudioPolicy); 2195 log.traceEnd(); 2196 2197 log.traceEnd(); 2198 if (r != AudioManager.SUCCESS) { 2199 throw new IllegalStateException("Audio routing policy registration, error: " + r); 2200 } 2201 return routingAudioPolicy; 2202 } 2203 2204 /** 2205 * @throws IllegalStateException if volume control audio policy registration failed. 2206 */ 2207 @GuardedBy("mImplLock") 2208 private void setupVolumeControlAudioPolicyLocked() { 2209 mCarVolume = new CarVolume(mCarAudioContext, mClock, 2210 mAudioVolumeAdjustmentContextsVersion, mKeyEventTimeoutMs); 2211 2212 AudioPolicy.Builder volumeControlPolicyBuilder = new AudioPolicy.Builder(mContext); 2213 volumeControlPolicyBuilder.setLooper(mHandlerThread.getLooper()); 2214 2215 AudioPolicyVolumeCallbackInternal volumeCallbackInternal = 2216 new AudioPolicyVolumeCallbackInternal() { 2217 @Override 2218 public void onMuteChange(boolean mute, int zoneId, int groupId, int flags) { 2219 if (useCarVolumeGroupMuting()) { 2220 setVolumeGroupMute(zoneId, groupId, mute, flags); 2221 return; 2222 } 2223 setMasterMute(mute, flags); 2224 } 2225 2226 @Override 2227 public void onGroupVolumeChange(int zoneId, int groupId, int volumeValue, 2228 int flags) { 2229 setGroupVolume(zoneId, groupId, volumeValue, flags); 2230 } 2231 }; 2232 2233 mCarAudioPolicyVolumeCallback = new CarAudioPolicyVolumeCallback(volumeCallbackInternal, 2234 mAudioManagerWrapper, new CarVolumeInfoWrapper(this), mUseCarVolumeGroupMuting); 2235 // Attach the {@link AudioPolicyVolumeCallback} 2236 CarAudioPolicyVolumeCallback.addVolumeCallbackToPolicy(volumeControlPolicyBuilder, 2237 mCarAudioPolicyVolumeCallback); 2238 2239 mVolumeControlAudioPolicy = volumeControlPolicyBuilder.build(); 2240 2241 int status = mAudioManagerWrapper.registerAudioPolicy(mVolumeControlAudioPolicy); 2242 if (status != AudioManager.SUCCESS) { 2243 throw new IllegalStateException("Could not register the car audio service's volume" 2244 + " control audio policy, error: " + status); 2245 } 2246 } 2247 2248 /** 2249 * @throws IllegalStateException if focus control audio policy registration failed. 2250 */ 2251 @GuardedBy("mImplLock") 2252 private void setupFocusControlAudioPolicyLocked() { 2253 // Used to configure our audio policy to handle focus events. 2254 // This gives us the ability to decide which audio focus requests to accept and bypasses 2255 // the framework ducking logic. 2256 mFocusHandler = CarZonesAudioFocus.createCarZonesAudioFocus(mAudioManagerWrapper, 2257 mContext.getPackageManager(), mCarAudioZones, mCarAudioSettings, mCarDucking, 2258 new CarVolumeInfoWrapper(this), getAudioFeaturesInfo()); 2259 2260 AudioPolicy.Builder focusControlPolicyBuilder = new AudioPolicy.Builder(mContext); 2261 focusControlPolicyBuilder.setLooper(mHandlerThread.getLooper()); 2262 2263 focusControlPolicyBuilder.setAudioPolicyFocusListener(mFocusHandler); 2264 focusControlPolicyBuilder.setIsAudioFocusPolicy(true); 2265 2266 mFocusControlAudioPolicy = focusControlPolicyBuilder.build(); 2267 mFocusHandler.setOwningPolicy(this, mFocusControlAudioPolicy); 2268 2269 int status = mAudioManagerWrapper.registerAudioPolicy(mFocusControlAudioPolicy); 2270 if (status != AudioManager.SUCCESS) { 2271 throw new IllegalStateException("Could not register the car audio service's focus" 2272 + " control audio policy, error: " + status); 2273 } 2274 } 2275 2276 private CarAudioFeaturesInfo getAudioFeaturesInfo() { 2277 if (!Flags.carAudioDynamicDevices()) { 2278 return null; 2279 } 2280 CarAudioFeaturesInfo.Builder builder = 2281 new CarAudioFeaturesInfo.Builder(CarAudioFeaturesInfo.AUDIO_FEATURE_NO_FEATURE); 2282 if (mUseIsolatedFocusForDynamicDevices) { 2283 builder.addAudioFeature(CarAudioFeaturesInfo.AUDIO_FEATURE_ISOLATED_DEVICE_FOCUS); 2284 } 2285 if (mUseFadeManagerConfiguration) { 2286 builder.addAudioFeature(CarAudioFeaturesInfo.AUDIO_FEATURE_FADE_MANAGER_CONFIGS); 2287 } 2288 2289 return builder.build(); 2290 } 2291 2292 /** 2293 * @throws IllegalStateException if fade config audio policy registration failed. 2294 */ 2295 @GuardedBy("mImplLock") 2296 private void setupFadeManagerConfigAudioPolicyLocked() { 2297 if (!mUseFadeManagerConfiguration) { 2298 return; 2299 } 2300 2301 mFadeManagerConfigAudioPolicy = new AudioPolicy.Builder(mContext) 2302 .setLooper(mHandlerThread.getLooper()).build(); 2303 int status = mAudioManagerWrapper.registerAudioPolicy(mFadeManagerConfigAudioPolicy); 2304 if (status != AudioManager.SUCCESS) { 2305 throw new IllegalStateException("Could not register the car audio service's fade" 2306 + " configuration audio policy, error: " + status); 2307 } 2308 updateFadeManagerConfigurationForPrimaryZoneLocked(); 2309 } 2310 2311 @GuardedBy("mImplLock") 2312 private void updateFadeManagerConfigurationForPrimaryZoneLocked() { 2313 CarAudioFadeConfiguration carAudioFadeConfiguration = mCarAudioZones.get(PRIMARY_AUDIO_ZONE) 2314 .getCurrentCarAudioZoneConfig().getDefaultCarAudioFadeConfiguration(); 2315 if (carAudioFadeConfiguration == null) { 2316 return; 2317 } 2318 // for primary zone, core framework handles the default fade config 2319 setAudioPolicyFadeManagerConfigurationLocked( 2320 carAudioFadeConfiguration.getFadeManagerConfiguration()); 2321 } 2322 2323 @GuardedBy("mImplLock") 2324 private void updateFadeManagerConfigurationLocked(boolean isPrimaryZone) { 2325 if (!mUseFadeManagerConfiguration || !isPrimaryZone) { 2326 return; 2327 } 2328 updateFadeManagerConfigurationForPrimaryZoneLocked(); 2329 } 2330 2331 @GuardedBy("mImplLock") 2332 private void setAudioPolicyFadeManagerConfigurationLocked( 2333 FadeManagerConfiguration fadeManagerConfiguration) { 2334 if (!mUseFadeManagerConfiguration || fadeManagerConfiguration == null 2335 || mFadeManagerConfigAudioPolicy == null) { 2336 String message = "Can not set fade manager configuration: feature flag enabled? " 2337 + mUseFadeManagerConfiguration 2338 + " audio policy for fade configs registered? " 2339 + (mFadeManagerConfigAudioPolicy != null) 2340 + " fade manager configuration: " + fadeManagerConfiguration; 2341 mServiceEventLogger.log(message); 2342 Slogf.e(TAG, message); 2343 return; 2344 } 2345 2346 TimingsTraceLog t = new TimingsTraceLog(TAG, TraceHelper.TRACE_TAG_CAR_SERVICE); 2347 t.traceBegin("set-fade-manager-configuration-for-focus-loss"); 2348 int status = mFadeManagerConfigAudioPolicy.setFadeManagerConfigurationForFocusLoss( 2349 fadeManagerConfiguration); 2350 t.traceEnd(); 2351 if (status != AudioManager.SUCCESS) { 2352 String message = "Failed setting audio policy fade manager configuration: " 2353 + fadeManagerConfiguration + " with error: " + status; 2354 mServiceEventLogger.log(message); 2355 Slogf.e(TAG, message); 2356 } 2357 } 2358 2359 @GuardedBy("mImplLock") 2360 private void setupMirrorDevicePolicyLocked(AudioPolicy.Builder mirrorPolicyBuilder) { 2361 if (!mCarAudioMirrorRequestHandler.isMirrorAudioEnabled()) { 2362 Slogf.w(TAG, "setupMirrorDevicePolicyLocked Audio mirroring is not enabled"); 2363 return; 2364 } 2365 2366 CarAudioDynamicRouting.setupAudioDynamicRoutingForMirrorDevice(mirrorPolicyBuilder, 2367 mCarAudioMirrorRequestHandler.getMirroringDeviceInfos(), mAudioManagerWrapper); 2368 } 2369 2370 @GuardedBy("mImplLock") 2371 private void setupAudioConfigurationCallbackLocked() { 2372 mCarAudioPlaybackCallback = new CarAudioPlaybackCallback(mCarAudioZones, 2373 mCarAudioPlaybackMonitor, mClock, mKeyEventTimeoutMs); 2374 mAudioManagerWrapper.registerAudioPlaybackCallback(mCarAudioPlaybackCallback, null); 2375 } 2376 2377 @GuardedBy("mImplLock") 2378 private void setupOccupantZoneInfoLocked() { 2379 CarOccupantZoneService occupantZoneService; 2380 SparseIntArray audioZoneIdToOccupantZoneMapping; 2381 audioZoneIdToOccupantZoneMapping = mAudioZoneIdToOccupantZoneIdMapping; 2382 occupantZoneService = getCarOccupantZoneService(); 2383 occupantZoneService.setAudioZoneIdsForOccupantZoneIds(audioZoneIdToOccupantZoneMapping); 2384 occupantZoneService.registerCallback(mOccupantZoneCallback); 2385 callOccupantConfigForSelfIfNeeded(occupantZoneService); 2386 } 2387 2388 private void callOccupantConfigForSelfIfNeeded(CarOccupantZoneService occupantZoneService) { 2389 int driverId = occupantZoneService.getDriverUserId(); 2390 boolean isSystemUser = UserHandle.SYSTEM.getIdentifier() == driverId; 2391 // If the current driver is the system, then we need to wait for the user to be started. 2392 // This will be triggered by the occupant zone service. 2393 if (isSystemUser) { 2394 return; 2395 } 2396 CarOccupantZoneManager.OccupantZoneInfo driverInfo = 2397 occupantZoneService.getOccupantZoneForUser(UserHandle.of(driverId)); 2398 // If the driver is not configured then need to wait for the driver to be configured. 2399 // This will be triggered by the occupant zone service. 2400 if (driverInfo == null) { 2401 return; 2402 } 2403 // Driver is already configured, need to handle the change given that we will not receive 2404 // the user change callback. This must be handled in separate thread to prevent blocking the 2405 // car service initialization. This may happen if audio server crash and car audio service 2406 // is re-initializing or if the car audio service took too long to initialized and user 2407 // driver occupant is already configured. 2408 mServiceEventLogger.log("User already initialized during car audio service init," 2409 + " handling occupant zone config internally"); 2410 mHandler.post(this::handleOccupantZoneUserChanged); 2411 } 2412 2413 @GuardedBy("mImplLock") 2414 private void setupHalAudioFocusListenerLocked() { 2415 AudioControlWrapper audioControlWrapper = getAudioControlWrapperLocked(); 2416 if (!audioControlWrapper.supportsFeature(AUDIOCONTROL_FEATURE_AUDIO_FOCUS)) { 2417 Slogf.d(TAG, "HalAudioFocus is not supported on this device"); 2418 return; 2419 } 2420 2421 mHalAudioFocus = new HalAudioFocus(mAudioManagerWrapper, mAudioControlWrapper, 2422 mCarAudioPlaybackMonitor, mCarAudioContext, getAudioZoneIds()); 2423 mHalAudioFocus.registerFocusListener(); 2424 } 2425 2426 @GuardedBy("mImplLock") 2427 private void setupHalAudioGainCallbackLocked() { 2428 AudioControlWrapper audioControlWrapper = getAudioControlWrapperLocked(); 2429 if (!audioControlWrapper.supportsFeature(AUDIOCONTROL_FEATURE_AUDIO_GAIN_CALLBACK)) { 2430 Slogf.d(CarLog.TAG_AUDIO, "HalAudioGainCallback is not supported on this device"); 2431 return; 2432 } 2433 synchronized (mImplLock) { 2434 mCarAudioGainMonitor = new CarAudioGainMonitor(mAudioControlWrapper, 2435 new CarVolumeInfoWrapper(this), mCarAudioZones); 2436 mCarAudioGainMonitor.registerAudioGainListener(mHalAudioGainCallback); 2437 } 2438 } 2439 2440 @GuardedBy("mImplLock") 2441 private void setupHalAudioModuleChangeCallbackLocked() { 2442 AudioControlWrapper audioControlWrapper = getAudioControlWrapperLocked(); 2443 if (!audioControlWrapper.supportsFeature(AUDIOCONTROL_FEATURE_AUDIO_MODULE_CALLBACK)) { 2444 Slogf.w(CarLog.TAG_AUDIO, "HalModuleChangeCallback is not supported on this device"); 2445 return; 2446 } 2447 mCarAudioModuleChangeMonitor = new CarAudioModuleChangeMonitor(mAudioControlWrapper, 2448 new CarVolumeInfoWrapper(this), mCarAudioZones); 2449 mCarAudioModuleChangeMonitor.setModuleChangeCallback(mHalAudioModuleChangeCallback); 2450 } 2451 2452 @GuardedBy("mImplLock") 2453 private void releaseHalAudioModuleChangeCallbackLocked() { 2454 if (mCarAudioModuleChangeMonitor == null) { 2455 return; 2456 } 2457 try { 2458 mCarAudioModuleChangeMonitor.clearModuleChangeCallback(); 2459 } catch (Exception e) { 2460 Slogf.w(TAG, "Failed to clear audio control wrapper module change callback", e); 2461 } 2462 mCarAudioModuleChangeMonitor = null; 2463 } 2464 2465 /** 2466 * Read from {@link #AUDIO_CONFIGURATION_PATHS} respectively. 2467 * @return File path of the first hit in {@link #AUDIO_CONFIGURATION_PATHS} 2468 */ 2469 @Nullable 2470 private static String getAudioConfigurationPath() { 2471 for (String path : AUDIO_CONFIGURATION_PATHS) { 2472 File configuration = new File(path); 2473 if (configuration.exists()) { 2474 return path; 2475 } 2476 } 2477 return null; 2478 } 2479 2480 @Nullable 2481 private static String getAudioFadeConfigurationPath() { 2482 File fadeConfiguration = new File(FADE_CONFIGURATION_PATH); 2483 if (fadeConfiguration.exists()) { 2484 return FADE_CONFIGURATION_PATH; 2485 } 2486 return null; 2487 } 2488 2489 @Override 2490 public void setFadeTowardFront(float value) { 2491 synchronized (mImplLock) { 2492 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 2493 requireValidFadeRange(value); 2494 getAudioControlWrapperLocked().setFadeTowardFront(value); 2495 } 2496 } 2497 2498 @Override 2499 public void setBalanceTowardRight(float value) { 2500 synchronized (mImplLock) { 2501 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 2502 requireValidBalanceRange(value); 2503 getAudioControlWrapperLocked().setBalanceTowardRight(value); 2504 } 2505 } 2506 2507 /** 2508 * @return Array of accumulated device addresses, empty array if we found nothing 2509 */ 2510 @Override 2511 public @NonNull String[] getExternalSources() { 2512 synchronized (mImplLock) { 2513 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 2514 List<String> sourceAddresses = new ArrayList<>(); 2515 2516 AudioDeviceInfo[] devices = 2517 mAudioManagerWrapper.getDevices(AudioManager.GET_DEVICES_INPUTS); 2518 if (devices.length == 0) { 2519 Slogf.w(TAG, "getExternalSources, no input devices found"); 2520 } 2521 2522 // Collect the list of non-microphone input ports 2523 for (AudioDeviceInfo info : devices) { 2524 switch (info.getType()) { 2525 // TODO: Can we trim this set down? Especially duplicates like FM vs FM_TUNER? 2526 case AudioDeviceInfo.TYPE_FM: 2527 case AudioDeviceInfo.TYPE_FM_TUNER: 2528 case AudioDeviceInfo.TYPE_TV_TUNER: 2529 case AudioDeviceInfo.TYPE_HDMI: 2530 case AudioDeviceInfo.TYPE_AUX_LINE: 2531 case AudioDeviceInfo.TYPE_LINE_ANALOG: 2532 case AudioDeviceInfo.TYPE_LINE_DIGITAL: 2533 case AudioDeviceInfo.TYPE_USB_ACCESSORY: 2534 case AudioDeviceInfo.TYPE_USB_DEVICE: 2535 case AudioDeviceInfo.TYPE_USB_HEADSET: 2536 case AudioDeviceInfo.TYPE_IP: 2537 case AudioDeviceInfo.TYPE_BUS: 2538 String address = info.getAddress(); 2539 if (TextUtils.isEmpty(address)) { 2540 Slogf.w(TAG, "Discarded device with empty address, type=%d", 2541 info.getType()); 2542 } else { 2543 sourceAddresses.add(address); 2544 } 2545 break; 2546 default: 2547 Slogf.w(TAG, "Unsupported input devices, type=%d", info.getType()); 2548 break; 2549 } 2550 } 2551 2552 return sourceAddresses.toArray(new String[0]); 2553 } 2554 } 2555 2556 @Override 2557 public CarAudioPatchHandle createAudioPatch(String sourceAddress, 2558 @AttributeUsage int usage, int gainInMillibels) { 2559 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 2560 enforceCanUseAudioPatchAPI(); 2561 synchronized (mImplLock) { 2562 return createAudioPatchLocked(sourceAddress, usage, gainInMillibels); 2563 } 2564 } 2565 2566 @Override 2567 public void releaseAudioPatch(CarAudioPatchHandle carPatch) { 2568 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 2569 enforceCanUseAudioPatchAPI(); 2570 synchronized (mImplLock) { 2571 releaseAudioPatchLocked(carPatch); 2572 } 2573 } 2574 2575 private void enforceCanUseAudioPatchAPI() { 2576 if (!areAudioPatchAPIsEnabled()) { 2577 throw new IllegalStateException("Audio Patch APIs not enabled, see " 2578 + PROPERTY_RO_ENABLE_AUDIO_PATCH); 2579 } 2580 } 2581 2582 private boolean areAudioPatchAPIsEnabled() { 2583 return SystemProperties.getBoolean(PROPERTY_RO_ENABLE_AUDIO_PATCH, /* default= */ false); 2584 } 2585 2586 @GuardedBy("mImplLock") 2587 private CarAudioPatchHandle createAudioPatchLocked(String sourceAddress, 2588 @AttributeUsage int usage, int gainInMillibels) { 2589 // Find the named source port 2590 AudioDeviceInfo sourcePortInfo = null; 2591 AudioDeviceInfo[] deviceInfos = 2592 mAudioManagerWrapper.getDevices(AudioManager.GET_DEVICES_INPUTS); 2593 for (AudioDeviceInfo info : deviceInfos) { 2594 if (sourceAddress.equals(info.getAddress())) { 2595 // This is the one for which we're looking 2596 sourcePortInfo = info; 2597 break; 2598 } 2599 } 2600 Objects.requireNonNull(sourcePortInfo, 2601 "Specified source is not available: " + sourceAddress); 2602 2603 AudioAttributes audioAttributes = CarAudioContext.getAudioAttributeFromUsage(usage); 2604 2605 AudioPatchInfo audioPatchInfo = AudioManagerHelper.createAudioPatch(sourcePortInfo, 2606 getOutputDeviceForAudioAttributeLocked(PRIMARY_AUDIO_ZONE, audioAttributes), 2607 gainInMillibels); 2608 2609 Slogf.d(TAG, "Audio patch created: %s", audioPatchInfo); 2610 2611 // Ensure the initial volume on output device port 2612 int groupId = getVolumeGroupIdForAudioAttributeLocked(PRIMARY_AUDIO_ZONE, audioAttributes); 2613 setGroupVolume(PRIMARY_AUDIO_ZONE, groupId, 2614 getGroupVolume(PRIMARY_AUDIO_ZONE, groupId), 0); 2615 2616 return new CarAudioPatchHandle(audioPatchInfo.getHandleId(), 2617 audioPatchInfo.getSourceAddress(), audioPatchInfo.getSinkAddress()); 2618 } 2619 2620 @GuardedBy("mImplLock") 2621 private void releaseAudioPatchLocked(CarAudioPatchHandle carPatch) { 2622 Objects.requireNonNull(carPatch); 2623 2624 if (mAudioManagerWrapper.releaseAudioPatch(getAudioPatchInfo(carPatch))) { 2625 Slogf.d(TAG, "releaseAudioPatch %s successfully", carPatch); 2626 } 2627 // If we didn't find a match, then something went awry, but it's probably not fatal... 2628 Slogf.e(TAG, "releaseAudioPatch found no match for %s", carPatch); 2629 } 2630 2631 private static AudioPatchInfo getAudioPatchInfo(CarAudioPatchHandle carPatch) { 2632 return new AudioPatchInfo(carPatch.getSourceAddress(), 2633 carPatch.getSinkAddress(), 2634 carPatch.getHandleId()); 2635 } 2636 2637 @Override 2638 public int getVolumeGroupCount(int zoneId) { 2639 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 2640 2641 if (runInLegacyMode()) { 2642 return CarAudioDynamicRouting.STREAM_TYPES.length; 2643 } 2644 2645 synchronized (mImplLock) { 2646 return getCarAudioZoneLocked(zoneId).getCurrentVolumeGroupCount(); 2647 } 2648 } 2649 2650 @Override 2651 public int getVolumeGroupIdForUsage(int zoneId, @AttributeUsage int usage) { 2652 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 2653 if (!CarAudioContext.isValidAudioAttributeUsage(usage)) { 2654 return INVALID_VOLUME_GROUP_ID; 2655 } 2656 2657 synchronized (mImplLock) { 2658 return getVolumeGroupIdForAudioAttributeLocked(zoneId, 2659 CarAudioContext.getAudioAttributeFromUsage(usage)); 2660 } 2661 } 2662 2663 @Override 2664 public CarVolumeGroupInfo getVolumeGroupInfo(int zoneId, int groupId) { 2665 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 2666 if (runInLegacyMode()) { 2667 return getVolumeGroupInfoForLegacyMode(zoneId, groupId); 2668 } 2669 synchronized (mImplLock) { 2670 return getCarVolumeGroupLocked(zoneId, groupId).getCarVolumeGroupInfo(); 2671 } 2672 } 2673 2674 @Override 2675 public List<CarVolumeGroupInfo> getVolumeGroupInfosForZone(int zoneId) { 2676 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 2677 if (runInLegacyMode()) { 2678 List<CarVolumeGroupInfo> infos = new ArrayList<CarVolumeGroupInfo>(); 2679 for (int groupId = 0; groupId < getVolumeGroupCount(zoneId); groupId++) { 2680 infos.add(getVolumeGroupInfoForLegacyMode(zoneId, groupId)); 2681 } 2682 return infos; 2683 } 2684 synchronized (mImplLock) { 2685 return getVolumeGroupInfosForZoneLocked(zoneId); 2686 } 2687 } 2688 2689 @Override 2690 public List<AudioAttributes> getAudioAttributesForVolumeGroup(CarVolumeGroupInfo groupInfo) { 2691 Objects.requireNonNull(groupInfo, "Car volume group info can not be null"); 2692 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 2693 if (runInLegacyMode()) { 2694 return getVolumeGroupInfoForLegacyMode(groupInfo.getZoneId(), groupInfo.getId()) 2695 .getAudioAttributes(); 2696 } 2697 2698 synchronized (mImplLock) { 2699 return getCarAudioZoneLocked(groupInfo.getZoneId()) 2700 .getCurrentVolumeGroup(groupInfo.getId()).getAudioAttributes(); 2701 } 2702 } 2703 2704 private CarVolumeGroupInfo getVolumeGroupInfoForLegacyMode(int zoneId, int groupId) { 2705 int maxIndex = getGroupMaxVolume(zoneId, groupId); 2706 int minIndex = getGroupMinVolume(zoneId, groupId); 2707 AudioAttributes audioAttributes = CarAudioContext.getAudioAttributeFromUsage( 2708 CarAudioDynamicRouting.STREAM_TYPE_USAGES[groupId]); 2709 2710 List<AudioDeviceAttributes> deviceAttributesList = mAudioManagerWrapper 2711 .getAudioDevicesForAttributes(audioAttributes) 2712 .stream() 2713 .map(AudioDeviceAttributes::new) 2714 .collect(Collectors.toList()); 2715 2716 return new CarVolumeGroupInfo.Builder("legacy_zone" + zoneId, zoneId, groupId) 2717 .setVolumeGainIndex(getGroupVolume(zoneId, groupId)) 2718 .setMaxVolumeGainIndex(maxIndex) 2719 .setMinVolumeGainIndex(minIndex) 2720 .setAudioAttributes(Arrays.asList(audioAttributes)) 2721 .setAudioDeviceAttributes(deviceAttributesList) 2722 .setMuted(mAudioManagerWrapper.isStreamMute( 2723 CarAudioDynamicRouting.STREAM_TYPES[groupId])) 2724 .setBlocked(false) 2725 .setAttenuated(false) 2726 .setMaxActivationVolumeGainIndex(maxIndex) 2727 .setMinActivationVolumeGainIndex(minIndex) 2728 .setMutedBySystem(false).build(); 2729 } 2730 2731 @GuardedBy("mImplLock") 2732 private int getVolumeGroupIdForAudioAttributeLocked(int zoneId, 2733 AudioAttributes audioAttributes) { 2734 if (runInLegacyMode()) { 2735 return getStreamTypeFromAudioAttribute(audioAttributes); 2736 } 2737 2738 @AudioContext int audioContext = 2739 mCarAudioContext.getContextForAudioAttribute(audioAttributes); 2740 return getVolumeGroupIdForAudioContextLocked(zoneId, audioContext); 2741 } 2742 2743 private static int getStreamTypeFromAudioAttribute(AudioAttributes audioAttributes) { 2744 int usage = audioAttributes.getSystemUsage(); 2745 for (int i = 0; i < CarAudioDynamicRouting.STREAM_TYPE_USAGES.length; i++) { 2746 if (usage == CarAudioDynamicRouting.STREAM_TYPE_USAGES[i]) { 2747 return i; 2748 } 2749 } 2750 2751 return INVALID_VOLUME_GROUP_ID; 2752 } 2753 2754 @GuardedBy("mImplLock") 2755 private int getVolumeGroupIdForAudioContextLocked(int zoneId, @AudioContext int audioContext) { 2756 CarVolumeGroup[] groups = getCarAudioZoneLocked(zoneId).getCurrentVolumeGroups(); 2757 for (int i = 0; i < groups.length; i++) { 2758 int[] groupAudioContexts = groups[i].getContexts(); 2759 for (int groupAudioContext : groupAudioContexts) { 2760 if (audioContext == groupAudioContext) { 2761 return i; 2762 } 2763 } 2764 } 2765 return INVALID_VOLUME_GROUP_ID; 2766 } 2767 2768 @Override 2769 public @NonNull int[] getUsagesForVolumeGroupId(int zoneId, int groupId) { 2770 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 2771 2772 if (runInLegacyMode()) { 2773 return new int[] { CarAudioDynamicRouting.STREAM_TYPE_USAGES[groupId] }; 2774 } 2775 synchronized (mImplLock) { 2776 CarVolumeGroup group = getCarVolumeGroupLocked(zoneId, groupId); 2777 int[] contexts = group.getContexts(); 2778 List<Integer> usages = new ArrayList<>(); 2779 for (int index = 0; index < contexts.length; index++) { 2780 AudioAttributes[] attributesForContext = 2781 mCarAudioContext.getAudioAttributesForContext(contexts[index]); 2782 for (int counter = 0; counter < attributesForContext.length; counter++) { 2783 usages.add(attributesForContext[counter].getSystemUsage()); 2784 } 2785 } 2786 2787 int[] usagesArray = CarServiceUtils.toIntArray(usages); 2788 2789 return usagesArray; 2790 } 2791 } 2792 2793 @Override 2794 public boolean isPlaybackOnVolumeGroupActive(int zoneId, int groupId) { 2795 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 2796 requireNonLegacyRouting(); 2797 Preconditions.checkArgument(isAudioZoneIdValid(zoneId), 2798 "Invalid audio zone id %d", zoneId); 2799 2800 CarVolume carVolume; 2801 synchronized (mImplLock) { 2802 carVolume = mCarVolume; 2803 } 2804 return carVolume.isAnyContextActive(getContextsForVolumeGroupId(zoneId, groupId), 2805 getActiveAttributesFromPlaybackConfigurations(zoneId), 2806 getCallStateForZone(zoneId), getActiveHalAudioAttributesForZone(zoneId)); 2807 } 2808 2809 /** 2810 * 2811 * returns the current call state ({@code CALL_STATE_OFFHOOK}, {@code CALL_STATE_RINGING}, 2812 * {@code CALL_STATE_IDLE}) from the telephony manager. 2813 */ 2814 int getCallStateForZone(int zoneId) { 2815 synchronized (mImplLock) { 2816 // Only driver can use telephony stack 2817 if (getUserIdForZoneLocked(zoneId) == getCarOccupantZoneService().getDriverUserId()) { 2818 return mTelephonyManager.getCallState(); 2819 } 2820 } 2821 return TelephonyManager.CALL_STATE_IDLE; 2822 } 2823 2824 private List<AudioAttributes> getActiveAttributesFromPlaybackConfigurations(int zoneId) { 2825 return getCarAudioZone(zoneId) 2826 .findActiveAudioAttributesFromPlaybackConfigurations(mAudioManagerWrapper 2827 .getActivePlaybackConfigurations()); 2828 } 2829 2830 private @NonNull @AudioContext int[] getContextsForVolumeGroupId(int zoneId, int groupId) { 2831 synchronized (mImplLock) { 2832 CarVolumeGroup group = getCarVolumeGroupLocked(zoneId, groupId); 2833 return group.getContexts(); 2834 } 2835 } 2836 2837 @GuardedBy("mImplLock") 2838 private List<CarVolumeGroupInfo> getVolumeGroupInfosForZoneLocked(int zoneId) { 2839 return getCarAudioZoneLocked(zoneId).getCurrentVolumeGroupInfos(); 2840 } 2841 2842 /** 2843 * Gets the ids of all available audio zones 2844 * 2845 * @return Array of available audio zones ids 2846 */ 2847 @Override 2848 public @NonNull int[] getAudioZoneIds() { 2849 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 2850 requireNonLegacyRouting(); 2851 synchronized (mImplLock) { 2852 int[] zoneIds = new int[mCarAudioZones.size()]; 2853 for (int i = 0; i < mCarAudioZones.size(); i++) { 2854 zoneIds[i] = mCarAudioZones.keyAt(i); 2855 } 2856 return zoneIds; 2857 } 2858 } 2859 2860 /** 2861 * Gets the audio zone id currently mapped to uid, 2862 * 2863 * <p><b>Note:</b> Will use uid mapping first, followed by uid's user id mapping. 2864 * defaults to PRIMARY_AUDIO_ZONE if no mapping exist 2865 * 2866 * @param uid The uid 2867 * @return zone id mapped to uid 2868 */ 2869 @Override 2870 public int getZoneIdForUid(int uid) { 2871 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 2872 requireNonLegacyRouting(); 2873 synchronized (mImplLock) { 2874 return getZoneIdForUidLocked(uid); 2875 } 2876 } 2877 2878 @GuardedBy("mImplLock") 2879 private int getZoneIdForUidLocked(int uid) { 2880 if (mUidToZoneMap.containsKey(uid)) { 2881 return mUidToZoneMap.get(uid); 2882 } 2883 2884 return getZoneIdForUserLocked(UserHandle.getUserHandleForUid(uid)); 2885 } 2886 2887 @GuardedBy("mImplLock") 2888 private int getZoneIdForUserLocked(UserHandle handle) { 2889 CarOccupantZoneService carOccupantZoneService = getCarOccupantZoneService(); 2890 CarOccupantZoneManager.OccupantZoneInfo info = 2891 carOccupantZoneService.getOccupantZoneForUser(handle); 2892 2893 int audioZoneId = CarAudioManager.INVALID_AUDIO_ZONE; 2894 if (info != null) { 2895 audioZoneId = carOccupantZoneService.getAudioZoneIdForOccupant(info.zoneId); 2896 } 2897 2898 return audioZoneId == CarAudioManager.INVALID_AUDIO_ZONE ? PRIMARY_AUDIO_ZONE : audioZoneId; 2899 } 2900 2901 /** 2902 * Maps the audio zone id to uid 2903 * 2904 * @param zoneId The audio zone id 2905 * @param uid The uid to map 2906 * 2907 * <p><b>Note:</b> Will throw if occupant zone mapping exist, as uid and occupant zone mapping 2908 * do not work in conjunction. 2909 * 2910 * @return true if the device affinities, for devices in zone, are successfully set 2911 */ 2912 @Override 2913 public boolean setZoneIdForUid(int zoneId, int uid) { 2914 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 2915 requireNonLegacyRouting(); 2916 Slogf.i(TAG, "setZoneIdForUid Calling uid %d mapped to : %d", uid, zoneId); 2917 synchronized (mImplLock) { 2918 checkAudioZoneIdLocked(zoneId); 2919 // If occupant mapping exist uid routing can not be used 2920 requiredOccupantZoneMappingDisabledLocked(); 2921 2922 // Figure out if anything is currently holding focus, 2923 // This will change the focus to transient loss while we are switching zones 2924 Integer currentZoneId = mUidToZoneMap.get(uid); 2925 ArrayList<AudioFocusInfo> currentFocusHoldersForUid = new ArrayList<>(); 2926 ArrayList<AudioFocusInfo> currentFocusLosersForUid = new ArrayList<>(); 2927 if (currentZoneId != null) { 2928 currentFocusHoldersForUid = mFocusHandler.getAudioFocusHoldersForUid(uid, 2929 currentZoneId.intValue()); 2930 currentFocusLosersForUid = mFocusHandler.getAudioFocusLosersForUid(uid, 2931 currentZoneId.intValue()); 2932 if (!currentFocusHoldersForUid.isEmpty() || !currentFocusLosersForUid.isEmpty()) { 2933 // Order matters here: Remove the focus losers first 2934 // then do the current holder to prevent loser from popping up while 2935 // the focus is being remove for current holders 2936 // Remove focus for current focus losers 2937 mFocusHandler.transientlyLoseInFocusInZone(currentFocusLosersForUid, 2938 currentZoneId.intValue()); 2939 // Remove focus for current holders 2940 mFocusHandler.transientlyLoseInFocusInZone(currentFocusHoldersForUid, 2941 currentZoneId.intValue()); 2942 } 2943 } 2944 2945 // if the current uid is in the list 2946 // remove it from the list 2947 2948 if (checkAndRemoveUidLocked(uid)) { 2949 if (setZoneIdForUidNoCheckLocked(zoneId, uid)) { 2950 // Order matters here: Regain focus for 2951 // Previously lost focus holders then regain 2952 // focus for holders that had it last 2953 // Regain focus for the focus losers from previous zone 2954 if (!currentFocusLosersForUid.isEmpty()) { 2955 regainAudioFocusLocked(currentFocusLosersForUid, zoneId); 2956 } 2957 // Regain focus for the focus holders from previous zone 2958 if (!currentFocusHoldersForUid.isEmpty()) { 2959 regainAudioFocusLocked(currentFocusHoldersForUid, zoneId); 2960 } 2961 return true; 2962 } 2963 } 2964 return false; 2965 } 2966 } 2967 2968 @GuardedBy("mImplLock") 2969 private boolean handleAssignAudioFromUserIdToPrimaryAudioZoneLocked( 2970 IBinder token, int userId, int zoneId, long requestId) { 2971 AudioFocusStack mediaFocusStack = 2972 mFocusHandler.transientlyLoseMediaAudioFocusForUser(userId, zoneId); 2973 2974 if (!shareAudioRoutingForUserInPrimaryAudioZoneLocked(userId, zoneId)) { 2975 Slogf.w(TAG, "Can not route user id %s to primary audio zone", userId); 2976 mFocusHandler.regainMediaAudioFocusInZone(mediaFocusStack, zoneId); 2977 return false; 2978 } 2979 2980 DeathRecipient deathRecipient = () -> handleAssignedAudioFromUserDeath(requestId); 2981 try { 2982 token.linkToDeath(deathRecipient, /* flags= */ 0); 2983 } catch (RemoteException e) { 2984 Slogf.e(TAG, e, "Can not route user id %d to primary audio zone, caller died", userId); 2985 mFocusHandler.regainMediaAudioFocusInZone(mediaFocusStack, zoneId); 2986 return false; 2987 } 2988 2989 mFocusHandler.regainMediaAudioFocusInZone(mediaFocusStack, PRIMARY_AUDIO_ZONE); 2990 mUserAssignedToPrimaryZoneToCallbackDeathRecipient.put(userId, deathRecipient); 2991 mMediaRequestHandler.acceptMediaAudioRequest(token, requestId); 2992 2993 Slogf.d(TAG, "Assigning user id %d from primary audio zone", userId); 2994 2995 return true; 2996 } 2997 2998 @GuardedBy("mImplLock") 2999 private boolean shareAudioRoutingForUserInPrimaryAudioZoneLocked(int userId, int zoneId) { 3000 CarAudioZone zone = mCarAudioZones.get(zoneId); 3001 return shareUserIdMediaInMainZoneLocked(userId, zone); 3002 } 3003 3004 @GuardedBy("mImplLock") 3005 private boolean shareUserIdMediaInMainZoneLocked(int userId, CarAudioZone audioZone) { 3006 List<AudioDeviceInfo> devices = getAudioDeviceInfos(audioZone); 3007 devices.add(getMediaDeviceForPrimaryZoneLocked()); 3008 3009 return setUserIdDeviceAffinityLocked(devices, userId, audioZone.getId()); 3010 } 3011 3012 private AudioDeviceInfo getAudioDeviceInfoOrThrowIfNotFound( 3013 AudioDeviceAttributes audioDeviceAttributes) { 3014 AudioDeviceInfo info = CarAudioUtils.getAudioDeviceInfo(audioDeviceAttributes, 3015 mAudioManagerWrapper); 3016 if (info != null) { 3017 return info; 3018 } 3019 throw new IllegalStateException("Output audio device address " 3020 + audioDeviceAttributes.getAddress() + " is not currently available"); 3021 } 3022 3023 @GuardedBy("mImplLock") 3024 private boolean setupMirrorDeviceForUserIdLocked(int userId, CarAudioZone audioZone, 3025 AudioDeviceAttributes mirrorDevice) { 3026 List<AudioDeviceAttributes> devices = audioZone.getCurrentAudioDevices(); 3027 devices.add(mirrorDevice); 3028 3029 Slogf.d(TAG, "setupMirrorDeviceForUserIdLocked for userId %d in zone %d", userId, 3030 audioZone.getId()); 3031 3032 return setUserIdDeviceAffinityLocked(getAudioDeviceInfosFromAttributes(devices), userId, 3033 audioZone.getId()); 3034 } 3035 3036 @GuardedBy("mImplLock") 3037 private boolean setUserIdDeviceAffinityLocked(List<AudioDeviceInfo> devices, 3038 int userId, int zoneId) { 3039 if (mIsAudioServerDown || mRoutingAudioPolicy == null) { 3040 return false; 3041 } 3042 boolean results = mRoutingAudioPolicy.setUserIdDeviceAffinity(userId, devices); 3043 if (!results) { 3044 Slogf.w(TAG, "setUserIdDeviceAffinityLocked for userId %d in zone %d Failed," 3045 + " could not set audio routing.", userId, zoneId); 3046 } 3047 return results; 3048 } 3049 3050 private void handleAssignedAudioFromUserDeath(long requestId) { 3051 Slogf.e(TAG, "IBinder for request %d died", requestId); 3052 handleUnassignAudioFromUserIdOnPrimaryAudioZone(requestId); 3053 } 3054 3055 private boolean handleUnassignAudioFromUserIdOnPrimaryAudioZone(long requestId) { 3056 CarOccupantZoneManager.OccupantZoneInfo info = 3057 mMediaRequestHandler.getOccupantForRequest(requestId); 3058 3059 if (info == null) { 3060 Slogf.w(TAG, "Occupant %s is not mapped to any audio zone", info); 3061 return false; 3062 } 3063 CarOccupantZoneService carOccupantZoneService = getCarOccupantZoneService(); 3064 int userId = carOccupantZoneService.getUserForOccupant(info.zoneId); 3065 int audioZoneId = carOccupantZoneService.getAudioZoneIdForOccupant(info.zoneId); 3066 3067 synchronized (mImplLock) { 3068 CarAudioZone audioZone = getCarAudioZoneLocked(audioZoneId); 3069 3070 AudioFocusStack mediaFocusStack = 3071 mFocusHandler.transientlyLoseMediaAudioFocusForUser(userId, PRIMARY_AUDIO_ZONE); 3072 3073 if (!resetUserIdMediaInMainZoneLocked(userId, audioZone)) { 3074 Slogf.w(TAG, "Can not remove route for user id %d to primary audio zone", userId); 3075 mFocusHandler.regainMediaAudioFocusInZone(mediaFocusStack, PRIMARY_AUDIO_ZONE); 3076 return false; 3077 } 3078 3079 mFocusHandler.regainMediaAudioFocusInZone(mediaFocusStack, audioZoneId); 3080 removeAssignedUserInfoLocked(userId); 3081 } 3082 3083 Slogf.d(TAG, "Unassigned user id %d from primary audio zone", userId); 3084 3085 return mMediaRequestHandler.stopMediaAudioOnPrimaryZone(requestId); 3086 } 3087 3088 @GuardedBy("mImplLock") 3089 private void removeAssignedUserInfoLocked(int userId) { 3090 mUserAssignedToPrimaryZoneToCallbackDeathRecipient.remove(userId); 3091 } 3092 3093 @GuardedBy("mImplLock") 3094 private boolean resetUserIdMediaInMainZoneLocked(int userId, CarAudioZone audioZone) { 3095 List<AudioDeviceInfo> devices = getAudioDeviceInfos(audioZone); 3096 return setUserIdDeviceAffinityLocked(devices, userId, audioZone.getId()); 3097 } 3098 3099 @GuardedBy("mImplLock") 3100 private AudioDeviceInfo getOutputDeviceForAudioAttributeLocked(int zoneId, 3101 AudioAttributes audioAttributes) { 3102 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 3103 requireNonLegacyRouting(); 3104 int contextForUsage = mCarAudioContext.getContextForAudioAttribute(audioAttributes); 3105 Preconditions.checkArgument(!CarAudioContext.isInvalidContextId(contextForUsage), 3106 "Invalid audio attribute usage %s", audioAttributes); 3107 return getAudioDeviceInfoOrThrowIfNotFound(getCarAudioZoneLocked(zoneId) 3108 .getAudioDeviceForContext(contextForUsage)); 3109 } 3110 3111 @Override 3112 public String getOutputDeviceAddressForUsage(int zoneId, @AttributeUsage int usage) { 3113 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 3114 requireNonLegacyRouting(); 3115 CarAudioContext.checkAudioAttributeUsage(usage); 3116 return getOutputDeviceAddressForUsageInternal(zoneId, usage); 3117 } 3118 3119 /** 3120 * Regain focus for the focus list passed in 3121 * @param afiList focus info list to regain 3122 * @param zoneId zone id where the focus holder belong 3123 */ 3124 @GuardedBy("mImplLock") 3125 void regainAudioFocusLocked(ArrayList<AudioFocusInfo> afiList, int zoneId) { 3126 for (AudioFocusInfo info : afiList) { 3127 if (mFocusHandler.reevaluateAndRegainAudioFocus(info) 3128 != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { 3129 Slogf.i(TAG, 3130 " Focus could not be granted for entry %s uid %d in zone %d", 3131 info.getClientId(), info.getClientUid(), zoneId); 3132 } 3133 } 3134 } 3135 3136 /** 3137 * Removes the current mapping of the uid, focus will be lost in zone 3138 * @param uid The uid to remove 3139 * 3140 * <p><b>Note:</b> Will throw if occupant zone mapping exist, as uid and occupant zone mapping 3141 * do not work in conjunction. 3142 * 3143 * return true if all the devices affinities currently 3144 * mapped to uid are successfully removed 3145 */ 3146 @Override 3147 public boolean clearZoneIdForUid(int uid) { 3148 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 3149 requireNonLegacyRouting(); 3150 synchronized (mImplLock) { 3151 // Throw so as to not set the wrong expectation, 3152 // that routing will be changed if clearZoneIdForUid is called. 3153 requiredOccupantZoneMappingDisabledLocked(); 3154 3155 return checkAndRemoveUidLocked(uid); 3156 } 3157 } 3158 3159 /** 3160 * Sets the zone id for uid 3161 * @param zoneId zone id to map to uid 3162 * @param uid uid to map 3163 * @return true if setting uid device affinity is successful 3164 */ 3165 @GuardedBy("mImplLock") 3166 private boolean setZoneIdForUidNoCheckLocked(int zoneId, int uid) { 3167 if (mIsAudioServerDown || mRoutingAudioPolicy == null) { 3168 Slogf.w(TAG, "setZoneIdForUidNoCheck Failed set device affinity" 3169 + " for uid %d in zone %d, routing policy not available.", 3170 uid, zoneId); 3171 return false; 3172 } 3173 Slogf.d(TAG, "setZoneIdForUidNoCheck Calling uid %d mapped to %d", uid, zoneId); 3174 //Request to add uid device affinity 3175 List<AudioDeviceInfo> deviceInfos = 3176 getAudioDeviceInfos(getCarAudioZoneLocked(zoneId)); 3177 if (mRoutingAudioPolicy.setUidDeviceAffinity(uid, deviceInfos)) { 3178 // TODO do not store uid mapping here instead use the uid 3179 // device affinity in audio policy when available 3180 mUidToZoneMap.put(uid, zoneId); 3181 return true; 3182 } 3183 Slogf.w(TAG, "setZoneIdForUidNoCheck Failed set device affinity for uid %d in zone %d", 3184 uid, zoneId); 3185 return false; 3186 } 3187 3188 /** 3189 * Check if uid is attached to a zone and remove it 3190 * @param uid unique id to remove 3191 * @return true if the uid was successfully removed or mapping was not assigned 3192 */ 3193 @GuardedBy("mImplLock") 3194 private boolean checkAndRemoveUidLocked(int uid) { 3195 if (mIsAudioServerDown || mRoutingAudioPolicy == null) { 3196 Slogf.w(TAG, "checkAndRemoveUid Failed remove device affinity for uid %d" 3197 + ", routing policy not available.", 3198 uid); 3199 return false; 3200 } 3201 Integer zoneId = mUidToZoneMap.get(uid); 3202 if (zoneId != null) { 3203 Slogf.i(TAG, "checkAndRemoveUid removing Calling uid %d from zone %d", uid, zoneId); 3204 if (mRoutingAudioPolicy.removeUidDeviceAffinity(uid)) { 3205 // TODO use the uid device affinity in audio policy when available 3206 mUidToZoneMap.remove(uid); 3207 return true; 3208 } 3209 //failed to remove device affinity from zone devices 3210 Slogf.w(TAG, "checkAndRemoveUid Failed remove device affinity for uid %d in zone %d", 3211 uid, zoneId); 3212 return false; 3213 } 3214 return true; 3215 } 3216 3217 /* 3218 * {@link android.car.media.CarAudioManager#registerCarVolumeGroupEventCallback()} 3219 */ 3220 @Override 3221 public boolean registerCarVolumeEventCallback(ICarVolumeEventCallback callback) { 3222 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 3223 requireNonLegacyRouting(); 3224 requireVolumeGroupEvents(); 3225 3226 int uid = Binder.getCallingUid(); 3227 mCarVolumeEventHandler.registerCarVolumeEventCallback(callback, uid); 3228 mCarVolumeCallbackHandler.checkAndRepriotize(uid, false); 3229 return true; 3230 } 3231 3232 /* 3233 * {@link android.car.media.CarAudioManager#unregisterCarVolumeGroupEventCallback()} 3234 */ 3235 @Override 3236 public boolean unregisterCarVolumeEventCallback(ICarVolumeEventCallback callback) { 3237 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 3238 requireNonLegacyRouting(); 3239 requireVolumeGroupEvents(); 3240 3241 int uid = Binder.getCallingUid(); 3242 mCarVolumeEventHandler.unregisterCarVolumeEventCallback(callback, uid); 3243 mCarVolumeCallbackHandler.checkAndRepriotize(uid, true); 3244 return true; 3245 } 3246 3247 @Override 3248 public void registerVolumeCallback(@NonNull IBinder binder) { 3249 synchronized (mImplLock) { 3250 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 3251 int uid = Binder.getCallingUid(); 3252 mCarVolumeCallbackHandler.registerCallback(binder, uid, 3253 !mCarVolumeEventHandler.checkIfUidIsRegistered(uid)); 3254 } 3255 } 3256 3257 @Override 3258 public void unregisterVolumeCallback(@NonNull IBinder binder) { 3259 synchronized (mImplLock) { 3260 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 3261 mCarVolumeCallbackHandler.unregisterCallback(binder, Binder.getCallingUid()); 3262 } 3263 } 3264 3265 /** 3266 * {@link android.car.media.CarAudioManager#isVolumeGroupMuted(int, int)} 3267 */ 3268 @Override 3269 public boolean isVolumeGroupMuted(int zoneId, int groupId) { 3270 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 3271 requireNonLegacyRouting(); 3272 if (!useCarVolumeGroupMuting()) { 3273 return false; 3274 } 3275 synchronized (mImplLock) { 3276 CarVolumeGroup group = getCarVolumeGroupLocked(zoneId, groupId); 3277 return group.isMuted(); 3278 } 3279 } 3280 3281 /** 3282 * {@link android.car.media.CarAudioManager#setVolumeGroupMute(int, int, boolean, int)} 3283 */ 3284 @Override 3285 public void setVolumeGroupMute(int zoneId, int groupId, boolean mute, int flags) { 3286 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 3287 requireNonLegacyRouting(); 3288 requireVolumeGroupMuting(); 3289 boolean muteStateChanged; 3290 boolean isSystemMuted; 3291 synchronized (mImplLock) { 3292 CarVolumeGroup group = getCarVolumeGroupLocked(zoneId, groupId); 3293 isSystemMuted = group.isHalMuted(); 3294 muteStateChanged = group.setMute(mute); 3295 } 3296 if (muteStateChanged || (isSystemMuted && !mute)) { 3297 handleMuteChanged(zoneId, groupId, flags); 3298 callbackVolumeGroupEvent(List.of(convertVolumeChangeToEvent( 3299 getVolumeGroupInfo(zoneId, groupId), flags, EVENT_TYPE_MUTE_CHANGED))); 3300 } 3301 } 3302 3303 @Override 3304 public @NonNull List<AudioDeviceAttributes> getInputDevicesForZoneId(int zoneId) { 3305 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 3306 requireNonLegacyRouting(); 3307 3308 return getCarAudioZone(zoneId).getInputAudioDevices(); 3309 } 3310 3311 @Override 3312 public CarAudioZoneConfigInfo getCurrentAudioZoneConfigInfo(int zoneId) { 3313 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 3314 requireNonLegacyRouting(); 3315 synchronized (mImplLock) { 3316 return getCarAudioZoneLocked(zoneId).getCurrentCarAudioZoneConfig() 3317 .getCarAudioZoneConfigInfo(); 3318 } 3319 } 3320 3321 @Override 3322 public List<CarAudioZoneConfigInfo> getAudioZoneConfigInfos(int zoneId) { 3323 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 3324 requireNonLegacyRouting(); 3325 synchronized (mImplLock) { 3326 return getCarAudioZoneLocked(zoneId).getCarAudioZoneConfigInfos(); 3327 } 3328 } 3329 3330 @Override 3331 public void switchZoneToConfig(CarAudioZoneConfigInfo zoneConfig, 3332 ISwitchAudioZoneConfigCallback callback) { 3333 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 3334 requireNonLegacyRouting(); 3335 Objects.requireNonNull(zoneConfig, "Car audio zone config to switch to can not be null"); 3336 verifyCanSwitchZoneConfigs(zoneConfig); 3337 mHandler.post(() -> { 3338 boolean isSuccessful = handleSwitchZoneConfig(zoneConfig); 3339 CarAudioZoneConfigInfo updatedInfo = getAudioZoneConfigInfo(zoneConfig); 3340 try { 3341 callback.onAudioZoneConfigSwitched(updatedInfo, isSuccessful); 3342 } catch (RemoteException e) { 3343 Slogf.e(TAG, e, "Could not inform zone configuration %s switch result", 3344 updatedInfo); 3345 } 3346 }); 3347 } 3348 3349 @Override 3350 public boolean registerAudioZoneConfigsChangeCallback( 3351 IAudioZoneConfigurationsChangeCallback callback) { 3352 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 3353 requireNonLegacyRouting(); 3354 Objects.requireNonNull(callback, "Car audio zone configs callback can not be null"); 3355 3356 return mConfigsCallbacks.register(callback); 3357 } 3358 3359 @Override 3360 public boolean unregisterAudioZoneConfigsChangeCallback( 3361 IAudioZoneConfigurationsChangeCallback callback) { 3362 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 3363 requireNonLegacyRouting(); 3364 Objects.requireNonNull(callback, "Car audio zone configs callback can not be null"); 3365 3366 return mConfigsCallbacks.unregister(callback); 3367 } 3368 3369 @Nullable 3370 private CarAudioZoneConfigInfo getAudioZoneConfigInfo(CarAudioZoneConfigInfo zoneConfig) { 3371 List<CarAudioZoneConfigInfo> infos = getAudioZoneConfigInfos(zoneConfig.getZoneId()); 3372 for (int c = 0; c < infos.size(); c++) { 3373 if (infos.get(c).getConfigId() != zoneConfig.getConfigId()) { 3374 continue; 3375 } 3376 return infos.get(c); 3377 } 3378 return null; 3379 } 3380 3381 private void verifyCanSwitchZoneConfigs(CarAudioZoneConfigInfo zoneConfig) { 3382 int zoneId = zoneConfig.getZoneId(); 3383 synchronized (mImplLock) { 3384 checkAudioZoneIdLocked(zoneId); 3385 } 3386 3387 CarAudioZoneConfigInfo updatedInfo = getAudioZoneConfigInfo(zoneConfig); 3388 3389 if (updatedInfo == null) { 3390 throw new IllegalStateException("Car audio zone config " + zoneConfig.getConfigId() 3391 + " in zone " + zoneId + " does not exist"); 3392 } 3393 3394 if (!updatedInfo.isActive()) { 3395 throw new IllegalStateException("Car audio zone config " + zoneConfig.getConfigId() 3396 + " in zone " + zoneId + " is not active"); 3397 } 3398 3399 int userId = getUserIdForZone(zoneId); 3400 if (userId == UserManagerHelper.USER_NULL) { 3401 throw new IllegalStateException( 3402 "Audio zone must have an active user to allow switching zone configuration"); 3403 } 3404 3405 CarOccupantZoneManager.OccupantZoneInfo info = 3406 getCarOccupantZoneService().getOccupantForAudioZoneId(zoneId); 3407 3408 if (mMediaRequestHandler.isMediaAudioAllowedInPrimaryZone(info)) { 3409 throw new IllegalStateException( 3410 "Occupant " + info + " in audio zone " + zoneId 3411 + " is currently sharing to primary zone, undo audio sharing in " 3412 + "primary zone before switching zone configuration"); 3413 } 3414 3415 if (mCarAudioMirrorRequestHandler.isMirrorAudioEnabled() 3416 && mCarAudioMirrorRequestHandler.isMirrorEnabledForZone(zoneId)) { 3417 throw new IllegalStateException("Audio zone " + zoneId + " is currently in a mirroring" 3418 + " configuration, undo audio mirroring before switching zone configuration"); 3419 } 3420 } 3421 3422 private boolean handleSwitchZoneConfig(CarAudioZoneConfigInfo zoneConfig) { 3423 int zoneId = zoneConfig.getZoneId(); 3424 CarAudioZone zone; 3425 TimingsTraceLog log = new TimingsTraceLog(TAG, TraceHelper.TRACE_TAG_CAR_SERVICE); 3426 log.traceBegin("switch-config-" + zoneConfig.getConfigId()); 3427 synchronized (mImplLock) { 3428 zone = getCarAudioZoneLocked(zoneId); 3429 } 3430 if (zone.isCurrentZoneConfig(zoneConfig)) { 3431 Slogf.w(TAG, "handleSwitchZoneConfig switch current zone configuration"); 3432 log.traceEnd(); 3433 return true; 3434 } 3435 3436 CarOccupantZoneManager.OccupantZoneInfo info = 3437 getCarOccupantZoneService().getOccupantForAudioZoneId(zoneId); 3438 if (mMediaRequestHandler.isMediaAudioAllowedInPrimaryZone(info)) { 3439 Slogf.w(TAG, "handleSwitchZoneConfig failed, occupant %s in audio zone %d is " 3440 + "currently sharing to primary zone, undo audio sharing in primary " 3441 + "zone before switching zone configuration", info, zoneId); 3442 log.traceEnd(); 3443 return false; 3444 } 3445 3446 if (mCarAudioMirrorRequestHandler.isMirrorAudioEnabled() 3447 && mCarAudioMirrorRequestHandler.isMirrorEnabledForZone(zoneId)) { 3448 Slogf.w(TAG, "handleSwitchZoneConfig failed, audio zone %d is currently in a mirroring" 3449 + "configuration, undo audio mirroring before switching zone configuration", 3450 zoneId); 3451 log.traceEnd(); 3452 return false; 3453 } 3454 3455 boolean succeeded = true; 3456 List<CarVolumeGroupInfo> carVolumeGroupInfoList = null; 3457 AudioPolicy newAudioPolicy = null; 3458 CarAudioZoneConfig prevZoneConfig; 3459 synchronized (mImplLock) { 3460 int userId = getUserIdForZoneLocked(zoneId); 3461 if (userId == UserManagerHelper.USER_NULL) { 3462 Slogf.w(TAG, "handleSwitchZoneConfig failed, audio zone configuration switching " 3463 + "not allowed for unassigned audio zone %d", zoneId); 3464 log.traceEnd(); 3465 return false; 3466 } 3467 List<AudioFocusInfo> pendingFocusInfos = 3468 mFocusHandler.transientlyLoseAllFocusHoldersInZone(zoneId); 3469 3470 prevZoneConfig = zone.getCurrentCarAudioZoneConfig(); 3471 try { 3472 log.traceBegin("switch-config-set-" + zoneConfig.getConfigId()); 3473 // Core routing uses config change to setup audio routing for config changes 3474 zone.setCurrentCarZoneConfig(zoneConfig); 3475 newAudioPolicy = changeAudioPolicyForConfigChangeLocked(); 3476 zone.updateVolumeGroupsSettingsForUser(userId); 3477 carVolumeGroupInfoList = getVolumeGroupInfosForZoneLocked(zoneId); 3478 updateFadeManagerConfigurationLocked(zone.isPrimaryZone()); 3479 resetActivationTypeLocked(zoneConfig.getZoneId()); 3480 } catch (Exception e) { 3481 Slogf.e(TAG, "Failed to switch configuration id " + zoneConfig.getConfigId()); 3482 zone.setCurrentCarZoneConfig(prevZoneConfig.getCarAudioZoneConfigInfo()); 3483 succeeded = false; 3484 // No need to unset the user id device affinities, since the policy is removed 3485 if (newAudioPolicy != null && newAudioPolicy != mRoutingAudioPolicy) { 3486 mAudioManagerWrapper.unregisterAudioPolicyAsync(newAudioPolicy); 3487 } 3488 } finally { 3489 log.traceEnd(); 3490 } 3491 log.traceBegin("switch-config-focus" + zoneConfig.getConfigId()); 3492 mFocusHandler.reevaluateAndRegainAudioFocusList(pendingFocusInfos); 3493 log.traceEnd(); 3494 } 3495 if (!succeeded) { 3496 log.traceEnd(); 3497 return false; 3498 } 3499 enableDynamicDevicesInOtherZones(prevZoneConfig.getCarAudioZoneConfigInfo()); 3500 disableDynamicDevicesInOtherZones(zoneConfig); 3501 3502 log.traceEnd(); 3503 callbackVolumeGroupEvent(getVolumeGroupEventsForSwitchZoneConfig(carVolumeGroupInfoList)); 3504 return true; 3505 } 3506 3507 @GuardedBy("mImplLock") 3508 @Nullable 3509 private AudioPolicy changeAudioPolicyForConfigChangeLocked() { 3510 // Core audio routing does not uses audio policy to setup routing 3511 if (mUseCoreAudioRouting) { 3512 return null; 3513 } 3514 AudioPolicy newAudioPolicy = setupRoutingAudioPolicyLocked(); 3515 setAllUserIdDeviceAffinitiesToNewPolicyLocked(newAudioPolicy); 3516 swapRoutingAudioPolicyLocked(newAudioPolicy); 3517 return newAudioPolicy; 3518 } 3519 3520 private void enableDynamicDevicesInOtherZones(CarAudioZoneConfigInfo zoneConfig) { 3521 if (!Flags.carAudioDynamicDevices()) { 3522 return; 3523 } 3524 if (excludesDynamicDevices(zoneConfig)) { 3525 return; 3526 } 3527 List<AudioDeviceInfo> dynamicDevicesInConfig = 3528 getDynamicDevicesInConfig(zoneConfig, mAudioManagerWrapper); 3529 // If the devices were already removed just move on, device removal will manage the rest 3530 if (dynamicDevicesInConfig.isEmpty()) { 3531 return; 3532 } 3533 List<Integer> zonesToSkip = List.of(zoneConfig.getZoneId()); 3534 handleDevicesAdded(dynamicDevicesInConfig, zonesToSkip); 3535 } 3536 3537 private void disableDynamicDevicesInOtherZones(CarAudioZoneConfigInfo zoneConfig) { 3538 if (!Flags.carAudioDynamicDevices()) { 3539 return; 3540 } 3541 if (excludesDynamicDevices(zoneConfig)) { 3542 return; 3543 } 3544 List<AudioDeviceInfo> dynamicDevicesInConfig = 3545 getDynamicDevicesInConfig(zoneConfig, mAudioManagerWrapper); 3546 // If the devices were already removed just move on, device removal will manage the rest 3547 if (dynamicDevicesInConfig.isEmpty()) { 3548 return; 3549 } 3550 List<Integer> zonesToSkip = List.of(zoneConfig.getZoneId()); 3551 handleDevicesRemoved(dynamicDevicesInConfig, zonesToSkip); 3552 } 3553 3554 @GuardedBy("mImplLock") 3555 private void swapRoutingAudioPolicyLocked(AudioPolicy newAudioPolicy) { 3556 TimingsTraceLog log = new TimingsTraceLog(TAG, TraceHelper.TRACE_TAG_CAR_SERVICE); 3557 log.traceBegin("swap-policy"); 3558 if (newAudioPolicy == mRoutingAudioPolicy) { 3559 log.traceEnd(); 3560 return; 3561 } 3562 AudioPolicy previousRoutingPolicy = mRoutingAudioPolicy; 3563 mRoutingAudioPolicy = newAudioPolicy; 3564 if (previousRoutingPolicy == null) { 3565 log.traceEnd(); 3566 return; 3567 } 3568 try { 3569 mAudioManagerWrapper.unregisterAudioPolicy(previousRoutingPolicy); 3570 } finally { 3571 log.traceEnd(); 3572 } 3573 } 3574 3575 @GuardedBy("mImplLock") 3576 private void setAllUserIdDeviceAffinitiesToNewPolicyLocked(AudioPolicy newAudioPolicy) { 3577 TimingsTraceLog log = new TimingsTraceLog(TAG, TraceHelper.TRACE_TAG_CAR_SERVICE); 3578 log.traceBegin("device-affinities-all-zones"); 3579 for (int c = 0; c < mAudioZoneIdToOccupantZoneIdMapping.size(); c++) { 3580 int audioZoneId = mAudioZoneIdToOccupantZoneIdMapping.keyAt(c); 3581 int occupantZoneId = mAudioZoneIdToOccupantZoneIdMapping.get(audioZoneId); 3582 int userId = getCarOccupantZoneService().getUserForOccupant(occupantZoneId); 3583 if (userId == UserManagerHelper.USER_NULL) { 3584 continue; 3585 } 3586 log.traceBegin("device-affinities-" + audioZoneId); 3587 CarAudioZone zone = getCarAudioZoneLocked(audioZoneId); 3588 resetUserIdDeviceAffinitiesLocked(newAudioPolicy, userId, zone); 3589 log.traceEnd(); 3590 } 3591 log.traceEnd(); 3592 } 3593 3594 @GuardedBy("mImplLock") 3595 private void resetUserIdDeviceAffinitiesLocked(AudioPolicy audioPolicy, int userId, 3596 CarAudioZone zone) { 3597 List<AudioDeviceInfo> devices = getAudioDeviceInfos(zone); 3598 CarOccupantZoneManager.OccupantZoneInfo info = 3599 getCarOccupantZoneService().getOccupantForAudioZoneId(zone.getId()); 3600 if (mMediaRequestHandler.isMediaAudioAllowedInPrimaryZone(info)) { 3601 devices.add(getMediaDeviceForPrimaryZoneLocked()); 3602 } else if (mCarAudioMirrorRequestHandler.isMirrorEnabledForZone(zone.getId())) { 3603 long request = mCarAudioMirrorRequestHandler.getRequestIdForAudioZone(zone.getId()); 3604 if (request != INVALID_REQUEST_ID) { 3605 devices.add(getAudioDeviceInfoOrThrowIfNotFound( 3606 mCarAudioMirrorRequestHandler.getAudioDevice(request))); 3607 } 3608 } 3609 if (audioPolicy.setUserIdDeviceAffinity(userId, devices)) { 3610 return; 3611 } 3612 throw new IllegalStateException("Could not setup audio policy routing for user " + userId 3613 + " in audio zone " + zone.getId()); 3614 } 3615 3616 @GuardedBy("mImplLock") 3617 private AudioDeviceInfo getMediaDeviceForPrimaryZoneLocked() { 3618 CarAudioZone primaryAudioZone = getCarAudioZoneLocked(PRIMARY_AUDIO_ZONE); 3619 AudioDeviceAttributes audioDeviceAttributes = 3620 primaryAudioZone.getAudioDeviceForContext(mCarAudioContext 3621 .getContextForAudioAttribute(MEDIA_AUDIO_ATTRIBUTE)); 3622 return getAudioDeviceInfoOrThrowIfNotFound(audioDeviceAttributes); 3623 } 3624 3625 private List<CarVolumeGroupEvent> getVolumeGroupEventsForSwitchZoneConfig( 3626 List<CarVolumeGroupInfo> volumeGroupInfos) { 3627 CarVolumeGroupEvent.Builder builder = new CarVolumeGroupEvent.Builder(volumeGroupInfos, 3628 CarVolumeGroupEvent.EVENT_TYPE_ZONE_CONFIGURATION_CHANGED); 3629 return List.of(builder.build()); 3630 } 3631 3632 void setAudioEnabled(boolean isAudioEnabled) { 3633 Slogf.i(TAG, "Setting isAudioEnabled to %b", isAudioEnabled); 3634 3635 mFocusHandler.setRestrictFocus(/* isFocusRestricted= */ !isAudioEnabled); 3636 if (useCarVolumeGroupMuting()) { 3637 mCarVolumeGroupMuting.setRestrictMuting(/* isMutingRestricted= */ !isAudioEnabled); 3638 } 3639 // TODO(b/176258537) if not using group volume, then set master mute accordingly 3640 } 3641 3642 private void enforcePermission(String permissionName) { 3643 if (mContext.checkCallingOrSelfPermission(permissionName) 3644 != PackageManager.PERMISSION_GRANTED) { 3645 throw new SecurityException("requires permission " + permissionName); 3646 } 3647 } 3648 3649 private void requireNonLegacyRouting() { 3650 Preconditions.checkState(!runInLegacyMode(), "Non legacy routing is required"); 3651 } 3652 3653 private void requireAudioMirroring() { 3654 Preconditions.checkState(mCarAudioMirrorRequestHandler.isMirrorAudioEnabled(), 3655 "Audio zones mirroring is required"); 3656 } 3657 3658 private void requireVolumeGroupMuting() { 3659 Preconditions.checkState(useCarVolumeGroupMuting(), 3660 "Car Volume Group Muting is required"); 3661 } 3662 3663 private void requireVolumeGroupEvents() { 3664 Preconditions.checkState(mUseCarVolumeGroupEvents, 3665 "Car Volume Group Event is required"); 3666 } 3667 3668 private void requireValidFadeRange(float value) { 3669 Preconditions.checkArgumentInRange(value, -1f, 1f, "Fade"); 3670 } 3671 3672 private void requireValidBalanceRange(float value) { 3673 Preconditions.checkArgumentInRange(value, -1f, 1f, "Balance"); 3674 } 3675 3676 @GuardedBy("mImplLock") 3677 private void requiredOccupantZoneMappingDisabledLocked() { 3678 if (isOccupantZoneMappingAvailableLocked()) { 3679 throw new IllegalStateException( 3680 "UID based routing is not supported while using occupant zone mapping"); 3681 } 3682 } 3683 3684 @AudioContext int getSuggestedAudioContextForZone(int zoneId) { 3685 if (!isAudioZoneIdValid(zoneId)) { 3686 return CarAudioContext.getInvalidContext(); 3687 } 3688 CarVolume carVolume; 3689 synchronized (mImplLock) { 3690 carVolume = mCarVolume; 3691 } 3692 return carVolume.getSuggestedAudioContextAndSaveIfFound( 3693 getAllActiveAttributesForZone(zoneId), getCallStateForZone(zoneId), 3694 getActiveHalAudioAttributesForZone(zoneId), 3695 getInactiveAudioAttributesForZone(zoneId)); 3696 } 3697 3698 private List<AudioAttributes> getInactiveAudioAttributesForZone(int zoneId) { 3699 if (mUseKeyEventsForDynamicDevices) { 3700 return Collections.emptyList(); 3701 } 3702 3703 CarAudioZoneConfigInfo info; 3704 synchronized (mImplLock) { 3705 info = getCarAudioZoneLocked(zoneId).getCurrentCarAudioZoneConfig() 3706 .getCarAudioZoneConfigInfo(); 3707 } 3708 3709 return CarAudioUtils.getAudioAttributesForDynamicDevices(info); 3710 } 3711 3712 private List<AudioAttributes> getActiveHalAudioAttributesForZone(int zoneId) { 3713 synchronized (mImplLock) { 3714 if (mHalAudioFocus == null) { 3715 return new ArrayList<>(0); 3716 } 3717 return mHalAudioFocus.getActiveAudioAttributesForZone(zoneId); 3718 } 3719 } 3720 3721 /** 3722 * Gets volume group by a given legacy stream type 3723 * @param streamType Legacy stream type such as {@link AudioManager#STREAM_MUSIC} 3724 * @return volume group id mapped from stream type 3725 */ 3726 private int getVolumeGroupIdForStreamType(int streamType) { 3727 int groupId = INVALID_VOLUME_GROUP_ID; 3728 for (int i = 0; i < CarAudioDynamicRouting.STREAM_TYPES.length; i++) { 3729 if (streamType == CarAudioDynamicRouting.STREAM_TYPES[i]) { 3730 groupId = i; 3731 break; 3732 } 3733 } 3734 return groupId; 3735 } 3736 3737 private void handleOccupantZoneUserChanged() { 3738 int driverUserId = getCarOccupantZoneService().getDriverUserId(); 3739 Slogf.i(TAG, "handleOccupantZoneUserChanged current driver %s", driverUserId); 3740 synchronized (mImplLock) { 3741 if (!isOccupantZoneMappingAvailableLocked()) { 3742 adjustZonesToUserIdLocked(driverUserId); 3743 return; 3744 } 3745 int occupantZoneForDriver = getOccupantZoneIdForDriver(); 3746 Set<Integer> assignedZones = new HashSet<Integer>(); 3747 for (int index = 0; index < mAudioZoneIdToOccupantZoneIdMapping.size(); index++) { 3748 int audioZoneId = mAudioZoneIdToOccupantZoneIdMapping.keyAt(index); 3749 int occupantZoneId = mAudioZoneIdToOccupantZoneIdMapping.get(audioZoneId); 3750 assignedZones.add(audioZoneId); 3751 updateUserForOccupantZoneLocked(occupantZoneId, audioZoneId, driverUserId, 3752 occupantZoneForDriver); 3753 } 3754 3755 assignMissingZonesToDriverLocked(driverUserId, assignedZones); 3756 } 3757 restoreVolumeGroupMuteState(); 3758 } 3759 3760 private void restoreVolumeGroupMuteState() { 3761 if (!useCarVolumeGroupMuting()) { 3762 return; 3763 } 3764 mCarVolumeGroupMuting.carMuteChanged(); 3765 } 3766 3767 @GuardedBy("mImplLock") 3768 private void assignMissingZonesToDriverLocked(@UserIdInt int driverUserId, 3769 Set<Integer> assignedZones) { 3770 for (int i = 0; i < mCarAudioZones.size(); i++) { 3771 CarAudioZone zone = mCarAudioZones.valueAt(i); 3772 if (assignedZones.contains(zone.getId())) { 3773 continue; 3774 } 3775 assignUserIdToAudioZoneLocked(zone, driverUserId); 3776 } 3777 } 3778 3779 @GuardedBy("mImplLock") 3780 private void adjustZonesToUserIdLocked(@UserIdInt int userId) { 3781 for (int i = 0; i < mCarAudioZones.size(); i++) { 3782 CarAudioZone zone = mCarAudioZones.valueAt(i); 3783 assignUserIdToAudioZoneLocked(zone, userId); 3784 } 3785 } 3786 3787 @GuardedBy("mImplLock") 3788 private void assignUserIdToAudioZoneLocked(CarAudioZone zone, @UserIdInt int userId) { 3789 if (userId == getUserIdForZoneLocked(zone.getId())) { 3790 Slogf.d(TAG, "assignUserIdToAudioZone userId(%d) already assigned to audioZoneId(%d)", 3791 userId, zone.getId()); 3792 return; 3793 } 3794 Slogf.d(TAG, "assignUserIdToAudioZone assigning userId(%d) to audioZoneId(%d)", 3795 userId, zone.getId()); 3796 zone.updateVolumeGroupsSettingsForUser(userId); 3797 mFocusHandler.updateUserForZoneId(zone.getId(), userId); 3798 setUserIdForAudioZoneLocked(userId, zone.getId()); 3799 resetActivationTypeLocked(zone.getId()); 3800 } 3801 3802 @GuardedBy("mImplLock") 3803 private boolean isOccupantZoneMappingAvailableLocked() { 3804 return mAudioZoneIdToOccupantZoneIdMapping.size() > 0; 3805 } 3806 3807 @GuardedBy("mImplLock") 3808 private void updateUserForOccupantZoneLocked(int occupantZoneId, int audioZoneId, 3809 @UserIdInt int driverUserId, int occupantZoneForDriver) { 3810 CarAudioZone audioZone = getCarAudioZoneLocked(audioZoneId); 3811 int userId = getCarOccupantZoneService().getUserForOccupant(occupantZoneId); 3812 int prevUserId = getUserIdForZoneLocked(audioZoneId); 3813 3814 if (userId == prevUserId) { 3815 Slogf.d(TAG, "updateUserForOccupantZone userId(%d) already assigned to audioZoneId(%d)", 3816 userId, audioZoneId); 3817 return; 3818 } 3819 3820 // No need to undo focus or user device affinities. 3821 // Focus is handled as user exits. 3822 // User device affinities are handled below as the user id routing is undone. 3823 removePrimaryZoneRequestForOccupantLocked(occupantZoneId, prevUserId); 3824 3825 removeAudioMirrorForZoneId(audioZoneId); 3826 3827 Slogf.d(TAG, "updateUserForOccupantZone assigning userId(%d) to audioZoneId(%d)", 3828 userId, audioZoneId); 3829 // If the user has changed, be sure to remove from current routing 3830 // This would be true even if the new user is UserManagerHelper.USER_NULL, 3831 // as that indicates the user has logged out. 3832 removeUserIdDeviceAffinitiesLocked(prevUserId); 3833 3834 if (userId == UserManagerHelper.USER_NULL) { 3835 // Reset zone back to driver user id 3836 resetZoneToDefaultUser(audioZone, driverUserId); 3837 setUserIdForAudioZoneLocked(userId, audioZoneId); 3838 return; 3839 } 3840 3841 // Only set user id device affinities for driver when it is the driver's occupant zone 3842 if (userId != driverUserId || occupantZoneId == occupantZoneForDriver) { 3843 setUserIdDeviceAffinitiesLocked(audioZone, userId, audioZoneId); 3844 } 3845 audioZone.updateVolumeGroupsSettingsForUser(userId); 3846 mFocusHandler.updateUserForZoneId(audioZoneId, userId); 3847 setUserIdForAudioZoneLocked(userId, audioZoneId); 3848 resetActivationTypeLocked(audioZoneId); 3849 } 3850 3851 private void removeAudioMirrorForZoneId(int audioZoneId) { 3852 long requestId = mCarAudioMirrorRequestHandler.getRequestIdForAudioZone(audioZoneId); 3853 if (requestId == INVALID_REQUEST_ID) { 3854 return; 3855 } 3856 Slogf.i(TAG, "Removing audio zone mirror for zone id %s", audioZoneId); 3857 handleDisableAudioMirrorForZonesInConfig(new int[]{audioZoneId}, requestId); 3858 } 3859 3860 @GuardedBy("mImplLock") 3861 private void removePrimaryZoneRequestForOccupantLocked(int occupantZoneId, int userId) { 3862 long requestId = mMediaRequestHandler.getAssignedRequestIdForOccupantZoneId(occupantZoneId); 3863 3864 if (requestId == INVALID_REQUEST_ID) { 3865 return; 3866 } 3867 3868 Slogf.d(TAG, "removePrimaryZoneRequestForOccupant removing request for %d occupant %d" 3869 + " and user id %d", requestId, occupantZoneId, userId); 3870 removeAssignedUserInfoLocked(userId); 3871 mMediaRequestHandler.cancelMediaAudioOnPrimaryZone(requestId); 3872 } 3873 3874 private int getOccupantZoneIdForDriver() { 3875 List<CarOccupantZoneManager.OccupantZoneInfo> occupantZoneInfos = 3876 getCarOccupantZoneService().getAllOccupantZones(); 3877 for (CarOccupantZoneManager.OccupantZoneInfo info: occupantZoneInfos) { 3878 if (info.occupantType == CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER) { 3879 return info.zoneId; 3880 } 3881 } 3882 return CarOccupantZoneManager.OccupantZoneInfo.INVALID_ZONE_ID; 3883 } 3884 3885 @GuardedBy("mImplLock") 3886 private void setUserIdDeviceAffinitiesLocked(CarAudioZone zone, @UserIdInt int userId, 3887 int audioZoneId) { 3888 if (mIsAudioServerDown || mRoutingAudioPolicy == null) { 3889 Slogf.w(TAG, "setUserIdDeviceAffinitiesLocked failed, audio policy not in bad state"); 3890 return; 3891 } 3892 List<AudioDeviceInfo> infos = getAudioDeviceInfos(zone); 3893 if (!infos.isEmpty() && !mRoutingAudioPolicy.setUserIdDeviceAffinity(userId, infos)) { 3894 throw new IllegalStateException(String.format( 3895 "setUserIdDeviceAffinity for userId %d in zone %d Failed," 3896 + " could not set audio routing.", 3897 userId, audioZoneId)); 3898 } 3899 } 3900 3901 private List<AudioDeviceInfo> getAudioDeviceInfos(CarAudioZone zone) { 3902 List<AudioDeviceAttributes> attributes = zone.getCurrentAudioDeviceSupportingDynamicMix(); 3903 return getAudioDeviceInfosFromAttributes(attributes); 3904 } 3905 3906 private List<AudioDeviceInfo> getAudioDeviceInfosFromAttributes( 3907 List<AudioDeviceAttributes> attributes) { 3908 List<AudioDeviceInfo> devices = new ArrayList<>(attributes.size()); 3909 for (int i = 0; i < attributes.size(); i++) { 3910 devices.add(getAudioDeviceInfoOrThrowIfNotFound(attributes.get(i))); 3911 } 3912 return devices; 3913 } 3914 3915 private void resetZoneToDefaultUser(CarAudioZone zone, @UserIdInt int driverUserId) { 3916 resetCarZonesAudioFocus(zone.getId(), driverUserId); 3917 zone.updateVolumeGroupsSettingsForUser(driverUserId); 3918 synchronized (mImplLock) { 3919 resetActivationTypeLocked(zone.getId()); 3920 } 3921 } 3922 3923 private void resetCarZonesAudioFocus(int audioZoneId, @UserIdInt int driverUserId) { 3924 mFocusHandler.updateUserForZoneId(audioZoneId, driverUserId); 3925 } 3926 3927 @GuardedBy("mImplLock") 3928 private void removeUserIdDeviceAffinitiesLocked(@UserIdInt int userId) { 3929 Slogf.d(TAG, "removeUserIdDeviceAffinities(%d) Succeeded", userId); 3930 if (userId == UserManagerHelper.USER_NULL) { 3931 return; 3932 } 3933 if (mIsAudioServerDown || mRoutingAudioPolicy == null) { 3934 Slogf.e(TAG, "removeUserIdDeviceAffinities(%d) routing policy unavailable", userId); 3935 return; 3936 } 3937 if (!mRoutingAudioPolicy.removeUserIdDeviceAffinity(userId)) { 3938 Slogf.e(TAG, "removeUserIdDeviceAffinities(%d) Failed", userId); 3939 } 3940 } 3941 3942 @VisibleForTesting 3943 @UserIdInt int getUserIdForZone(int audioZoneId) { 3944 synchronized (mImplLock) { 3945 return getUserIdForZoneLocked(audioZoneId); 3946 } 3947 } 3948 3949 @GuardedBy("mImplLock") 3950 private @UserIdInt int getUserIdForZoneLocked(int audioZoneId) { 3951 return mAudioZoneIdToUserIdMapping.get(audioZoneId, UserManagerHelper.USER_NULL); 3952 } 3953 3954 @GuardedBy("mImplLock") 3955 private void setUserIdForAudioZoneLocked(@UserIdInt int userId, int audioZoneId) { 3956 mAudioZoneIdToUserIdMapping.put(audioZoneId, userId); 3957 } 3958 3959 @GuardedBy("mImplLock") 3960 private AudioControlWrapper getAudioControlWrapperLocked() { 3961 if (mAudioControlWrapper == null) { 3962 mAudioControlWrapper = AudioControlFactory.newAudioControl(); 3963 mAudioControlWrapper.linkToDeath(this::audioControlDied); 3964 } 3965 return mAudioControlWrapper; 3966 } 3967 3968 @GuardedBy("mImplLock") 3969 private void resetHalAudioFocusLocked() { 3970 if (mHalAudioFocus == null) { 3971 return; 3972 } 3973 mHalAudioFocus.reset(); 3974 mHalAudioFocus.registerFocusListener(); 3975 } 3976 3977 @GuardedBy("mImplLock") 3978 private void resetHalAudioGainLocked() { 3979 synchronized (mImplLock) { 3980 if (mCarAudioGainMonitor == null) { 3981 return; 3982 } 3983 mCarAudioGainMonitor.reset(); 3984 mCarAudioGainMonitor.registerAudioGainListener(mHalAudioGainCallback); 3985 } 3986 } 3987 3988 @GuardedBy("mImplLock") 3989 private void resetHalAudioModuleChangeLocked() { 3990 if (mCarAudioModuleChangeMonitor == null) { 3991 return; 3992 } 3993 mCarAudioModuleChangeMonitor.setModuleChangeCallback(mHalAudioModuleChangeCallback); 3994 } 3995 3996 @GuardedBy("mImplLock") 3997 private void handleAudioDeviceGainsChangedLocked( 3998 List<Integer> halReasons, List<CarAudioGainConfigInfo> gains) { 3999 if (mCarAudioGainMonitor == null) { 4000 return; 4001 } 4002 mCarAudioGainMonitor.handleAudioDeviceGainsChanged(halReasons, gains); 4003 } 4004 4005 @GuardedBy("mImplLock") 4006 private void handleAudioPortsChangedLocked(List<HalAudioDeviceInfo> deviceInfos) { 4007 if (mCarAudioModuleChangeMonitor == null) { 4008 return; 4009 } 4010 mCarAudioModuleChangeMonitor.handleAudioPortsChanged(deviceInfos); 4011 } 4012 4013 private void audioControlDied() { 4014 // If audio server is down, do not attempt to recover since it may lead to contention. 4015 // Once the audio server is back up the audio control HAL will be re-initialized. 4016 if (!mAudioManagerWrapper.isAudioServerRunning()) { 4017 String message = "Audio control died while audio server is not running"; 4018 Slogf.w(TAG, message); 4019 mServiceEventLogger.log(message); 4020 return; 4021 } 4022 synchronized (mImplLock) { 4023 // Verify the server has not gone down to prevent releasing audio control HAL 4024 if (mIsAudioServerDown) { 4025 String message = "Audio control died while audio server is down"; 4026 Slogf.w(TAG, message); 4027 mServiceEventLogger.log(message); 4028 return; 4029 } 4030 resetHalAudioFocusLocked(); 4031 resetHalAudioGainLocked(); 4032 resetHalAudioModuleChangeLocked(); 4033 } 4034 } 4035 4036 boolean isAudioZoneIdValid(int zoneId) { 4037 synchronized (mImplLock) { 4038 return mCarAudioZones.contains(zoneId); 4039 } 4040 } 4041 4042 private CarAudioZone getCarAudioZone(int zoneId) { 4043 synchronized (mImplLock) { 4044 return getCarAudioZoneLocked(zoneId); 4045 } 4046 } 4047 4048 @GuardedBy("mImplLock") 4049 private CarAudioZone getCarAudioZoneLocked(int zoneId) { 4050 checkAudioZoneIdLocked(zoneId); 4051 return mCarAudioZones.get(zoneId); 4052 } 4053 4054 private void checkAudioZoneId(int zoneId) { 4055 synchronized (mImplLock) { 4056 checkAudioZoneIdLocked(zoneId); 4057 } 4058 } 4059 4060 @GuardedBy("mImplLock") 4061 private void checkAudioZoneIdLocked(int zoneId) { 4062 Preconditions.checkArgument(mCarAudioZones.contains(zoneId), 4063 "Invalid audio zone Id " + zoneId); 4064 } 4065 4066 int getVolumeGroupIdForAudioContext(int zoneId, int suggestedContext) { 4067 synchronized (mImplLock) { 4068 return getVolumeGroupIdForAudioContextLocked(zoneId, suggestedContext); 4069 } 4070 } 4071 4072 /** 4073 * Resets the last selected volume context. 4074 */ 4075 @ExcludeFromCodeCoverageGeneratedReport(reason = DEBUGGING_CODE) 4076 public void resetSelectedVolumeContext() { 4077 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 4078 synchronized (mImplLock) { 4079 mCarVolume.resetSelectedVolumeContext(); 4080 mCarAudioPlaybackCallback.resetStillActiveContexts(); 4081 } 4082 } 4083 4084 @VisibleForTesting 4085 CarAudioContext getCarAudioContext() { 4086 synchronized (mImplLock) { 4087 return mCarAudioContext; 4088 } 4089 } 4090 4091 @VisibleForTesting 4092 boolean isConfiguredUsingAudioControlHAL() { 4093 synchronized (mImplLock) { 4094 return mCarAudioControlHalConfig; 4095 } 4096 } 4097 4098 @VisibleForTesting 4099 void requestAudioFocusForTest(AudioFocusInfo audioFocusInfo, int audioFocusResult) { 4100 mFocusHandler.onAudioFocusRequest(audioFocusInfo, audioFocusResult); 4101 } 4102 4103 int getZoneIdForAudioFocusInfo(AudioFocusInfo focusInfo) { 4104 if (isAllowedInPrimaryZone(focusInfo)) { 4105 return PRIMARY_AUDIO_ZONE; 4106 } 4107 4108 int audioZoneId; 4109 synchronized (mImplLock) { 4110 audioZoneId = getZoneIdForUidLocked(focusInfo.getClientUid()); 4111 } 4112 4113 if (isAudioZoneMirroringEnabledForZone(audioZoneId)) { 4114 long requestId = mCarAudioMirrorRequestHandler.getRequestIdForAudioZone(audioZoneId); 4115 int[] mirrorZones = mCarAudioMirrorRequestHandler.getMirrorAudioZonesForRequest( 4116 requestId); 4117 return ArrayUtils.isEmpty(mirrorZones) ? audioZoneId : mirrorZones[0]; 4118 } 4119 4120 return audioZoneId; 4121 } 4122 4123 private boolean isAllowedInPrimaryZone(AudioFocusInfo focusInfo) { 4124 boolean isMedia = CarAudioContext.AudioAttributesWrapper.audioAttributeMatches( 4125 CarAudioContext.getAudioAttributeFromUsage(USAGE_MEDIA), 4126 focusInfo.getAttributes()); 4127 4128 return isMedia && mMediaRequestHandler 4129 .isMediaAudioAllowedInPrimaryZone(getCarOccupantZoneService() 4130 .getOccupantZoneForUser(UserHandle 4131 .getUserHandleForUid(focusInfo.getClientUid()))); 4132 } 4133 4134 private boolean isAudioZoneMirroringEnabledForZone(int zoneId) { 4135 return mCarAudioMirrorRequestHandler.isMirrorEnabledForZone(zoneId); 4136 } 4137 4138 private List<AudioAttributes> getAllActiveAttributesForZone(int zoneId) { 4139 synchronized (mImplLock) { 4140 return mCarAudioPlaybackCallback.getAllActiveAudioAttributesForZone(zoneId); 4141 } 4142 } 4143 4144 private boolean runInLegacyMode() { 4145 synchronized (mImplLock) { 4146 return !mUseDynamicRouting && !mUseCoreAudioRouting; 4147 } 4148 } 4149 4150 List<CarVolumeGroupInfo> getMutedVolumeGroups(int zoneId) { 4151 List<CarVolumeGroupInfo> mutedGroups = new ArrayList<>(); 4152 4153 if (!useCarVolumeGroupMuting() || !isAudioZoneIdValid(zoneId)) { 4154 return mutedGroups; 4155 } 4156 4157 synchronized (mImplLock) { 4158 int groupCount = getCarAudioZoneLocked(zoneId).getCurrentVolumeGroupCount(); 4159 for (int groupId = 0; groupId < groupCount; groupId++) { 4160 CarVolumeGroup group = getCarVolumeGroupLocked(zoneId, groupId); 4161 if (!group.isMuted()) { 4162 continue; 4163 } 4164 4165 mutedGroups.add(group.getCarVolumeGroupInfo()); 4166 } 4167 } 4168 4169 return mutedGroups; 4170 } 4171 4172 List<AudioAttributes> getActiveAudioAttributesForZone(int zoneId) { 4173 List<AudioAttributes> activeAudioAttributes = new ArrayList<>(); 4174 activeAudioAttributes.addAll(getAllActiveAttributesForZone(zoneId)); 4175 activeAudioAttributes.addAll(getActiveHalAudioAttributesForZone(zoneId)); 4176 4177 return activeAudioAttributes; 4178 } 4179 4180 int getVolumeGroupIdForAudioAttribute(int audioZoneId, AudioAttributes attributes) { 4181 Objects.requireNonNull(attributes, "Audio attributes can not be null"); 4182 checkAudioZoneId(audioZoneId); 4183 synchronized (mImplLock) { 4184 return getVolumeGroupIdForAudioAttributeLocked(audioZoneId, attributes); 4185 } 4186 } 4187 4188 void audioDevicesAdded(AudioDeviceInfo[] addedDevices) { 4189 synchronized (mImplLock) { 4190 if (mIsAudioServerDown) { 4191 return; 4192 } 4193 } 4194 Slogf.d(TAG, "Added audio devices " + Arrays.toString(addedDevices)); 4195 List<AudioDeviceInfo> devices = filterBusDevices(addedDevices); 4196 4197 if (devices.isEmpty()) { 4198 return; 4199 } 4200 4201 handleDevicesAdded(devices, EMPTY_LIST); 4202 } 4203 4204 private void handleDevicesAdded(List<AudioDeviceInfo> devices, List<Integer> zonesToSkip) { 4205 List<CarAudioZoneConfigInfo> updatedInfos = new ArrayList<>(); 4206 synchronized (mImplLock) { 4207 for (int c = 0; c < mCarAudioZones.size(); c++) { 4208 CarAudioZone zone = mCarAudioZones.valueAt(c); 4209 if (zonesToSkip.contains(zone.getId())) { 4210 continue; 4211 } 4212 if (!zone.audioDevicesAdded(devices)) { 4213 continue; 4214 } 4215 updatedInfos.addAll(zone.getCarAudioZoneConfigInfos()); 4216 } 4217 } 4218 mHandler.post(() -> { 4219 triggerAudioZoneConfigInfosUpdated(new AudioZoneConfigCallbackInfo(updatedInfos, 4220 CONFIG_STATUS_CHANGED)); 4221 }); 4222 } 4223 4224 void audioDevicesRemoved(AudioDeviceInfo[] removedDevices) { 4225 synchronized (mImplLock) { 4226 if (mIsAudioServerDown) { 4227 return; 4228 } 4229 } 4230 Slogf.d(TAG, "Removed audio devices " + Arrays.toString(removedDevices)); 4231 List<AudioDeviceInfo> devices = filterBusDevices(removedDevices); 4232 4233 if (devices.isEmpty()) { 4234 return; 4235 } 4236 4237 handleDevicesRemoved(devices, EMPTY_LIST); 4238 } 4239 4240 private void handleDevicesRemoved(List<AudioDeviceInfo> devices, List<Integer> zonesToSkip) { 4241 List<AudioZoneConfigCallbackInfo> callbackInfos = new ArrayList<>(); 4242 List<CarAudioZoneConfigInfo> updatedInfos = new ArrayList<>(); 4243 synchronized (mImplLock) { 4244 for (int c = 0; c < mCarAudioZones.size(); c++) { 4245 CarAudioZone zone = mCarAudioZones.valueAt(c); 4246 if (zonesToSkip.contains(zone.getId())) { 4247 continue; 4248 } 4249 if (!zone.audioDevicesRemoved(devices)) { 4250 continue; 4251 } 4252 CarAudioZoneConfigInfo prevConfig = 4253 zone.getCurrentCarAudioZoneConfig().getCarAudioZoneConfigInfo(); 4254 if (!prevConfig.isSelected() || prevConfig.isActive()) { 4255 // Only update the infos if it is not auto switching 4256 // Otherwise let auto switching handle the callback for the config info 4257 // change 4258 updatedInfos.addAll(zone.getCarAudioZoneConfigInfos()); 4259 continue; 4260 } 4261 // If we are skipping configurations then auto switch to prevent recursion 4262 if (!zonesToSkip.isEmpty()) { 4263 continue; 4264 } 4265 // Current config is no longer active, switch back to default and trigger 4266 // callback with auto switched signal 4267 CarAudioZoneConfigInfo defaultConfig = zone.getDefaultAudioZoneConfigInfo(); 4268 handleSwitchZoneConfig(defaultConfig); 4269 CarAudioZoneConfigInfo updatedConfig = getAudioZoneConfigInfo(defaultConfig); 4270 CarAudioZoneConfigInfo updatedPrevInfo = getAudioZoneConfigInfo(prevConfig); 4271 callbackInfos.add(new AudioZoneConfigCallbackInfo( 4272 List.of(updatedConfig, updatedPrevInfo), 4273 CarAudioManager.CONFIG_STATUS_AUTO_SWITCHED)); 4274 } 4275 } 4276 callbackInfos.add(new AudioZoneConfigCallbackInfo(updatedInfos, CONFIG_STATUS_CHANGED)); 4277 mHandler.post(() -> { 4278 for (int c = 0; c < callbackInfos.size(); c++) { 4279 triggerAudioZoneConfigInfosUpdated(callbackInfos.get(c)); 4280 } 4281 }); 4282 } 4283 4284 private void triggerAudioZoneConfigInfosUpdated(AudioZoneConfigCallbackInfo configsInfo) { 4285 if (configsInfo.mInfos.isEmpty()) { 4286 return; 4287 } 4288 int n = mConfigsCallbacks.beginBroadcast(); 4289 while (n > 0) { 4290 n--; 4291 IAudioZoneConfigurationsChangeCallback callback = mConfigsCallbacks.getBroadcastItem(n); 4292 try { 4293 callback.onAudioZoneConfigurationsChanged(configsInfo.mInfos, configsInfo.mStatus); 4294 } catch (RemoteException e) { 4295 Slogf.e(TAG, "Failed to trigger audio zone config changed callback " 4296 + configsInfo.mStatus + " callback[" + n + "] " + callback.asBinder()); 4297 } 4298 } 4299 mConfigsCallbacks.finishBroadcast(); 4300 } 4301 4302 private static List<AudioDeviceInfo> filterBusDevices(AudioDeviceInfo[] infos) { 4303 List<AudioDeviceInfo> devices = new ArrayList<>(); 4304 for (int c = 0; c < infos.length; c++) { 4305 if (infos[c].isSource() || infos[c].getType() == AudioDeviceInfo.TYPE_BUS) { 4306 continue; 4307 } 4308 devices.add(infos[c]); 4309 } 4310 return devices; 4311 } 4312 4313 static final class SystemClockWrapper { 4314 public long uptimeMillis() { 4315 return SystemClock.uptimeMillis(); 4316 } 4317 } 4318 4319 void onVolumeGroupEvent(List<CarVolumeGroupEvent> events) { 4320 for (int index = 0; index < events.size(); index++) { 4321 CarVolumeGroupEvent event = events.get(index); 4322 List<CarVolumeGroupInfo> infos = event.getCarVolumeGroupInfos(); 4323 boolean volumeEvent = 4324 (event.getEventTypes() & EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED) != 0; 4325 boolean muteEvent = (event.getEventTypes() & EVENT_TYPE_MUTE_CHANGED) != 0; 4326 if (!volumeEvent && !muteEvent) { 4327 continue; 4328 } 4329 for (int infoIndex = 0; infoIndex < infos.size(); infoIndex++) { 4330 CarVolumeGroupInfo info = infos.get(infoIndex); 4331 int groupId = info.getId(); 4332 int zoneId = info.getZoneId(); 4333 if (volumeEvent) { 4334 mCarVolumeCallbackHandler.onVolumeGroupChange(zoneId, groupId, /* flags= */ 0); 4335 } 4336 if (muteEvent) { 4337 handleMuteChanged(zoneId, groupId, /* flags= */ 0); 4338 } 4339 } 4340 } 4341 callbackVolumeGroupEvent(events); 4342 } 4343 4344 void onAudioVolumeGroupChanged(int zoneId, String groupName, int flags) { 4345 int callbackFlags = flags; 4346 synchronized (mImplLock) { 4347 CarVolumeGroup group = getCarVolumeGroupLocked(zoneId, groupName); 4348 if (group == null) { 4349 Slogf.w(TAG, "onAudioVolumeGroupChanged reported on unmanaged group (%s)", 4350 groupName); 4351 return; 4352 } 4353 int eventTypes = group.onAudioVolumeGroupChanged(callbackFlags); 4354 if (eventTypes == 0) { 4355 return; 4356 } 4357 if ((eventTypes & EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED) != 0) { 4358 callbackGroupVolumeChange(zoneId, group.getId(), callbackFlags); 4359 if (!runInLegacyMode() && !isPlaybackOnVolumeGroupActive(zoneId, group.getId())) { 4360 callbackFlags |= FLAG_PLAY_SOUND; 4361 } 4362 } 4363 if ((eventTypes & EVENT_TYPE_MUTE_CHANGED) != 0) { 4364 // Mute event shall not play any sound, use policy flags only. 4365 handleMuteChanged(zoneId, group.getId(), flags); 4366 } 4367 callbackVolumeGroupEvent(List.of(convertVolumeChangeToEvent( 4368 getVolumeGroupInfo(zoneId, group.getId()), callbackFlags, eventTypes))); 4369 } 4370 } 4371 4372 private static final class AudioFocusStackRequest { 4373 private final AudioFocusStack mStack; 4374 private final int mOriginalZoneId; 4375 4376 AudioFocusStackRequest(AudioFocusStack stack, int originalZoneId) { 4377 mOriginalZoneId = originalZoneId; 4378 mStack = stack; 4379 } 4380 } 4381 4382 private static final class AudioZoneConfigCallbackInfo { 4383 private final List<CarAudioZoneConfigInfo> mInfos; 4384 private final int mStatus; 4385 4386 AudioZoneConfigCallbackInfo(List<CarAudioZoneConfigInfo> infos, int status) { 4387 mInfos = infos; 4388 mStatus = status; 4389 } 4390 } 4391 } 4392