Android10 车载音频架构之动态路由的配置

Android10 车载音频架构之动态路由的配置_第1张图片

启用 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就关联上了。

你可能感兴趣的:(车载系统)