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