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