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