启用 AAOS 路由
xref: /packages/services/Car/service/res/values/config.xml
true
如果设为 false,路由和大部分 CarAudioService 将被停用,并且 AAOS 会回退到 AudioService 的默认行为。
xref: /packages/services/Car/service/src/com/android/car/audio/CarAudioService.java
202 public CarAudioService(Context context) {
203 mContext = context;
204 mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
205 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
206 mUseDynamicRouting = mContext.getResources().getBoolean(R.bool.audioUseDynamicRouting);
207 mPersistMasterMuteState = mContext.getResources().getBoolean(
208 R.bool.audioPersistMasterMuteState);
209 mUidToZoneMap = new HashMap<>();
210 }
211
212 /**
213 * Dynamic routing and volume groups are set only if
214 * {@link #mUseDynamicRouting} is {@code true}. Otherwise, this service runs in legacy mode.
215 */
216 @Override
217 public void init() {
218 synchronized (mImplLock) {
219 if (mUseDynamicRouting) {
220 // Enumerate all output bus device ports
221 AudioDeviceInfo[] deviceInfos = mAudioManager.getDevices(
222 AudioManager.GET_DEVICES_OUTPUTS);
223 if (deviceInfos.length == 0) {
224 Log.e(CarLog.TAG_AUDIO, "No output device available, ignore");
225 return;
226 }
227 SparseArray busToCarAudioDeviceInfo = new SparseArray<>();
228 for (AudioDeviceInfo info : deviceInfos) {
229 Log.v(CarLog.TAG_AUDIO, String.format("output id=%d address=%s type=%s",
230 info.getId(), info.getAddress(), info.getType()));
231 if (info.getType() == AudioDeviceInfo.TYPE_BUS) {
232 final CarAudioDeviceInfo carInfo = new CarAudioDeviceInfo(info);
233 // See also the audio_policy_configuration.xml,
234 // the bus number should be no less than zero.
235 if (carInfo.getBusNumber() >= 0) {
236 busToCarAudioDeviceInfo.put(carInfo.getBusNumber(), carInfo);
237 Log.i(CarLog.TAG_AUDIO, "Valid bus found " + carInfo);
238 }
239 }
240 }
241 setupDynamicRouting(busToCarAudioDeviceInfo);
242 } else {
243 Log.i(CarLog.TAG_AUDIO, "Audio dynamic routing not enabled, run in legacy mode");
244 setupLegacyVolumeChangedListener();
245 }
246
247 // Restore master mute state if applicable
248 if (mPersistMasterMuteState) {
249 boolean storedMasterMute = Settings.Global.getInt(mContext.getContentResolver(),
250 VOLUME_SETTINGS_KEY_MASTER_MUTE, 0) != 0;
251 setMasterMute(storedMasterMute, 0);
252 }
253 }
254 }
解析config.xml文件,如果audioUseDynamicRouting是true,则mUseDynamicRouting也是true,执行init函数的时候将走动态路由策略。
a.通过AudioManager获取到所以输出总线设备
b.过滤出type是AudioDeviceInfo.TYPE_BUS的输出设备,然后重新封装到CarAudioDeviceInfo中
c.设置动态路由
xref: /packages/services/Car/service/src/com/android/car/audio/CarAudioDeviceInfo.java
41 /**
42 * Parse device address. Expected format is BUS%d_%s, address, usage hint
43 * @return valid address (from 0 to positive) or -1 for invalid address.
44 */
45 static int parseDeviceAddress(String address) {
46 String[] words = address.split("_");
47 int addressParsed = -1;
48 if (words[0].toLowerCase().startsWith("bus")) {
49 try {
50 addressParsed = Integer.parseInt(words[0].substring(3));
51 } catch (NumberFormatException e) {
52 //ignore
53 }
54 }
55 if (addressParsed < 0) {
56 return -1;
57 }
58 return addressParsed;
59 }
.....................................
77 CarAudioDeviceInfo(AudioDeviceInfo audioDeviceInfo) {
78 mAudioDeviceInfo = audioDeviceInfo;
79 mBusNumber = parseDeviceAddress(audioDeviceInfo.getAddress());
80 mSampleRate = getMaxSampleRate(audioDeviceInfo);
81 mEncodingFormat = getEncodingFormat(audioDeviceInfo);
82 mChannelCount = getMaxChannels(audioDeviceInfo);
83 final AudioGain audioGain = Preconditions.checkNotNull(
84 getAudioGain(), "No audio gain on device port " + audioDeviceInfo);
85 mDefaultGain = audioGain.defaultValue();
86 mMaxGain = audioGain.maxValue();
87 mMinGain = audioGain.minValue();
88
89 mCurrentGain = -1; // Not initialized till explicitly set
90 }
解析device address获取到busNumber,找到bus和第一个"_"中间的字符截取出来转成int。
xref: /packages/services/Car/service/src/com/android/car/audio/CarAudioService.java
99 private static final String[] AUDIO_CONFIGURATION_PATHS = new String[] {
100 "/vendor/etc/car_audio_configuration.xml",
101 "/system/etc/car_audio_configuration.xml"
102 };
...........................
439 private void setupDynamicRouting(SparseArray busToCarAudioDeviceInfo) {
440 final AudioPolicy.Builder builder = new AudioPolicy.Builder(mContext);
441 builder.setLooper(Looper.getMainLooper());
442
443 mCarAudioConfigurationPath = getAudioConfigurationPath();
444 if (mCarAudioConfigurationPath != null) {
445 try (InputStream inputStream = new FileInputStream(mCarAudioConfigurationPath)) {
446 CarAudioZonesHelper zonesHelper = new CarAudioZonesHelper(mContext, inputStream,
447 busToCarAudioDeviceInfo);
448 mCarAudioZones = zonesHelper.loadAudioZones();
449 } catch (IOException | XmlPullParserException e) {
450 throw new RuntimeException("Failed to parse audio zone configuration", e);
451 }
452 } else {
453 // In legacy mode, context -> bus mapping is done by querying IAudioControl HAL.
454 final IAudioControl audioControl = getAudioControl();
455 if (audioControl == null) {
456 throw new RuntimeException(
457 "Dynamic routing requested but audioControl HAL not available");
458 }
459 CarAudioZonesHelperLegacy legacyHelper = new CarAudioZonesHelperLegacy(mContext,
460 R.xml.car_volume_groups, busToCarAudioDeviceInfo, audioControl);
461 mCarAudioZones = legacyHelper.loadAudioZones();
462 }
463 for (CarAudioZone zone : mCarAudioZones) {
464 if (!zone.validateVolumeGroups()) {
465 throw new RuntimeException("Invalid volume groups configuration");
466 }
467 // Ensure HAL gets our initial value
468 zone.synchronizeCurrentGainIndex();
469 Log.v(CarLog.TAG_AUDIO, "Processed audio zone: " + zone);
470 }
471
472 // Setup dynamic routing rules by usage
473 final CarAudioDynamicRouting dynamicRouting = new CarAudioDynamicRouting(mCarAudioZones);
474 dynamicRouting.setupAudioDynamicRouting(builder);
475
476 // Attach the {@link AudioPolicyVolumeCallback}
477 builder.setAudioPolicyVolumeCallback(mAudioPolicyVolumeCallback);
478
479 if (sUseCarAudioFocus) {
480 // Configure our AudioPolicy to handle focus events.
481 // This gives us the ability to decide which audio focus requests to accept and bypasses
482 // the framework ducking logic.
483 mFocusHandler = new CarZonesAudioFocus(mAudioManager,
484 mContext.getPackageManager(),
485 mCarAudioZones);
486 builder.setAudioPolicyFocusListener(mFocusHandler);
487 builder.setIsAudioFocusPolicy(true);
488 }
489
490 mAudioPolicy = builder.build();
491 if (sUseCarAudioFocus) {
492 // Connect the AudioPolicy and the focus listener
493 mFocusHandler.setOwningPolicy(this, mAudioPolicy);
494 }
495
496 int r = mAudioManager.registerAudioPolicy(mAudioPolicy);
497 if (r != AudioManager.SUCCESS) {
498 throw new RuntimeException("registerAudioPolicy failed " + r);
499 }
500 }
通过getAudioConfigurationPath获取配置文件路径,然后解析配置文件。
xref: /packages/services/Car/service/src/com/android/car/audio/CarAudioZonesHelper.java
64 private static final Map CONTEXT_NAME_MAP;
65
66 static {
67 CONTEXT_NAME_MAP = new HashMap<>();
68 CONTEXT_NAME_MAP.put("music", ContextNumber.MUSIC);
69 CONTEXT_NAME_MAP.put("navigation", ContextNumber.NAVIGATION);
70 CONTEXT_NAME_MAP.put("voice_command", ContextNumber.VOICE_COMMAND);
71 CONTEXT_NAME_MAP.put("call_ring", ContextNumber.CALL_RING);
72 CONTEXT_NAME_MAP.put("call", ContextNumber.CALL);
73 CONTEXT_NAME_MAP.put("alarm", ContextNumber.ALARM);
74 CONTEXT_NAME_MAP.put("notification", ContextNumber.NOTIFICATION);
75 CONTEXT_NAME_MAP.put("system_sound", ContextNumber.SYSTEM_SOUND);
76 }
......................................
232 private void parseVolumeGroupContexts(
233 XmlPullParser parser, CarVolumeGroup group, int busNumber)
234 throws XmlPullParserException, IOException {
235 while (parser.next() != XmlPullParser.END_TAG) {
236 if (parser.getEventType() != XmlPullParser.START_TAG) continue;
237 if (TAG_CONTEXT.equals(parser.getName())) {
238 group.bind(
239 parseContextNumber(parser.getAttributeValue(NAMESPACE, ATTR_CONTEXT_NAME)),
240 busNumber, mBusToCarAudioDeviceInfo.get(busNumber));
241 }
242 // Always skip to upper level since we're at the lowest.
243 skip(parser);
244 }
245 }
......................................
264 private int parseContextNumber(String context) {
265 return CONTEXT_NAME_MAP.getOrDefault(context.toLowerCase(), ContextNumber.INVALID);
266 }
xref: /packages/services/Car/service/src/com/android/car/audio/CarVolumeGroup.java
134 /**
135 * Binds the context number to physical bus number and audio device port information.
136 * Because this may change the groups min/max values, thus invalidating an index computed from
137 * a gain before this call, all calls to this function must happen at startup before any
138 * set/getGainIndex calls.
139 *
140 * @param contextNumber Context number as defined in audio control HAL
141 * @param busNumber Physical bus number for the audio device port
142 * @param info {@link CarAudioDeviceInfo} instance relates to the physical bus
143 */
144 void bind(int contextNumber, int busNumber, CarAudioDeviceInfo info) {
145 if (mBusToCarAudioDeviceInfo.size() == 0) {
146 mStepSize = info.getAudioGain().stepValue();
147 } else {
148 Preconditions.checkArgument(
149 info.getAudioGain().stepValue() == mStepSize,
150 "Gain controls within one group must have same step value");
151 }
152
153 mContextToBus.put(contextNumber, busNumber);
154 mBusToCarAudioDeviceInfo.put(busNumber, info);
155
156 if (info.getDefaultGain() > mDefaultGain) {
157 // We're arbitrarily selecting the highest bus default gain as the group's default.
158 mDefaultGain = info.getDefaultGain();
159 }
160 if (info.getMaxGain() > mMaxGain) {
161 mMaxGain = info.getMaxGain();
162 }
163 if (info.getMinGain() < mMinGain) {
164 mMinGain = info.getMinGain();
165 }
166 if (mStoredGainIndex < getMinGainIndex() || mStoredGainIndex > getMaxGainIndex()) {
167 // We expected to load a value from last boot, but if we didn't (perhaps this is the
168 // first boot ever?), then use the highest "default" we've seen to initialize
169 // ourselves.
170 mCurrentGainIndex = getIndexForGain(mDefaultGain);
171 } else {
172 // Just use the gain index we stored last time the gain was set (presumably during our
173 // last boot cycle).
174 mCurrentGainIndex = mStoredGainIndex;
175 }
176 }
mContextToBus.put(contextNumber, busNumber);将contextNumber与busNumber 相对应。 mBusToCarAudioDeviceInfo.put(busNumber, info);将busNumber与info相对应。这样每个contextNumber都对应着具体的输出设备。接下来看contextNumber是如何产生的。
car_audio_configuration.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
audio_policy_configuration.xml
2
16
17
18
19
20
21
22
23
49
50
51
52
53
54 - bus0_media_out
55 - bus1_navigation_out
56 - bus2_voice_command_out
57 - bus3_call_ring_out
58 - bus4_call_out
59 - bus5_alarm_out
60 - bus6_notification_out
61 - bus7_system_sound_out
62 - bus100_rear_seat
63 - Built-In Mic
64 - Built-In Back Mic
65 - Echo-Reference Mic
66 - FM Tuner
67
68 bus0_media_out
69
70
72
75
76
78
81
82
84
87
88
90
93
94
96
99
100
102
105
106
108
111
112
114
117
118
120
123
124
125
128
129
130
133
134
135
136
138
140
141
143
144
145
147
149
150
152
153
154
156
158
159
161
162
163
165
167
168
170
171
172
174
176
177
179
180
181
183
185
186
188
189
190
192
194
195
197
198
199
201
203
204
206
207
208
210
212
213
215
216
217
218
221
222
223
226
227
228
231
232
234
236
237
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
通过CONTEXT_NAME_MAP关系,根据context可以找到contextNumber。
xref: /packages/services/Car/service/src/com/android/car/audio/CarAudioService.java
472 // Setup dynamic routing rules by usage
473 final CarAudioDynamicRouting dynamicRouting = new CarAudioDynamicRouting(mCarAudioZones);
474 dynamicRouting.setupAudioDynamicRouting(builder);
通过usage设置动态路由策略
xref: /packages/services/Car/service/src/com/android/car/audio/CarAudioDynamicRouting.java
50 static final SparseIntArray USAGE_TO_CONTEXT = new SparseIntArray();
...............................................
67 static {
68 USAGE_TO_CONTEXT.put(AudioAttributes.USAGE_UNKNOWN, ContextNumber.MUSIC);
69 USAGE_TO_CONTEXT.put(AudioAttributes.USAGE_MEDIA, ContextNumber.MUSIC);
70 USAGE_TO_CONTEXT.put(AudioAttributes.USAGE_VOICE_COMMUNICATION, ContextNumber.CALL);
71 USAGE_TO_CONTEXT.put(AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING,
72 ContextNumber.CALL);
73 USAGE_TO_CONTEXT.put(AudioAttributes.USAGE_ALARM, ContextNumber.ALARM);
74 USAGE_TO_CONTEXT.put(AudioAttributes.USAGE_NOTIFICATION, ContextNumber.NOTIFICATION);
75 USAGE_TO_CONTEXT.put(AudioAttributes.USAGE_NOTIFICATION_RINGTONE, ContextNumber.CALL_RING);
76 USAGE_TO_CONTEXT.put(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST,
77 ContextNumber.NOTIFICATION);
78 USAGE_TO_CONTEXT.put(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT,
79 ContextNumber.NOTIFICATION);
80 USAGE_TO_CONTEXT.put(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED,
81 ContextNumber.NOTIFICATION);
82 USAGE_TO_CONTEXT.put(AudioAttributes.USAGE_NOTIFICATION_EVENT, ContextNumber.NOTIFICATION);
83 USAGE_TO_CONTEXT.put(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY,
84 ContextNumber.VOICE_COMMAND);
85 USAGE_TO_CONTEXT.put(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
86 ContextNumber.NAVIGATION);
87 USAGE_TO_CONTEXT.put(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION,
88 ContextNumber.SYSTEM_SOUND);
89 USAGE_TO_CONTEXT.put(AudioAttributes.USAGE_GAME, ContextNumber.MUSIC);
90 USAGE_TO_CONTEXT.put(AudioAttributes.USAGE_VIRTUAL_SOURCE, ContextNumber.INVALID);
91 USAGE_TO_CONTEXT.put(AudioAttributes.USAGE_ASSISTANT, ContextNumber.VOICE_COMMAND);
92 }
.......................................
100 void setupAudioDynamicRouting(AudioPolicy.Builder builder) {
101 for (CarAudioZone zone : mCarAudioZones) {
102 for (CarVolumeGroup group : zone.getVolumeGroups()) {
103 setupAudioDynamicRoutingForGroup(group, builder);
104 }
105 }
106 }
107
108 /**
109 * Enumerates all physical buses in a given volume group and attach the mixing rules.
110 * @param group {@link CarVolumeGroup} instance to enumerate the buses with
111 * @param builder {@link AudioPolicy.Builder} to attach the mixing rules
112 */
113 private void setupAudioDynamicRoutingForGroup(CarVolumeGroup group,
114 AudioPolicy.Builder builder) {
115 // Note that one can not register audio mix for same bus more than once.
116 for (int busNumber : group.getBusNumbers()) {
117 boolean hasContext = false;
118 CarAudioDeviceInfo info = group.getCarAudioDeviceInfoForBus(busNumber);
119 AudioFormat mixFormat = new AudioFormat.Builder()
120 .setSampleRate(info.getSampleRate())
121 .setEncoding(info.getEncodingFormat())
122 .setChannelMask(info.getChannelCount())
123 .build();
124 AudioMixingRule.Builder mixingRuleBuilder = new AudioMixingRule.Builder();
125 for (int contextNumber : group.getContextsForBus(busNumber)) {
126 hasContext = true;
127 int[] usages = getUsagesForContext(contextNumber);
128 for (int usage : usages) {
129 mixingRuleBuilder.addRule(
130 new AudioAttributes.Builder().setUsage(usage).build(),
131 AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE);
132 }
133 Log.d(CarLog.TAG_AUDIO, "Bus number: " + busNumber
134 + " contextNumber: " + contextNumber
135 + " sampleRate: " + info.getSampleRate()
136 + " channels: " + info.getChannelCount()
137 + " usages: " + Arrays.toString(usages));
138 }
139 if (hasContext) {
140 // It's a valid case that an audio output bus is defined in
141 // audio_policy_configuration and no context is assigned to it.
142 // In such case, do not build a policy mix with zero rules.
143 AudioMix audioMix = new AudioMix.Builder(mixingRuleBuilder.build())
144 .setFormat(mixFormat)
145 .setDevice(info.getAudioDeviceInfo())
146 .setRouteFlags(AudioMix.ROUTE_FLAG_RENDER)
147 .build();
148 builder.addMix(audioMix);
149 }
150 }
151 }
152
153 private int[] getUsagesForContext(int contextNumber) {
154 final List usages = new ArrayList<>();
155 for (int i = 0; i < CarAudioDynamicRouting.USAGE_TO_CONTEXT.size(); i++) {
156 if (CarAudioDynamicRouting.USAGE_TO_CONTEXT.valueAt(i) == contextNumber) {
157 usages.add(CarAudioDynamicRouting.USAGE_TO_CONTEXT.keyAt(i));
158 }
159 }
160 return usages.stream().mapToInt(i -> i).toArray();
161 }
162 }
通过contextNumber获取到usages,遍历usages将usage添加到mixingRuleBuilder中,然后再将mixingRuleBuilder添加到AudioMix中,将AudioMix addMix 到AudioPolicy中, 这样usage和device就关联上了。