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