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