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