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.builtin.media.AudioManagerHelper.isMasterMute; 20 import static android.car.media.CarAudioManager.AUDIO_FEATURE_DYNAMIC_ROUTING; 21 import static android.car.media.CarAudioManager.AUDIO_FEATURE_VOLUME_GROUP_MUTING; 22 import static android.car.media.CarAudioManager.CarAudioFeature; 23 import static android.car.media.CarAudioManager.INVALID_VOLUME_GROUP_ID; 24 import static android.car.media.CarAudioManager.PRIMARY_AUDIO_ZONE; 25 import static android.media.AudioManager.FLAG_FROM_KEY; 26 import static android.media.AudioManager.FLAG_PLAY_SOUND; 27 import static android.media.AudioManager.FLAG_SHOW_UI; 28 29 import static com.android.car.audio.CarVolume.VERSION_TWO; 30 import static com.android.car.audio.hal.AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_DUCKING; 31 import static com.android.car.audio.hal.AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_FOCUS; 32 import static com.android.car.audio.hal.AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_GAIN_CALLBACK; 33 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 34 35 import android.annotation.NonNull; 36 import android.annotation.Nullable; 37 import android.annotation.UserIdInt; 38 import android.car.Car; 39 import android.car.CarOccupantZoneManager; 40 import android.car.CarOccupantZoneManager.OccupantZoneConfigChangeListener; 41 import android.car.builtin.media.AudioManagerHelper; 42 import android.car.builtin.media.AudioManagerHelper.AudioPatchInfo; 43 import android.car.builtin.media.AudioManagerHelper.VolumeAndMuteReceiver; 44 import android.car.builtin.os.UserManagerHelper; 45 import android.car.builtin.util.Slogf; 46 import android.car.media.CarAudioManager; 47 import android.car.media.CarAudioPatchHandle; 48 import android.car.media.CarVolumeGroupInfo; 49 import android.car.media.ICarAudio; 50 import android.car.media.ICarVolumeCallback; 51 import android.content.Context; 52 import android.content.pm.PackageManager; 53 import android.media.AudioAttributes; 54 import android.media.AudioDeviceAttributes; 55 import android.media.AudioDeviceInfo; 56 import android.media.AudioFocusInfo; 57 import android.media.AudioManager; 58 import android.media.audiopolicy.AudioPolicy; 59 import android.os.IBinder; 60 import android.os.Looper; 61 import android.os.SystemClock; 62 import android.os.SystemProperties; 63 import android.os.UserHandle; 64 import android.telephony.TelephonyManager; 65 import android.text.TextUtils; 66 import android.util.SparseArray; 67 import android.util.SparseIntArray; 68 69 import com.android.car.CarLocalServices; 70 import com.android.car.CarLog; 71 import com.android.car.CarOccupantZoneService; 72 import com.android.car.CarServiceBase; 73 import com.android.car.CarServiceUtils; 74 import com.android.car.R; 75 import com.android.car.audio.CarAudioContext.AudioContext; 76 import com.android.car.audio.hal.AudioControlFactory; 77 import com.android.car.audio.hal.AudioControlWrapper; 78 import com.android.car.audio.hal.AudioControlWrapperV1; 79 import com.android.car.audio.hal.HalAudioFocus; 80 import com.android.car.audio.hal.HalAudioGainCallback; 81 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 82 import com.android.car.internal.annotation.AttributeUsage; 83 import com.android.car.internal.util.IndentingPrintWriter; 84 import com.android.internal.annotations.GuardedBy; 85 import com.android.internal.annotations.VisibleForTesting; 86 import com.android.internal.util.Preconditions; 87 88 import org.xmlpull.v1.XmlPullParserException; 89 90 import java.io.File; 91 import java.io.FileInputStream; 92 import java.io.IOException; 93 import java.io.InputStream; 94 import java.util.ArrayList; 95 import java.util.Collections; 96 import java.util.HashMap; 97 import java.util.HashSet; 98 import java.util.List; 99 import java.util.Map; 100 import java.util.Objects; 101 import java.util.Set; 102 103 /** 104 * Service responsible for interaction with car's audio system. 105 */ 106 public class CarAudioService extends ICarAudio.Stub implements CarServiceBase { 107 108 static final String TAG = CarLog.TAG_AUDIO; 109 110 static final AudioAttributes CAR_DEFAULT_AUDIO_ATTRIBUTE = 111 CarAudioContext.getAudioAttributeFromUsage(AudioAttributes.USAGE_MEDIA); 112 113 private static final String PROPERTY_RO_ENABLE_AUDIO_PATCH = 114 "ro.android.car.audio.enableaudiopatch"; 115 116 // CarAudioService reads configuration from the following paths respectively. 117 // If the first one is found, all others are ignored. 118 // If no one is found, it fallbacks to car_volume_groups.xml resource file. 119 private static final String[] AUDIO_CONFIGURATION_PATHS = new String[] { 120 "/vendor/etc/car_audio_configuration.xml", 121 "/system/etc/car_audio_configuration.xml" 122 }; 123 124 private final Object mImplLock = new Object(); 125 126 private final Context mContext; 127 private final TelephonyManager mTelephonyManager; 128 private final AudioManager mAudioManager; 129 private final boolean mUseDynamicRouting; 130 private final boolean mUseCarVolumeGroupMuting; 131 private final boolean mUseHalDuckingSignals; 132 private final @CarVolume.CarVolumeListVersion int mAudioVolumeAdjustmentContextsVersion; 133 private final boolean mPersistMasterMuteState; 134 private final CarAudioSettings mCarAudioSettings; 135 private final int mKeyEventTimeoutMs; 136 private AudioControlWrapper mAudioControlWrapper; 137 private CarDucking mCarDucking; 138 private CarVolumeGroupMuting mCarVolumeGroupMuting; 139 private HalAudioFocus mHalAudioFocus; 140 private @Nullable CarAudioGainMonitor mCarAudioGainMonitor; 141 142 private CarOccupantZoneService mOccupantZoneService; 143 144 private CarOccupantZoneManager mOccupantZoneManager; 145 146 /** 147 * Simulates {@link ICarVolumeCallback} when it's running in legacy mode. 148 * This receiver assumes the intent is sent to {@link CarAudioManager#PRIMARY_AUDIO_ZONE}. 149 */ 150 private final VolumeAndMuteReceiver mLegacyVolumeChangedHelper = 151 new AudioManagerHelper.VolumeAndMuteReceiver() { 152 @Override 153 public void onVolumeChanged(int streamType) { 154 if (streamType == UNDEFINED_STREAM_TYPE) { 155 Slogf.w(TAG, "Invalid stream type: %d", streamType); 156 } 157 int groupId = getVolumeGroupIdForStreamType(streamType); 158 if (groupId == INVALID_VOLUME_GROUP_ID) { 159 Slogf.w(TAG, "Unknown stream type: %d", streamType); 160 } else { 161 callbackGroupVolumeChange(PRIMARY_AUDIO_ZONE, groupId, 162 FLAG_FROM_KEY | FLAG_SHOW_UI); 163 } 164 } 165 166 @Override 167 public void onMuteChanged() { 168 callbackMasterMuteChange(PRIMARY_AUDIO_ZONE, FLAG_FROM_KEY | FLAG_SHOW_UI); 169 } 170 }; 171 172 private AudioPolicy mAudioPolicy; 173 private CarZonesAudioFocus mFocusHandler; 174 private String mCarAudioConfigurationPath; 175 private SparseIntArray mAudioZoneIdToOccupantZoneIdMapping; 176 @GuardedBy("mImplLock") 177 private SparseArray<CarAudioZone> mCarAudioZones; 178 @GuardedBy("mImplLock") 179 private CarVolume mCarVolume; 180 @GuardedBy("mImplLock") 181 private CarAudioContext mCarAudioContext; 182 private final CarVolumeCallbackHandler mCarVolumeCallbackHandler; 183 private final SparseIntArray mAudioZoneIdToUserIdMapping; 184 private final SystemClockWrapper mClock = new SystemClockWrapper(); 185 186 // TODO do not store uid mapping here instead use the uid 187 // device affinity in audio policy when available 188 private Map<Integer, Integer> mUidToZoneMap; 189 private OccupantZoneConfigChangeListener 190 mOccupantZoneConfigChangeListener = new CarAudioOccupantConfigChangeListener(); 191 private CarAudioPlaybackCallback mCarAudioPlaybackCallback; 192 private CarAudioPowerListener mCarAudioPowerListener; 193 194 private final HalAudioGainCallback mHalAudioGainCallback = 195 new HalAudioGainCallback() { 196 @Override 197 public void onAudioDeviceGainsChanged( 198 List<Integer> halReasons, List<CarAudioGainConfigInfo> gains) { 199 synchronized (mImplLock) { 200 handleAudioDeviceGainsChangedLocked(halReasons, gains); 201 } 202 } 203 }; 204 CarAudioService(Context context)205 public CarAudioService(Context context) { 206 this(context, getAudioConfigurationPath(), new CarVolumeCallbackHandler()); 207 } 208 209 @VisibleForTesting CarAudioService(Context context, @Nullable String audioConfigurationPath, CarVolumeCallbackHandler carVolumeCallbackHandler)210 CarAudioService(Context context, @Nullable String audioConfigurationPath, 211 CarVolumeCallbackHandler carVolumeCallbackHandler) { 212 mContext = Objects.requireNonNull(context, 213 "Context to create car audio service can not be null"); 214 mCarAudioConfigurationPath = audioConfigurationPath; 215 mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); 216 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 217 218 mUseDynamicRouting = mContext.getResources().getBoolean(R.bool.audioUseDynamicRouting); 219 mKeyEventTimeoutMs = 220 mContext.getResources().getInteger(R.integer.audioVolumeKeyEventTimeoutMs); 221 mUseHalDuckingSignals = mContext.getResources().getBoolean( 222 R.bool.audioUseHalDuckingSignals); 223 224 mUidToZoneMap = new HashMap<>(); 225 mCarVolumeCallbackHandler = carVolumeCallbackHandler; 226 mCarAudioSettings = new CarAudioSettings(mContext); 227 mAudioZoneIdToUserIdMapping = new SparseIntArray(); 228 mAudioVolumeAdjustmentContextsVersion = 229 mContext.getResources().getInteger(R.integer.audioVolumeAdjustmentContextsVersion); 230 boolean useCarVolumeGroupMuting = mUseDynamicRouting && mContext.getResources().getBoolean( 231 R.bool.audioUseCarVolumeGroupMuting); 232 if (mAudioVolumeAdjustmentContextsVersion != VERSION_TWO && useCarVolumeGroupMuting) { 233 throw new IllegalArgumentException("audioUseCarVolumeGroupMuting is enabled but " 234 + "this requires audioVolumeAdjustmentContextsVersion 2," 235 + " instead version " + mAudioVolumeAdjustmentContextsVersion + " was found"); 236 } 237 mUseCarVolumeGroupMuting = useCarVolumeGroupMuting; 238 mPersistMasterMuteState = !mUseCarVolumeGroupMuting && mContext.getResources().getBoolean( 239 R.bool.audioPersistMasterMuteState); 240 } 241 242 /** 243 * Dynamic routing and volume groups are set only if 244 * {@link #mUseDynamicRouting} is {@code true}. Otherwise, this service runs in legacy mode. 245 */ 246 @Override init()247 public void init() { 248 synchronized (mImplLock) { 249 mOccupantZoneService = CarLocalServices.getService(CarOccupantZoneService.class); 250 Car car = new Car(mContext, /* service= */null, /* handler= */ null); 251 mOccupantZoneManager = new CarOccupantZoneManager(car, mOccupantZoneService); 252 if (mUseDynamicRouting) { 253 setupDynamicRoutingLocked(); 254 setupHalAudioFocusListenerLocked(); 255 setupHalAudioGainCallbackLocked(); 256 setupAudioConfigurationCallbackLocked(); 257 setupPowerPolicyListener(); 258 } else { 259 Slogf.i(TAG, "Audio dynamic routing not enabled, run in legacy mode"); 260 setupLegacyVolumeChangedListener(); 261 } 262 263 mAudioManager.setSupportedSystemUsages(CarAudioContext.getSystemUsages()); 264 } 265 266 restoreMasterMuteState(); 267 } 268 setupPowerPolicyListener()269 private void setupPowerPolicyListener() { 270 mCarAudioPowerListener = CarAudioPowerListener.newCarAudioPowerListener(this); 271 mCarAudioPowerListener.startListeningForPolicyChanges(); 272 } 273 restoreMasterMuteState()274 private void restoreMasterMuteState() { 275 if (mUseCarVolumeGroupMuting) { 276 return; 277 } 278 // Restore master mute state if applicable 279 if (mPersistMasterMuteState) { 280 boolean storedMasterMute = mCarAudioSettings.getMasterMute(); 281 setMasterMute(storedMasterMute, 0); 282 } 283 } 284 285 @Override release()286 public void release() { 287 synchronized (mImplLock) { 288 if (mUseDynamicRouting) { 289 if (mAudioPolicy != null) { 290 mAudioManager.unregisterAudioPolicyAsync(mAudioPolicy); 291 mAudioPolicy = null; 292 mFocusHandler.setOwningPolicy(null, null); 293 mFocusHandler = null; 294 } 295 } else { 296 AudioManagerHelper.unregisterVolumeAndMuteReceiver(mContext, 297 mLegacyVolumeChangedHelper); 298 299 } 300 301 mCarVolumeCallbackHandler.release(); 302 303 if (mHalAudioFocus != null) { 304 mHalAudioFocus.unregisterFocusListener(); 305 } 306 307 if (mAudioControlWrapper != null) { 308 mAudioControlWrapper.unlinkToDeath(); 309 mAudioControlWrapper = null; 310 } 311 312 if (mCarAudioPowerListener != null) { 313 mCarAudioPowerListener.stopListeningForPolicyChanges(); 314 } 315 } 316 } 317 318 @Override 319 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)320 public void dump(IndentingPrintWriter writer) { 321 synchronized (mImplLock) { 322 writer.println("*CarAudioService*"); 323 writer.increaseIndent(); 324 325 writer.println("Configurations:"); 326 writer.increaseIndent(); 327 writer.printf("Run in legacy mode? %b\n", !mUseDynamicRouting); 328 writer.printf("Audio Patch APIs enabled? %b\n", areAudioPatchAPIsEnabled()); 329 writer.printf("Persist master mute state? %b\n", mPersistMasterMuteState); 330 writer.printf("Use hal ducking signals %b\n", mUseHalDuckingSignals); 331 writer.printf("Volume key event timeout ms: %d\n", mKeyEventTimeoutMs); 332 if (mCarAudioConfigurationPath != null) { 333 writer.printf("Car audio configuration path: %s\n", mCarAudioConfigurationPath); 334 } 335 writer.decreaseIndent(); 336 writer.println(); 337 338 writer.println("Current State:"); 339 writer.increaseIndent(); 340 writer.printf("Master muted? %b\n", isMasterMute(mAudioManager)); 341 if (mCarAudioPowerListener != null) { 342 writer.printf("Audio enabled? %b\n", mCarAudioPowerListener.isAudioEnabled()); 343 } 344 writer.decreaseIndent(); 345 writer.println(); 346 347 if (mUseDynamicRouting) { 348 writer.printf("Volume Group Mute Enabled? %b\n", mUseCarVolumeGroupMuting); 349 writer.println(); 350 mCarVolume.dump(writer); 351 writer.println(); 352 mCarAudioContext.dump(writer); 353 writer.println(); 354 for (int i = 0; i < mCarAudioZones.size(); i++) { 355 CarAudioZone zone = mCarAudioZones.valueAt(i); 356 zone.dump(writer); 357 } 358 writer.println(); 359 writer.println("UserId to Zone Mapping:"); 360 writer.increaseIndent(); 361 for (int index = 0; index < mAudioZoneIdToUserIdMapping.size(); index++) { 362 int audioZoneId = mAudioZoneIdToUserIdMapping.keyAt(index); 363 writer.printf("UserId %d mapped to zone %d\n", 364 mAudioZoneIdToUserIdMapping.get(audioZoneId), 365 audioZoneId); 366 } 367 writer.decreaseIndent(); 368 writer.println(); 369 writer.println("Audio Zone to Occupant Zone Mapping:"); 370 writer.increaseIndent(); 371 for (int index = 0; index < mAudioZoneIdToOccupantZoneIdMapping.size(); index++) { 372 int audioZoneId = mAudioZoneIdToOccupantZoneIdMapping.keyAt(index); 373 writer.printf("AudioZoneId %d mapped to OccupantZoneId %d\n", audioZoneId, 374 mAudioZoneIdToOccupantZoneIdMapping.get(audioZoneId)); 375 } 376 writer.decreaseIndent(); 377 writer.println(); 378 writer.println("UID to Zone Mapping:"); 379 writer.increaseIndent(); 380 for (int callingId : mUidToZoneMap.keySet()) { 381 writer.printf("UID %d mapped to zone %d\n", 382 callingId, 383 mUidToZoneMap.get(callingId)); 384 } 385 writer.decreaseIndent(); 386 387 writer.println(); 388 mFocusHandler.dump(writer); 389 390 writer.println(); 391 getAudioControlWrapperLocked().dump(writer); 392 393 if (mHalAudioFocus != null) { 394 writer.println(); 395 mHalAudioFocus.dump(writer); 396 } else { 397 writer.println("No HalAudioFocus instance\n"); 398 } 399 if (mCarDucking != null) { 400 writer.println(); 401 mCarDucking.dump(writer); 402 } 403 if (mCarVolumeGroupMuting != null) { 404 mCarVolumeGroupMuting.dump(writer); 405 } 406 407 } 408 writer.decreaseIndent(); 409 } 410 } 411 412 @Override isAudioFeatureEnabled(@arAudioFeature int audioFeatureType)413 public boolean isAudioFeatureEnabled(@CarAudioFeature int audioFeatureType) { 414 switch (audioFeatureType) { 415 case AUDIO_FEATURE_DYNAMIC_ROUTING: 416 return mUseDynamicRouting; 417 case AUDIO_FEATURE_VOLUME_GROUP_MUTING: 418 return mUseCarVolumeGroupMuting; 419 default: 420 throw new IllegalArgumentException("Unknown Audio Feature type: " 421 + audioFeatureType); 422 } 423 } 424 425 /** 426 * @see {@link android.car.media.CarAudioManager#setGroupVolume(int, int, int, int)} 427 */ 428 @Override setGroupVolume(int zoneId, int groupId, int index, int flags)429 public void setGroupVolume(int zoneId, int groupId, int index, int flags) { 430 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 431 callbackGroupVolumeChange(zoneId, groupId, flags); 432 // For legacy stream type based volume control 433 boolean wasMute; 434 if (!mUseDynamicRouting) { 435 mAudioManager.setStreamVolume( 436 CarAudioDynamicRouting.STREAM_TYPES[groupId], index, flags); 437 return; 438 } 439 synchronized (mImplLock) { 440 CarVolumeGroup group = getCarVolumeGroupLocked(zoneId, groupId); 441 wasMute = group.isMuted(); 442 group.setCurrentGainIndex(index); 443 } 444 if (wasMute) { 445 handleMuteChanged(zoneId, groupId, flags); 446 } 447 } 448 handleMuteChanged(int zoneId, int groupId, int flags)449 private void handleMuteChanged(int zoneId, int groupId, int flags) { 450 callbackGroupMuteChanged(zoneId, groupId, flags); 451 mCarVolumeGroupMuting.carMuteChanged(); 452 } 453 callbackGroupVolumeChange(int zoneId, int groupId, int flags)454 private void callbackGroupVolumeChange(int zoneId, int groupId, int flags) { 455 if (mUseDynamicRouting && !isPlaybackOnVolumeGroupActive(zoneId, groupId)) { 456 flags |= FLAG_PLAY_SOUND; 457 } 458 mCarVolumeCallbackHandler.onVolumeGroupChange(zoneId, groupId, flags); 459 } 460 callbackGroupMuteChanged(int zoneId, int groupId, int flags)461 private void callbackGroupMuteChanged(int zoneId, int groupId, int flags) { 462 mCarVolumeCallbackHandler.onGroupMuteChange(zoneId, groupId, flags); 463 } 464 setMasterMute(boolean mute, int flags)465 void setMasterMute(boolean mute, int flags) { 466 AudioManagerHelper.setMasterMute(mAudioManager, mute, flags); 467 468 // Master Mute only applies to primary zone 469 callbackMasterMuteChange(PRIMARY_AUDIO_ZONE, flags); 470 } 471 callbackMasterMuteChange(int zoneId, int flags)472 void callbackMasterMuteChange(int zoneId, int flags) { 473 mCarVolumeCallbackHandler.onMasterMuteChanged(zoneId, flags); 474 475 // Persists master mute state if applicable 476 if (mPersistMasterMuteState) { 477 mCarAudioSettings.storeMasterMute(isMasterMute(mAudioManager)); 478 } 479 } 480 481 /** 482 * @see {@link android.car.media.CarAudioManager#getGroupMaxVolume(int, int)} 483 */ 484 @Override getGroupMaxVolume(int zoneId, int groupId)485 public int getGroupMaxVolume(int zoneId, int groupId) { 486 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 487 488 if (!mUseDynamicRouting) { 489 return mAudioManager.getStreamMaxVolume( 490 CarAudioDynamicRouting.STREAM_TYPES[groupId]); 491 } 492 493 synchronized (mImplLock) { 494 CarVolumeGroup group = getCarVolumeGroupLocked(zoneId, groupId); 495 return group.getMaxGainIndex(); 496 } 497 } 498 499 /** 500 * @see {@link android.car.media.CarAudioManager#getGroupMinVolume(int, int)} 501 */ 502 @Override getGroupMinVolume(int zoneId, int groupId)503 public int getGroupMinVolume(int zoneId, int groupId) { 504 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 505 506 if (!mUseDynamicRouting) { 507 return mAudioManager.getStreamMinVolume( 508 CarAudioDynamicRouting.STREAM_TYPES[groupId]); 509 } 510 511 synchronized (mImplLock) { 512 CarVolumeGroup group = getCarVolumeGroupLocked(zoneId, groupId); 513 return group.getMinGainIndex(); 514 } 515 } 516 517 /** 518 * @see {@link android.car.media.CarAudioManager#getGroupVolume(int, int)} 519 */ 520 @Override getGroupVolume(int zoneId, int groupId)521 public int getGroupVolume(int zoneId, int groupId) { 522 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 523 524 // For legacy stream type based volume control 525 if (!mUseDynamicRouting) { 526 return mAudioManager.getStreamVolume( 527 CarAudioDynamicRouting.STREAM_TYPES[groupId]); 528 } 529 530 synchronized (mImplLock) { 531 CarVolumeGroup group = getCarVolumeGroupLocked(zoneId, groupId); 532 return group.getCurrentGainIndex(); 533 } 534 } 535 536 @GuardedBy("mImplLock") getCarVolumeGroupLocked(int zoneId, int groupId)537 private CarVolumeGroup getCarVolumeGroupLocked(int zoneId, int groupId) { 538 return getCarAudioZoneLocked(zoneId).getVolumeGroup(groupId); 539 } 540 setupLegacyVolumeChangedListener()541 private void setupLegacyVolumeChangedListener() { 542 AudioManagerHelper.registerVolumeAndMuteReceiver(mContext, mLegacyVolumeChangedHelper); 543 } 544 generateCarAudioDeviceInfos()545 private List<CarAudioDeviceInfo> generateCarAudioDeviceInfos() { 546 AudioDeviceInfo[] deviceInfos = mAudioManager.getDevices( 547 AudioManager.GET_DEVICES_OUTPUTS); 548 549 List<CarAudioDeviceInfo> infos = new ArrayList<>(); 550 551 for (int index = 0; index < deviceInfos.length; index++) { 552 if (deviceInfos[index].getType() == AudioDeviceInfo.TYPE_BUS) { 553 infos.add(new CarAudioDeviceInfo(mAudioManager, deviceInfos[index])); 554 } 555 } 556 return infos; 557 } 558 getAllInputDevices()559 private AudioDeviceInfo[] getAllInputDevices() { 560 return mAudioManager.getDevices( 561 AudioManager.GET_DEVICES_INPUTS); 562 } 563 564 @GuardedBy("mImplLock") loadCarAudioConfigurationLocked( List<CarAudioDeviceInfo> carAudioDeviceInfos, AudioDeviceInfo[] inputDevices)565 private SparseArray<CarAudioZone> loadCarAudioConfigurationLocked( 566 List<CarAudioDeviceInfo> carAudioDeviceInfos, AudioDeviceInfo[] inputDevices) { 567 try (InputStream inputStream = new FileInputStream(mCarAudioConfigurationPath)) { 568 CarAudioZonesHelper zonesHelper = new CarAudioZonesHelper(mCarAudioSettings, 569 inputStream, carAudioDeviceInfos, inputDevices, mUseCarVolumeGroupMuting); 570 mAudioZoneIdToOccupantZoneIdMapping = 571 zonesHelper.getCarAudioZoneIdToOccupantZoneIdMapping(); 572 SparseArray<CarAudioZone> zones = zonesHelper.loadAudioZones(); 573 mCarAudioContext = zonesHelper.getCarAudioContext(); 574 return zones; 575 } catch (IOException | XmlPullParserException e) { 576 throw new RuntimeException("Failed to parse audio zone configuration", e); 577 } 578 } 579 580 @GuardedBy("mImplLock") loadVolumeGroupConfigurationWithAudioControlLocked( List<CarAudioDeviceInfo> carAudioDeviceInfos, AudioDeviceInfo[] inputDevices)581 private SparseArray<CarAudioZone> loadVolumeGroupConfigurationWithAudioControlLocked( 582 List<CarAudioDeviceInfo> carAudioDeviceInfos, AudioDeviceInfo[] inputDevices) { 583 AudioControlWrapper audioControlWrapper = getAudioControlWrapperLocked(); 584 if (!(audioControlWrapper instanceof AudioControlWrapperV1)) { 585 throw new IllegalStateException( 586 "Updated version of IAudioControl no longer supports CarAudioZonesHelperLegacy." 587 + " Please provide car_audio_configuration.xml."); 588 } 589 mCarAudioContext = new CarAudioContext(CarAudioContext.getAllContextsInfo()); 590 CarAudioZonesHelperLegacy legacyHelper = new CarAudioZonesHelperLegacy(mContext, 591 mCarAudioContext, R.xml.car_volume_groups, carAudioDeviceInfos, 592 (AudioControlWrapperV1) audioControlWrapper, 593 mCarAudioSettings, inputDevices); 594 return legacyHelper.loadAudioZones(); 595 } 596 597 @GuardedBy("mImplLock") loadCarAudioZonesLocked()598 private void loadCarAudioZonesLocked() { 599 List<CarAudioDeviceInfo> carAudioDeviceInfos = generateCarAudioDeviceInfos(); 600 AudioDeviceInfo[] inputDevices = getAllInputDevices(); 601 602 if (mCarAudioConfigurationPath != null) { 603 mCarAudioZones = loadCarAudioConfigurationLocked(carAudioDeviceInfos, inputDevices); 604 } else { 605 mCarAudioZones = 606 loadVolumeGroupConfigurationWithAudioControlLocked(carAudioDeviceInfos, 607 inputDevices); 608 } 609 610 CarAudioZonesValidator.validate(mCarAudioZones); 611 } 612 613 @GuardedBy("mImplLock") setupDynamicRoutingLocked()614 private void setupDynamicRoutingLocked() { 615 final AudioPolicy.Builder builder = new AudioPolicy.Builder(mContext); 616 builder.setLooper(Looper.getMainLooper()); 617 618 loadCarAudioZonesLocked(); 619 620 mCarVolume = new CarVolume(mCarAudioContext, mClock, 621 mAudioVolumeAdjustmentContextsVersion, mKeyEventTimeoutMs); 622 623 for (int i = 0; i < mCarAudioZones.size(); i++) { 624 CarAudioZone zone = mCarAudioZones.valueAt(i); 625 // Ensure HAL gets our initial value 626 zone.synchronizeCurrentGainIndex(); 627 Slogf.v(TAG, "Processed audio zone: %s", zone); 628 } 629 630 CarAudioDynamicRouting.setupAudioDynamicRouting(builder, mCarAudioZones, 631 mCarAudioContext); 632 633 // Attach the {@link AudioPolicyVolumeCallback} 634 CarAudioPolicyVolumeCallback 635 .addVolumeCallbackToPolicy(builder, mAudioManager, new CarVolumeInfoWrapper(this), 636 mUseCarVolumeGroupMuting); 637 638 639 AudioControlWrapper audioControlWrapper = getAudioControlWrapperLocked(); 640 if (mUseHalDuckingSignals) { 641 if (audioControlWrapper.supportsFeature(AUDIOCONTROL_FEATURE_AUDIO_DUCKING)) { 642 mCarDucking = new CarDucking(mCarAudioZones, audioControlWrapper); 643 } 644 } 645 646 if (mUseCarVolumeGroupMuting) { 647 mCarVolumeGroupMuting = new CarVolumeGroupMuting(mCarAudioZones, audioControlWrapper); 648 } 649 650 // Configure our AudioPolicy to handle focus events. 651 // This gives us the ability to decide which audio focus requests to accept and bypasses 652 // the framework ducking logic. 653 mFocusHandler = CarZonesAudioFocus.createCarZonesAudioFocus(mAudioManager, 654 mContext.getPackageManager(), 655 mCarAudioZones, 656 mCarAudioSettings, 657 mCarDucking, 658 new CarVolumeInfoWrapper(this)); 659 builder.setAudioPolicyFocusListener(mFocusHandler); 660 builder.setIsAudioFocusPolicy(true); 661 662 mAudioPolicy = builder.build(); 663 664 // Connect the AudioPolicy and the focus listener 665 mFocusHandler.setOwningPolicy(this, mAudioPolicy); 666 667 int r = mAudioManager.registerAudioPolicy(mAudioPolicy); 668 if (r != AudioManager.SUCCESS) { 669 throw new RuntimeException("registerAudioPolicy failed " + r); 670 } 671 672 setupOccupantZoneInfo(); 673 } 674 675 @GuardedBy("mImplLock") setupAudioConfigurationCallbackLocked()676 private void setupAudioConfigurationCallbackLocked() { 677 mCarAudioPlaybackCallback = 678 new CarAudioPlaybackCallback(getCarAudioZone(PRIMARY_AUDIO_ZONE), 679 mClock, mKeyEventTimeoutMs); 680 mAudioManager.registerAudioPlaybackCallback(mCarAudioPlaybackCallback, null); 681 } 682 setupOccupantZoneInfo()683 private void setupOccupantZoneInfo() { 684 CarOccupantZoneService occupantZoneService; 685 CarOccupantZoneManager occupantZoneManager; 686 SparseIntArray audioZoneIdToOccupantZoneMapping; 687 OccupantZoneConfigChangeListener listener; 688 synchronized (mImplLock) { 689 audioZoneIdToOccupantZoneMapping = mAudioZoneIdToOccupantZoneIdMapping; 690 occupantZoneService = mOccupantZoneService; 691 occupantZoneManager = mOccupantZoneManager; 692 listener = mOccupantZoneConfigChangeListener; 693 } 694 occupantZoneService.setAudioZoneIdsForOccupantZoneIds(audioZoneIdToOccupantZoneMapping); 695 occupantZoneManager.registerOccupantZoneConfigChangeListener(listener); 696 } 697 698 @GuardedBy("mImplLock") setupHalAudioFocusListenerLocked()699 private void setupHalAudioFocusListenerLocked() { 700 AudioControlWrapper audioControlWrapper = getAudioControlWrapperLocked(); 701 if (!audioControlWrapper.supportsFeature(AUDIOCONTROL_FEATURE_AUDIO_FOCUS)) { 702 Slogf.d(TAG, "HalAudioFocus is not supported on this device"); 703 return; 704 } 705 706 mHalAudioFocus = new HalAudioFocus(mAudioManager, mAudioControlWrapper, getAudioZoneIds()); 707 mHalAudioFocus.registerFocusListener(); 708 } 709 710 @GuardedBy("mImplLock") setupHalAudioGainCallbackLocked()711 private void setupHalAudioGainCallbackLocked() { 712 AudioControlWrapper audioControlWrapper = getAudioControlWrapperLocked(); 713 if (!audioControlWrapper.supportsFeature(AUDIOCONTROL_FEATURE_AUDIO_GAIN_CALLBACK)) { 714 Slogf.d(CarLog.TAG_AUDIO, "HalAudioGainCallback is not supported on this device"); 715 return; 716 } 717 mCarAudioGainMonitor = new CarAudioGainMonitor(mAudioControlWrapper, mCarAudioZones); 718 mCarAudioGainMonitor.registerAudioGainListener(mHalAudioGainCallback); 719 } 720 721 /** 722 * Read from {@link #AUDIO_CONFIGURATION_PATHS} respectively. 723 * @return File path of the first hit in {@link #AUDIO_CONFIGURATION_PATHS} 724 */ 725 @Nullable getAudioConfigurationPath()726 private static String getAudioConfigurationPath() { 727 for (String path : AUDIO_CONFIGURATION_PATHS) { 728 File configuration = new File(path); 729 if (configuration.exists()) { 730 return path; 731 } 732 } 733 return null; 734 } 735 736 @Override setFadeTowardFront(float value)737 public void setFadeTowardFront(float value) { 738 synchronized (mImplLock) { 739 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 740 getAudioControlWrapperLocked().setFadeTowardFront(value); 741 } 742 } 743 744 @Override setBalanceTowardRight(float value)745 public void setBalanceTowardRight(float value) { 746 synchronized (mImplLock) { 747 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 748 getAudioControlWrapperLocked().setBalanceTowardRight(value); 749 } 750 } 751 752 /** 753 * @return Array of accumulated device addresses, empty array if we found nothing 754 */ 755 @Override getExternalSources()756 public @NonNull String[] getExternalSources() { 757 synchronized (mImplLock) { 758 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 759 List<String> sourceAddresses = new ArrayList<>(); 760 761 AudioDeviceInfo[] devices = mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS); 762 if (devices.length == 0) { 763 Slogf.w(TAG, "getExternalSources, no input devices found"); 764 } 765 766 // Collect the list of non-microphone input ports 767 for (AudioDeviceInfo info : devices) { 768 switch (info.getType()) { 769 // TODO: Can we trim this set down? Especially duplicates like FM vs FM_TUNER? 770 case AudioDeviceInfo.TYPE_FM: 771 case AudioDeviceInfo.TYPE_FM_TUNER: 772 case AudioDeviceInfo.TYPE_TV_TUNER: 773 case AudioDeviceInfo.TYPE_HDMI: 774 case AudioDeviceInfo.TYPE_AUX_LINE: 775 case AudioDeviceInfo.TYPE_LINE_ANALOG: 776 case AudioDeviceInfo.TYPE_LINE_DIGITAL: 777 case AudioDeviceInfo.TYPE_USB_ACCESSORY: 778 case AudioDeviceInfo.TYPE_USB_DEVICE: 779 case AudioDeviceInfo.TYPE_USB_HEADSET: 780 case AudioDeviceInfo.TYPE_IP: 781 case AudioDeviceInfo.TYPE_BUS: 782 String address = info.getAddress(); 783 if (TextUtils.isEmpty(address)) { 784 Slogf.w(TAG, "Discarded device with empty address, type=%d", 785 info.getType()); 786 } else { 787 sourceAddresses.add(address); 788 } 789 } 790 } 791 792 return sourceAddresses.toArray(new String[0]); 793 } 794 } 795 796 @Override createAudioPatch(String sourceAddress, @AttributeUsage int usage, int gainInMillibels)797 public CarAudioPatchHandle createAudioPatch(String sourceAddress, 798 @AttributeUsage int usage, int gainInMillibels) { 799 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 800 enforceCanUseAudioPatchAPI(); 801 synchronized (mImplLock) { 802 return createAudioPatchLocked(sourceAddress, usage, gainInMillibels); 803 } 804 } 805 806 @Override releaseAudioPatch(CarAudioPatchHandle carPatch)807 public void releaseAudioPatch(CarAudioPatchHandle carPatch) { 808 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 809 enforceCanUseAudioPatchAPI(); 810 synchronized (mImplLock) { 811 releaseAudioPatchLocked(carPatch); 812 } 813 } 814 enforceCanUseAudioPatchAPI()815 private void enforceCanUseAudioPatchAPI() { 816 if (!areAudioPatchAPIsEnabled()) { 817 throw new IllegalStateException("Audio Patch APIs not enabled, see " 818 + PROPERTY_RO_ENABLE_AUDIO_PATCH); 819 } 820 } 821 areAudioPatchAPIsEnabled()822 private boolean areAudioPatchAPIsEnabled() { 823 return SystemProperties.getBoolean(PROPERTY_RO_ENABLE_AUDIO_PATCH, /* default= */ false); 824 } 825 826 @GuardedBy("mImplLock") createAudioPatchLocked(String sourceAddress, @AttributeUsage int usage, int gainInMillibels)827 private CarAudioPatchHandle createAudioPatchLocked(String sourceAddress, 828 @AttributeUsage int usage, int gainInMillibels) { 829 // Find the named source port 830 AudioDeviceInfo sourcePortInfo = null; 831 AudioDeviceInfo[] deviceInfos = mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS); 832 for (AudioDeviceInfo info : deviceInfos) { 833 if (sourceAddress.equals(info.getAddress())) { 834 // This is the one for which we're looking 835 sourcePortInfo = info; 836 break; 837 } 838 } 839 Objects.requireNonNull(sourcePortInfo, 840 "Specified source is not available: " + sourceAddress); 841 842 AudioAttributes audioAttributes = CarAudioContext.getAudioAttributeFromUsage(usage); 843 844 AudioPatchInfo audioPatchInfo = AudioManagerHelper.createAudioPatch(sourcePortInfo, 845 getOutputDeviceForAudioAttributeLocked(PRIMARY_AUDIO_ZONE, audioAttributes), 846 gainInMillibels); 847 848 Slogf.d(TAG, "Audio patch created: %s", audioPatchInfo); 849 850 // Ensure the initial volume on output device port 851 int groupId = getVolumeGroupIdForAudioAttributeLocked(PRIMARY_AUDIO_ZONE, audioAttributes); 852 setGroupVolume(PRIMARY_AUDIO_ZONE, groupId, 853 getGroupVolume(PRIMARY_AUDIO_ZONE, groupId), 0); 854 855 return new CarAudioPatchHandle(audioPatchInfo.getHandleId(), 856 audioPatchInfo.getSourceAddress(), audioPatchInfo.getSinkAddress()); 857 } 858 859 @GuardedBy("mImplLock") releaseAudioPatchLocked(CarAudioPatchHandle carPatch)860 private void releaseAudioPatchLocked(CarAudioPatchHandle carPatch) { 861 Objects.requireNonNull(carPatch); 862 863 if (AudioManagerHelper.releaseAudioPatch(mAudioManager, getAudioPatchInfo(carPatch))) { 864 Slogf.d(TAG, "releaseAudioPatch %s successfully", carPatch); 865 } 866 // If we didn't find a match, then something went awry, but it's probably not fatal... 867 Slogf.e(TAG, "releaseAudioPatch found no match for %s", carPatch); 868 } 869 getAudioPatchInfo(CarAudioPatchHandle carPatch)870 private static AudioPatchInfo getAudioPatchInfo(CarAudioPatchHandle carPatch) { 871 return new AudioPatchInfo(carPatch.getSourceAddress(), 872 carPatch.getSinkAddress(), 873 carPatch.getHandleId()); 874 } 875 876 @Override getVolumeGroupCount(int zoneId)877 public int getVolumeGroupCount(int zoneId) { 878 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 879 880 if (!mUseDynamicRouting) { 881 return CarAudioDynamicRouting.STREAM_TYPES.length; 882 } 883 884 synchronized (mImplLock) { 885 return getCarAudioZoneLocked(zoneId).getVolumeGroupCount(); 886 } 887 } 888 889 @Override getVolumeGroupIdForUsage(int zoneId, @AttributeUsage int usage)890 public int getVolumeGroupIdForUsage(int zoneId, @AttributeUsage int usage) { 891 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 892 if (!CarAudioContext.isValidAudioAttributeUsage(usage)) { 893 return INVALID_VOLUME_GROUP_ID; 894 } 895 896 synchronized (mImplLock) { 897 return getVolumeGroupIdForAudioAttributeLocked(zoneId, 898 CarAudioContext.getAudioAttributeFromUsage(usage)); 899 } 900 } 901 902 @Override getVolumeGroupInfo(int zoneId, int groupId)903 public CarVolumeGroupInfo getVolumeGroupInfo(int zoneId, int groupId) { 904 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 905 if (!mUseDynamicRouting) { 906 return null; 907 } 908 synchronized (mImplLock) { 909 return getCarVolumeGroupLocked(zoneId, groupId).getCarVolumeGroupInfo(); 910 } 911 } 912 913 @Override getVolumeGroupInfosForZone(int zoneId)914 public List<CarVolumeGroupInfo> getVolumeGroupInfosForZone(int zoneId) { 915 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 916 if (!mUseDynamicRouting) { 917 return Collections.EMPTY_LIST; 918 } 919 synchronized (mImplLock) { 920 return getCarAudioZoneLocked(zoneId).getVolumeGroupInfos(); 921 } 922 } 923 924 @Override getAudioAttributesForVolumeGroup(CarVolumeGroupInfo groupInfo)925 public List<AudioAttributes> getAudioAttributesForVolumeGroup(CarVolumeGroupInfo groupInfo) { 926 Objects.requireNonNull(groupInfo, "Car volume group info can not be null"); 927 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 928 if (!mUseDynamicRouting) { 929 return Collections.EMPTY_LIST; 930 } 931 932 synchronized (mImplLock) { 933 return getCarAudioZoneLocked(groupInfo.getZoneId()) 934 .getVolumeGroup(groupInfo.getId()).getAudioAttributes(); 935 } 936 } 937 938 @GuardedBy("mImplLock") getVolumeGroupIdForAudioAttributeLocked(int zoneId, AudioAttributes audioAttributes)939 private int getVolumeGroupIdForAudioAttributeLocked(int zoneId, 940 AudioAttributes audioAttributes) { 941 if (!mUseDynamicRouting) { 942 return getStreamTypeFromAudioAttribute(audioAttributes); 943 } 944 945 @AudioContext int audioContext = 946 mCarAudioContext.getContextForAudioAttribute(audioAttributes); 947 return getVolumeGroupIdForAudioContextLocked(zoneId, audioContext); 948 } 949 getStreamTypeFromAudioAttribute(AudioAttributes audioAttributes)950 private static int getStreamTypeFromAudioAttribute(AudioAttributes audioAttributes) { 951 int usage = audioAttributes.getSystemUsage(); 952 for (int i = 0; i < CarAudioDynamicRouting.STREAM_TYPE_USAGES.length; i++) { 953 if (usage == CarAudioDynamicRouting.STREAM_TYPE_USAGES[i]) { 954 return i; 955 } 956 } 957 958 return INVALID_VOLUME_GROUP_ID; 959 } 960 961 @GuardedBy("mImplLock") getVolumeGroupIdForAudioContextLocked(int zoneId, @AudioContext int audioContext)962 private int getVolumeGroupIdForAudioContextLocked(int zoneId, @AudioContext int audioContext) { 963 CarVolumeGroup[] groups = getCarAudioZoneLocked(zoneId).getVolumeGroups(); 964 for (int i = 0; i < groups.length; i++) { 965 int[] groupAudioContexts = groups[i].getContexts(); 966 for (int groupAudioContext : groupAudioContexts) { 967 if (audioContext == groupAudioContext) { 968 return i; 969 } 970 } 971 } 972 return INVALID_VOLUME_GROUP_ID; 973 } 974 975 @Override getUsagesForVolumeGroupId(int zoneId, int groupId)976 public @NonNull int[] getUsagesForVolumeGroupId(int zoneId, int groupId) { 977 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 978 979 if (!mUseDynamicRouting) { 980 return new int[] { CarAudioDynamicRouting.STREAM_TYPE_USAGES[groupId] }; 981 } 982 synchronized (mImplLock) { 983 CarVolumeGroup group = getCarVolumeGroupLocked(zoneId, groupId); 984 int[] contexts = group.getContexts(); 985 List<Integer> usages = new ArrayList<>(); 986 for (int index = 0; index < contexts.length; index++) { 987 AudioAttributes[] attributesForContext = 988 mCarAudioContext.getAudioAttributesForContext(contexts[index]); 989 for (int counter = 0; counter < attributesForContext.length; counter++) { 990 usages.add(attributesForContext[counter].getSystemUsage()); 991 } 992 } 993 994 int[] usagesArray = CarServiceUtils.toIntArray(usages); 995 996 return usagesArray; 997 } 998 } 999 1000 @Override isPlaybackOnVolumeGroupActive(int zoneId, int groupId)1001 public boolean isPlaybackOnVolumeGroupActive(int zoneId, int groupId) { 1002 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 1003 requireDynamicRouting(); 1004 Preconditions.checkArgument(isAudioZoneIdValid(zoneId), 1005 "Invalid audio zone id %d", zoneId); 1006 1007 CarVolume carVolume; 1008 synchronized (mImplLock) { 1009 carVolume = mCarVolume; 1010 } 1011 return carVolume.isAnyContextActive(getContextsForVolumeGroupId(zoneId, groupId), 1012 getActiveAttributesFromPlaybackConfigurations(zoneId), 1013 getCallStateForZone(zoneId), getActiveHalAudioAttributesForZone(zoneId)); 1014 } 1015 1016 /** 1017 * 1018 * returns the current call state ({@code CALL_STATE_OFFHOOK}, {@code CALL_STATE_RINGING}, 1019 * {@code CALL_STATE_IDLE}) from the telephony manager. 1020 */ getCallStateForZone(int zoneId)1021 int getCallStateForZone(int zoneId) { 1022 synchronized (mImplLock) { 1023 // Only driver can use telephony stack 1024 if (getUserIdForZoneLocked(zoneId) == mOccupantZoneService.getDriverUserId()) { 1025 return mTelephonyManager.getCallState(); 1026 } 1027 } 1028 return TelephonyManager.CALL_STATE_IDLE; 1029 } 1030 getActiveAttributesFromPlaybackConfigurations(int zoneId)1031 private List<AudioAttributes> getActiveAttributesFromPlaybackConfigurations(int zoneId) { 1032 return getCarAudioZone(zoneId) 1033 .findActiveAudioAttributesFromPlaybackConfigurations(mAudioManager 1034 .getActivePlaybackConfigurations()); 1035 } 1036 1037 getContextsForVolumeGroupId(int zoneId, int groupId)1038 private @NonNull @AudioContext int[] getContextsForVolumeGroupId(int zoneId, int groupId) { 1039 synchronized (mImplLock) { 1040 CarVolumeGroup group = getCarVolumeGroupLocked(zoneId, groupId); 1041 return group.getContexts(); 1042 } 1043 } 1044 1045 /** 1046 * Gets the ids of all available audio zones 1047 * 1048 * @return Array of available audio zones ids 1049 */ 1050 @Override getAudioZoneIds()1051 public @NonNull int[] getAudioZoneIds() { 1052 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1053 requireDynamicRouting(); 1054 synchronized (mImplLock) { 1055 int[] zoneIds = new int[mCarAudioZones.size()]; 1056 for (int i = 0; i < mCarAudioZones.size(); i++) { 1057 zoneIds[i] = mCarAudioZones.keyAt(i); 1058 } 1059 return zoneIds; 1060 } 1061 } 1062 1063 /** 1064 * Gets the audio zone id currently mapped to uid, 1065 * 1066 * <p><b>Note:</b> Will use uid mapping first, followed by uid's {@userId} mapping. 1067 * defaults to PRIMARY_AUDIO_ZONE if no mapping exist 1068 * 1069 * @param uid The uid 1070 * @return zone id mapped to uid 1071 */ 1072 @Override getZoneIdForUid(int uid)1073 public int getZoneIdForUid(int uid) { 1074 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1075 requireDynamicRouting(); 1076 synchronized (mImplLock) { 1077 if (mUidToZoneMap.containsKey(uid)) { 1078 return mUidToZoneMap.get(uid); 1079 } 1080 int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); 1081 return getZoneIdForUserIdLocked(userId); 1082 } 1083 } 1084 1085 @GuardedBy("mImplLock") getZoneIdForUserIdLocked(@serIdInt int userId)1086 private int getZoneIdForUserIdLocked(@UserIdInt int userId) { 1087 int audioZoneId = mOccupantZoneService.getAudioZoneIdForOccupant( 1088 mOccupantZoneService.getOccupantZoneIdForUserId(userId)); 1089 if (audioZoneId != CarAudioManager.INVALID_AUDIO_ZONE) { 1090 return audioZoneId; 1091 } 1092 Slogf.w(TAG, 1093 "getZoneIdForUid userId %d does not have a zone. Defaulting to %s: %d", 1094 userId, "PRIMARY_AUDIO_ZONE", PRIMARY_AUDIO_ZONE); 1095 return PRIMARY_AUDIO_ZONE; 1096 } 1097 1098 /** 1099 * Maps the audio zone id to uid 1100 * 1101 * @param zoneId The audio zone id 1102 * @param uid The uid to map 1103 * 1104 * <p><b>Note:</b> Will throw if occupant zone mapping exist, as uid and occupant zone mapping 1105 * do not work in conjunction. 1106 * 1107 * @return true if the device affinities, for devices in zone, are successfully set 1108 */ 1109 @Override setZoneIdForUid(int zoneId, int uid)1110 public boolean setZoneIdForUid(int zoneId, int uid) { 1111 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1112 requireDynamicRouting(); 1113 synchronized (mImplLock) { 1114 checkAudioZoneIdLocked(zoneId); 1115 Slogf.i(TAG, "setZoneIdForUid Calling uid %d mapped to : %d", uid, zoneId); 1116 1117 // If occupant mapping exist uid routing can not be used 1118 requiredOccupantZoneMappingDisabledLocked(); 1119 1120 // Figure out if anything is currently holding focus, 1121 // This will change the focus to transient loss while we are switching zones 1122 Integer currentZoneId = mUidToZoneMap.get(uid); 1123 ArrayList<AudioFocusInfo> currentFocusHoldersForUid = new ArrayList<>(); 1124 ArrayList<AudioFocusInfo> currentFocusLosersForUid = new ArrayList<>(); 1125 if (currentZoneId != null) { 1126 currentFocusHoldersForUid = mFocusHandler.getAudioFocusHoldersForUid(uid, 1127 currentZoneId.intValue()); 1128 currentFocusLosersForUid = mFocusHandler.getAudioFocusLosersForUid(uid, 1129 currentZoneId.intValue()); 1130 if (!currentFocusHoldersForUid.isEmpty() || !currentFocusLosersForUid.isEmpty()) { 1131 // Order matters here: Remove the focus losers first 1132 // then do the current holder to prevent loser from popping up while 1133 // the focus is being remove for current holders 1134 // Remove focus for current focus losers 1135 mFocusHandler.transientlyLoseInFocusInZone(currentFocusLosersForUid, 1136 currentZoneId.intValue()); 1137 // Remove focus for current holders 1138 mFocusHandler.transientlyLoseInFocusInZone(currentFocusHoldersForUid, 1139 currentZoneId.intValue()); 1140 } 1141 } 1142 1143 // if the current uid is in the list 1144 // remove it from the list 1145 1146 if (checkAndRemoveUidLocked(uid)) { 1147 if (setZoneIdForUidNoCheckLocked(zoneId, uid)) { 1148 // Order matters here: Regain focus for 1149 // Previously lost focus holders then regain 1150 // focus for holders that had it last 1151 // Regain focus for the focus losers from previous zone 1152 if (!currentFocusLosersForUid.isEmpty()) { 1153 regainAudioFocusLocked(currentFocusLosersForUid, zoneId); 1154 } 1155 // Regain focus for the focus holders from previous zone 1156 if (!currentFocusHoldersForUid.isEmpty()) { 1157 regainAudioFocusLocked(currentFocusHoldersForUid, zoneId); 1158 } 1159 return true; 1160 } 1161 } 1162 return false; 1163 } 1164 } 1165 1166 @GuardedBy("mImplLock") getOutputDeviceForAudioAttributeLocked(int zoneId, AudioAttributes audioAttributes)1167 private AudioDeviceInfo getOutputDeviceForAudioAttributeLocked(int zoneId, 1168 AudioAttributes audioAttributes) { 1169 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1170 requireDynamicRouting(); 1171 int contextForUsage = mCarAudioContext.getContextForAudioAttribute(audioAttributes); 1172 Preconditions.checkArgument(!CarAudioContext.isInvalidContextId(contextForUsage), 1173 "Invalid audio attribute usage %d", audioAttributes); 1174 return getCarAudioZoneLocked(zoneId).getAudioDeviceForContext(contextForUsage); 1175 } 1176 1177 @Override getOutputDeviceAddressForUsage(int zoneId, @AttributeUsage int usage)1178 public String getOutputDeviceAddressForUsage(int zoneId, @AttributeUsage int usage) { 1179 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1180 requireDynamicRouting(); 1181 CarAudioContext.checkAudioAttributeUsage(usage); 1182 int contextForUsage = getCarAudioContext() 1183 .getContextForAudioAttribute(CarAudioContext.getAudioAttributeFromUsage(usage)); 1184 return getCarAudioZone(zoneId).getAddressForContext(contextForUsage); 1185 } 1186 1187 /** 1188 * Regain focus for the focus list passed in 1189 * @param afiList focus info list to regain 1190 * @param zoneId zone id where the focus holder belong 1191 */ 1192 @GuardedBy("mImplLock") regainAudioFocusLocked(ArrayList<AudioFocusInfo> afiList, int zoneId)1193 void regainAudioFocusLocked(ArrayList<AudioFocusInfo> afiList, int zoneId) { 1194 for (AudioFocusInfo info : afiList) { 1195 if (mFocusHandler.reevaluateAndRegainAudioFocus(info) 1196 != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { 1197 Slogf.i(TAG, 1198 " Focus could not be granted for entry %s uid %d in zone %d", 1199 info.getClientId(), info.getClientUid(), zoneId); 1200 } 1201 } 1202 } 1203 1204 /** 1205 * Removes the current mapping of the uid, focus will be lost in zone 1206 * @param uid The uid to remove 1207 * 1208 * <p><b>Note:</b> Will throw if occupant zone mapping exist, as uid and occupant zone mapping 1209 * do not work in conjunction. 1210 * 1211 * return true if all the devices affinities currently 1212 * mapped to uid are successfully removed 1213 */ 1214 @Override clearZoneIdForUid(int uid)1215 public boolean clearZoneIdForUid(int uid) { 1216 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1217 requireDynamicRouting(); 1218 synchronized (mImplLock) { 1219 // Throw so as to not set the wrong expectation, 1220 // that routing will be changed if clearZoneIdForUid is called. 1221 requiredOccupantZoneMappingDisabledLocked(); 1222 1223 return checkAndRemoveUidLocked(uid); 1224 } 1225 } 1226 1227 /** 1228 * Sets the zone id for uid 1229 * @param zoneId zone id to map to uid 1230 * @param uid uid to map 1231 * @return true if setting uid device affinity is successful 1232 */ 1233 @GuardedBy("mImplLock") setZoneIdForUidNoCheckLocked(int zoneId, int uid)1234 private boolean setZoneIdForUidNoCheckLocked(int zoneId, int uid) { 1235 Slogf.d(TAG, "setZoneIdForUidNoCheck Calling uid %d mapped to %d", uid, zoneId); 1236 //Request to add uid device affinity 1237 List<AudioDeviceInfo> deviceInfos = getCarAudioZoneLocked(zoneId).getAudioDeviceInfos(); 1238 if (mAudioPolicy.setUidDeviceAffinity(uid, deviceInfos)) { 1239 // TODO do not store uid mapping here instead use the uid 1240 // device affinity in audio policy when available 1241 mUidToZoneMap.put(uid, zoneId); 1242 return true; 1243 } 1244 Slogf.w(TAG, "setZoneIdForUidNoCheck Failed set device affinity for uid %d in zone %d", 1245 uid, zoneId); 1246 return false; 1247 } 1248 1249 /** 1250 * Check if uid is attached to a zone and remove it 1251 * @param uid unique id to remove 1252 * @return true if the uid was successfully removed or mapping was not assigned 1253 */ 1254 @GuardedBy("mImplLock") checkAndRemoveUidLocked(int uid)1255 private boolean checkAndRemoveUidLocked(int uid) { 1256 Integer zoneId = mUidToZoneMap.get(uid); 1257 if (zoneId != null) { 1258 Slogf.i(TAG, "checkAndRemoveUid removing Calling uid %d from zone %d", uid, zoneId); 1259 if (mAudioPolicy.removeUidDeviceAffinity(uid)) { 1260 // TODO use the uid device affinity in audio policy when available 1261 mUidToZoneMap.remove(uid); 1262 return true; 1263 } 1264 //failed to remove device affinity from zone devices 1265 Slogf.w(TAG, "checkAndRemoveUid Failed remove device affinity for uid %d in zone %d", 1266 uid, zoneId); 1267 return false; 1268 } 1269 return true; 1270 } 1271 1272 @Override registerVolumeCallback(@onNull IBinder binder)1273 public void registerVolumeCallback(@NonNull IBinder binder) { 1274 synchronized (mImplLock) { 1275 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 1276 mCarVolumeCallbackHandler.registerCallback(binder); 1277 } 1278 } 1279 1280 @Override unregisterVolumeCallback(@onNull IBinder binder)1281 public void unregisterVolumeCallback(@NonNull IBinder binder) { 1282 synchronized (mImplLock) { 1283 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 1284 mCarVolumeCallbackHandler.unregisterCallback(binder); 1285 } 1286 } 1287 1288 /** 1289 * @see {@link android.car.media.CarAudioManager#isVolumeGroupMuted(int, int)} 1290 */ 1291 @Override isVolumeGroupMuted(int zoneId, int groupId)1292 public boolean isVolumeGroupMuted(int zoneId, int groupId) { 1293 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 1294 requireDynamicRouting(); 1295 if (!mUseCarVolumeGroupMuting) { 1296 return false; 1297 } 1298 synchronized (mImplLock) { 1299 CarVolumeGroup group = getCarVolumeGroupLocked(zoneId, groupId); 1300 return group.isMuted(); 1301 } 1302 } 1303 1304 /** 1305 * @see {@link android.car.media.CarAudioManager#setVolumeGroupMute(int, int, boolean, int)} 1306 */ 1307 @Override setVolumeGroupMute(int zoneId, int groupId, boolean mute, int flags)1308 public void setVolumeGroupMute(int zoneId, int groupId, boolean mute, int flags) { 1309 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 1310 requireDynamicRouting(); 1311 requireVolumeGroupMuting(); 1312 synchronized (mImplLock) { 1313 CarVolumeGroup group = getCarVolumeGroupLocked(zoneId, groupId); 1314 group.setMute(mute); 1315 } 1316 handleMuteChanged(zoneId, groupId, flags); 1317 } 1318 1319 @Override getInputDevicesForZoneId(int zoneId)1320 public @NonNull List<AudioDeviceAttributes> getInputDevicesForZoneId(int zoneId) { 1321 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS); 1322 requireDynamicRouting(); 1323 1324 return getCarAudioZone(zoneId).getInputAudioDevices(); 1325 } 1326 setAudioEnabled(boolean isAudioEnabled)1327 void setAudioEnabled(boolean isAudioEnabled) { 1328 Slogf.d(TAG, "Setting isAudioEnabled to %b", isAudioEnabled); 1329 1330 mFocusHandler.setRestrictFocus(/* isFocusRestricted= */ !isAudioEnabled); 1331 if (mUseCarVolumeGroupMuting) { 1332 mCarVolumeGroupMuting.setRestrictMuting(/* isMutingRestricted= */ !isAudioEnabled); 1333 } 1334 // TODO(b/176258537) if not using group volume, then set master mute accordingly 1335 } 1336 enforcePermission(String permissionName)1337 private void enforcePermission(String permissionName) { 1338 if (mContext.checkCallingOrSelfPermission(permissionName) 1339 != PackageManager.PERMISSION_GRANTED) { 1340 throw new SecurityException("requires permission " + permissionName); 1341 } 1342 } 1343 requireDynamicRouting()1344 private void requireDynamicRouting() { 1345 Preconditions.checkState(mUseDynamicRouting, "Dynamic routing is required"); 1346 } 1347 requireVolumeGroupMuting()1348 private void requireVolumeGroupMuting() { 1349 Preconditions.checkState(mUseCarVolumeGroupMuting, 1350 "Car Volume Group Muting is required"); 1351 } 1352 1353 @GuardedBy("mImplLock") requiredOccupantZoneMappingDisabledLocked()1354 private void requiredOccupantZoneMappingDisabledLocked() { 1355 if (isOccupantZoneMappingAvailableLocked()) { 1356 throw new IllegalStateException( 1357 "UID based routing is not supported while using occupant zone mapping"); 1358 } 1359 } 1360 getSuggestedAudioContextForPrimaryZone()1361 @AudioContext int getSuggestedAudioContextForPrimaryZone() { 1362 int zoneId = PRIMARY_AUDIO_ZONE; 1363 CarVolume carVolume; 1364 synchronized (mImplLock) { 1365 carVolume = mCarVolume; 1366 } 1367 return carVolume.getSuggestedAudioContextAndSaveIfFound( 1368 getAllActiveAttributesForPrimaryZone(), getCallStateForZone(zoneId), 1369 getActiveHalAudioAttributesForZone(zoneId)); 1370 } 1371 getActiveHalAudioAttributesForZone(int zoneId)1372 private List<AudioAttributes> getActiveHalAudioAttributesForZone(int zoneId) { 1373 if (mHalAudioFocus == null) { 1374 return new ArrayList<>(0); 1375 } 1376 return mHalAudioFocus.getActiveAudioAttributesForZone(zoneId); 1377 } 1378 1379 /** 1380 * Gets volume group by a given legacy stream type 1381 * @param streamType Legacy stream type such as {@link AudioManager#STREAM_MUSIC} 1382 * @return volume group id mapped from stream type 1383 */ getVolumeGroupIdForStreamType(int streamType)1384 private int getVolumeGroupIdForStreamType(int streamType) { 1385 int groupId = INVALID_VOLUME_GROUP_ID; 1386 for (int i = 0; i < CarAudioDynamicRouting.STREAM_TYPES.length; i++) { 1387 if (streamType == CarAudioDynamicRouting.STREAM_TYPES[i]) { 1388 groupId = i; 1389 break; 1390 } 1391 } 1392 return groupId; 1393 } 1394 handleOccupantZoneUserChanged()1395 private void handleOccupantZoneUserChanged() { 1396 int driverUserId = mOccupantZoneService.getDriverUserId(); 1397 synchronized (mImplLock) { 1398 if (!isOccupantZoneMappingAvailableLocked()) { 1399 adjustZonesToUserIdLocked(driverUserId); 1400 return; 1401 } 1402 int occupantZoneForDriver = getOccupantZoneIdForDriver(); 1403 Set<Integer> assignedZones = new HashSet<Integer>(); 1404 for (int index = 0; index < mAudioZoneIdToOccupantZoneIdMapping.size(); index++) { 1405 int audioZoneId = mAudioZoneIdToOccupantZoneIdMapping.keyAt(index); 1406 int occupantZoneId = mAudioZoneIdToOccupantZoneIdMapping.get(audioZoneId); 1407 assignedZones.add(audioZoneId); 1408 updateUserForOccupantZoneLocked(occupantZoneId, audioZoneId, driverUserId, 1409 occupantZoneForDriver); 1410 } 1411 1412 assignMissingZonesToDriverLocked(driverUserId, assignedZones); 1413 } 1414 restoreVolumeGroupMuteState(); 1415 } 1416 restoreVolumeGroupMuteState()1417 private void restoreVolumeGroupMuteState() { 1418 if (!mUseCarVolumeGroupMuting) { 1419 return; 1420 } 1421 mCarVolumeGroupMuting.carMuteChanged(); 1422 } 1423 1424 @GuardedBy("mImplLock") assignMissingZonesToDriverLocked(@serIdInt int driverUserId, Set<Integer> assignedZones)1425 private void assignMissingZonesToDriverLocked(@UserIdInt int driverUserId, 1426 Set<Integer> assignedZones) { 1427 for (int i = 0; i < mCarAudioZones.size(); i++) { 1428 CarAudioZone zone = mCarAudioZones.valueAt(i); 1429 if (assignedZones.contains(zone.getId())) { 1430 continue; 1431 } 1432 assignUserIdToAudioZoneLocked(zone, driverUserId); 1433 } 1434 } 1435 1436 @GuardedBy("mImplLock") adjustZonesToUserIdLocked(@serIdInt int userId)1437 private void adjustZonesToUserIdLocked(@UserIdInt int userId) { 1438 for (int i = 0; i < mCarAudioZones.size(); i++) { 1439 CarAudioZone zone = mCarAudioZones.valueAt(i); 1440 assignUserIdToAudioZoneLocked(zone, userId); 1441 } 1442 } 1443 1444 @GuardedBy("mImplLock") assignUserIdToAudioZoneLocked(CarAudioZone zone, @UserIdInt int userId)1445 private void assignUserIdToAudioZoneLocked(CarAudioZone zone, @UserIdInt int userId) { 1446 if (userId == getUserIdForZoneLocked(zone.getId())) { 1447 Slogf.d(TAG, "assignUserIdToAudioZone userId(%d) already assigned to audioZoneId(%d)", 1448 userId, zone.getId()); 1449 return; 1450 } 1451 Slogf.d(TAG, "assignUserIdToAudioZone assigning userId(%d) to audioZoneId(%d)", 1452 userId, zone.getId()); 1453 zone.updateVolumeGroupsSettingsForUser(userId); 1454 mFocusHandler.updateUserForZoneId(zone.getId(), userId); 1455 setUserIdForAudioZoneLocked(userId, zone.getId()); 1456 } 1457 1458 @GuardedBy("mImplLock") isOccupantZoneMappingAvailableLocked()1459 private boolean isOccupantZoneMappingAvailableLocked() { 1460 return mAudioZoneIdToOccupantZoneIdMapping.size() > 0; 1461 } 1462 1463 @GuardedBy("mImplLock") updateUserForOccupantZoneLocked(int occupantZoneId, int audioZoneId, @UserIdInt int driverUserId, int occupantZoneForDriver)1464 private void updateUserForOccupantZoneLocked(int occupantZoneId, int audioZoneId, 1465 @UserIdInt int driverUserId, int occupantZoneForDriver) { 1466 CarAudioZone audioZone = getCarAudioZoneLocked(audioZoneId); 1467 int userId = mOccupantZoneService.getUserForOccupant(occupantZoneId); 1468 int prevUserId = getUserIdForZoneLocked(audioZoneId); 1469 1470 if (userId == prevUserId) { 1471 Slogf.d(TAG, "updateUserForOccupantZone userId(%d) already assigned to audioZoneId(%d)", 1472 userId, audioZoneId); 1473 return; 1474 } 1475 Slogf.d(TAG, "updateUserForOccupantZone assigning userId(%d) to audioZoneId(%d)", 1476 userId, audioZoneId); 1477 // If the user has changed, be sure to remove from current routing 1478 // This would be true even if the new user is UserManagerHelper.USER_NULL, 1479 // as that indicates the user has logged out. 1480 removeUserIdDeviceAffinitiesLocked(prevUserId); 1481 1482 if (userId == UserManagerHelper.USER_NULL) { 1483 // Reset zone back to driver user id 1484 resetZoneToDefaultUser(audioZone, driverUserId); 1485 setUserIdForAudioZoneLocked(userId, audioZoneId); 1486 return; 1487 } 1488 1489 // Only set user id device affinities for driver when it is the driver's occupant zone 1490 if (userId != driverUserId || occupantZoneId == occupantZoneForDriver) { 1491 setUserIdDeviceAffinitiesLocked(audioZone, userId, audioZoneId); 1492 } 1493 audioZone.updateVolumeGroupsSettingsForUser(userId); 1494 mFocusHandler.updateUserForZoneId(audioZoneId, userId); 1495 setUserIdForAudioZoneLocked(userId, audioZoneId); 1496 } 1497 getOccupantZoneIdForDriver()1498 private int getOccupantZoneIdForDriver() { 1499 List<CarOccupantZoneManager.OccupantZoneInfo> occupantZoneInfos = 1500 mOccupantZoneManager.getAllOccupantZones(); 1501 for (CarOccupantZoneManager.OccupantZoneInfo info: occupantZoneInfos) { 1502 if (info.occupantType == CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER) { 1503 return info.zoneId; 1504 } 1505 } 1506 return CarOccupantZoneManager.OccupantZoneInfo.INVALID_ZONE_ID; 1507 } 1508 1509 @GuardedBy("mImplLock") setUserIdDeviceAffinitiesLocked(CarAudioZone zone, @UserIdInt int userId, int audioZoneId)1510 private void setUserIdDeviceAffinitiesLocked(CarAudioZone zone, @UserIdInt int userId, 1511 int audioZoneId) { 1512 if (!mAudioPolicy.setUserIdDeviceAffinity(userId, zone.getAudioDeviceInfos())) { 1513 throw new IllegalStateException(String.format( 1514 "setUserIdDeviceAffinity for userId %d in zone %d Failed," 1515 + " could not set audio routing.", 1516 userId, audioZoneId)); 1517 } 1518 } 1519 resetZoneToDefaultUser(CarAudioZone zone, @UserIdInt int driverUserId)1520 private void resetZoneToDefaultUser(CarAudioZone zone, @UserIdInt int driverUserId) { 1521 resetCarZonesAudioFocus(zone.getId(), driverUserId); 1522 zone.updateVolumeGroupsSettingsForUser(driverUserId); 1523 } 1524 resetCarZonesAudioFocus(int audioZoneId, @UserIdInt int driverUserId)1525 private void resetCarZonesAudioFocus(int audioZoneId, @UserIdInt int driverUserId) { 1526 mFocusHandler.updateUserForZoneId(audioZoneId, driverUserId); 1527 } 1528 1529 @GuardedBy("mImplLock") removeUserIdDeviceAffinitiesLocked(@serIdInt int userId)1530 private void removeUserIdDeviceAffinitiesLocked(@UserIdInt int userId) { 1531 Slogf.d(TAG, "removeUserIdDeviceAffinities(%d) Succeeded", userId); 1532 if (userId == UserManagerHelper.USER_NULL) { 1533 return; 1534 } 1535 if (!mAudioPolicy.removeUserIdDeviceAffinity(userId)) { 1536 Slogf.e(TAG, "removeUserIdDeviceAffinities(%d) Failed", userId); 1537 return; 1538 } 1539 } 1540 1541 @GuardedBy("mImplLock") getUserIdForZoneLocked(int audioZoneId)1542 private @UserIdInt int getUserIdForZoneLocked(int audioZoneId) { 1543 return mAudioZoneIdToUserIdMapping.get(audioZoneId, UserManagerHelper.USER_NULL); 1544 } 1545 1546 @GuardedBy("mImplLock") setUserIdForAudioZoneLocked(@serIdInt int userId, int audioZoneId)1547 private void setUserIdForAudioZoneLocked(@UserIdInt int userId, int audioZoneId) { 1548 mAudioZoneIdToUserIdMapping.put(audioZoneId, userId); 1549 } 1550 1551 @GuardedBy("mImplLock") getAudioControlWrapperLocked()1552 private AudioControlWrapper getAudioControlWrapperLocked() { 1553 if (mAudioControlWrapper == null) { 1554 mAudioControlWrapper = AudioControlFactory.newAudioControl(); 1555 mAudioControlWrapper.linkToDeath(this::audioControlDied); 1556 } 1557 return mAudioControlWrapper; 1558 } 1559 resetHalAudioFocus()1560 private void resetHalAudioFocus() { 1561 if (mHalAudioFocus != null) { 1562 mHalAudioFocus.reset(); 1563 mHalAudioFocus.registerFocusListener(); 1564 } 1565 } 1566 resetHalAudioGain()1567 private void resetHalAudioGain() { 1568 if (mCarAudioGainMonitor != null) { 1569 mCarAudioGainMonitor.reset(); 1570 mCarAudioGainMonitor.registerAudioGainListener(mHalAudioGainCallback); 1571 } 1572 } 1573 handleAudioDeviceGainsChangedLocked( List<Integer> halReasons, List<CarAudioGainConfigInfo> gains)1574 private void handleAudioDeviceGainsChangedLocked( 1575 List<Integer> halReasons, List<CarAudioGainConfigInfo> gains) { 1576 mCarAudioGainMonitor.handleAudioDeviceGainsChanged(halReasons, gains); 1577 } 1578 audioControlDied()1579 private void audioControlDied() { 1580 resetHalAudioFocus(); 1581 resetHalAudioGain(); 1582 } 1583 isAudioZoneIdValid(int zoneId)1584 boolean isAudioZoneIdValid(int zoneId) { 1585 synchronized (mImplLock) { 1586 return mCarAudioZones.contains(zoneId); 1587 } 1588 } 1589 getCarAudioZone(int zoneId)1590 private CarAudioZone getCarAudioZone(int zoneId) { 1591 synchronized (mImplLock) { 1592 return getCarAudioZoneLocked(zoneId); 1593 } 1594 } 1595 1596 @GuardedBy("mImplLock") getCarAudioZoneLocked(int zoneId)1597 private CarAudioZone getCarAudioZoneLocked(int zoneId) { 1598 checkAudioZoneIdLocked(zoneId); 1599 return mCarAudioZones.get(zoneId); 1600 } 1601 1602 @GuardedBy("mImplLock") checkAudioZoneIdLocked(int zoneId)1603 private void checkAudioZoneIdLocked(int zoneId) { 1604 Preconditions.checkArgument(mCarAudioZones.contains(zoneId), 1605 "Invalid audio zone Id " + zoneId); 1606 } 1607 getVolumeGroupIdForAudioContext(int zoneId, int suggestedContext)1608 int getVolumeGroupIdForAudioContext(int zoneId, int suggestedContext) { 1609 synchronized (mImplLock) { 1610 return getVolumeGroupIdForAudioContextLocked(zoneId, suggestedContext); 1611 } 1612 } 1613 1614 /** 1615 * Resets the last selected volume context. 1616 */ resetSelectedVolumeContext()1617 public void resetSelectedVolumeContext() { 1618 enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME); 1619 synchronized (mImplLock) { 1620 mCarVolume.resetSelectedVolumeContext(); 1621 mCarAudioPlaybackCallback.resetStillActiveContexts(); 1622 } 1623 } 1624 1625 @VisibleForTesting getCarAudioContext()1626 CarAudioContext getCarAudioContext() { 1627 synchronized (mImplLock) { 1628 return mCarAudioContext; 1629 } 1630 } 1631 1632 @VisibleForTesting requestAudioFocusForTest(AudioFocusInfo audioFocusInfo, int audioFocusResult)1633 void requestAudioFocusForTest(AudioFocusInfo audioFocusInfo, int audioFocusResult) { 1634 mFocusHandler.onAudioFocusRequest(audioFocusInfo, audioFocusResult); 1635 } 1636 1637 private class CarAudioOccupantConfigChangeListener implements OccupantZoneConfigChangeListener { 1638 @Override onOccupantZoneConfigChanged(int flags)1639 public void onOccupantZoneConfigChanged(int flags) { 1640 Slogf.d(TAG, "onOccupantZoneConfigChanged(%d)", flags); 1641 if (((flags & CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER) 1642 == CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER) 1643 || ((flags & CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_DISPLAY) 1644 == CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_DISPLAY)) { 1645 handleOccupantZoneUserChanged(); 1646 } 1647 } 1648 } 1649 getAllActiveAttributesForPrimaryZone()1650 private List<AudioAttributes> getAllActiveAttributesForPrimaryZone() { 1651 synchronized (mImplLock) { 1652 return mCarAudioPlaybackCallback.getAllActiveAudioAttributesForPrimaryZone(); 1653 } 1654 } 1655 getMutedVolumeGroups(int zoneId)1656 List<CarVolumeGroupInfo> getMutedVolumeGroups(int zoneId) { 1657 List<CarVolumeGroupInfo> mutedGroups = new ArrayList<>(); 1658 1659 if (!mUseCarVolumeGroupMuting || !isAudioZoneIdValid(zoneId)) { 1660 return mutedGroups; 1661 } 1662 1663 synchronized (mImplLock) { 1664 int groupCount = getCarAudioZoneLocked(zoneId).getVolumeGroupCount(); 1665 for (int groupId = 0; groupId < groupCount; groupId++) { 1666 CarVolumeGroup group = getCarVolumeGroupLocked(zoneId, groupId); 1667 if (!group.isMuted()) { 1668 continue; 1669 } 1670 1671 mutedGroups.add(group.getCarVolumeGroupInfo()); 1672 } 1673 } 1674 1675 return mutedGroups; 1676 } 1677 getActiveAudioAttributesForZone(int zoneId)1678 List<AudioAttributes> getActiveAudioAttributesForZone(int zoneId) { 1679 List<AudioAttributes> activeAudioAttributes = new ArrayList<>(); 1680 activeAudioAttributes.addAll(getActiveAttributesFromPlaybackConfigurations(zoneId)); 1681 activeAudioAttributes.addAll(getActiveHalAudioAttributesForZone(zoneId)); 1682 1683 return activeAudioAttributes; 1684 } 1685 getVolumeGroupIdForAudioAttribute(int audioZoneId, AudioAttributes attributes)1686 int getVolumeGroupIdForAudioAttribute(int audioZoneId, AudioAttributes attributes) { 1687 Objects.requireNonNull(attributes, "Audio attributes can not be null"); 1688 synchronized (mImplLock) { 1689 checkAudioZoneIdLocked(audioZoneId); 1690 return getVolumeGroupIdForAudioAttributeLocked(audioZoneId, attributes); 1691 } 1692 } 1693 1694 static final class SystemClockWrapper { uptimeMillis()1695 public long uptimeMillis() { 1696 return SystemClock.uptimeMillis(); 1697 } 1698 } 1699 } 1700