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

启用 AAOS 路由

xref: /packages/services/Car/service/res/values/config.xml

<resources>
    <bool name="audioUseDynamicRouting">true</bool>
</resources>

如果设为 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<CarAudioDeviceInfo> 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<CarAudioDeviceInfo> 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<String, Integer> 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 <?xml version="1.0" encoding="utf-8"?>
2 <carAudioConfiguration version="1">
3     <zones>
4         <zone name="primary zone" isPrimary="true">
5             <volumeGroups>
6                 <group>
7                     <device address="bus0_media_out">
8                         <context context="music"/>
9                     </device>
10                     <device address="bus3_call_ring_out">
11                         <context context="call_ring"/>
12                     </device>
13                 </group>
14                 <group>
15                     <device address="bus1_navigation_out">
16                         <context context="navigation"/>
17                     </device>
18                 </group>
19             </volumeGroups>
20             <displays>
21                 <display port="1"/>
22                 <display port="2"/>
23             </displays>
24         </zone>
25         <zone name="rear seat zone">
26             <volumeGroups>
27                 <group>
28                     <device address="bus100_rear_seat">
29                         <context context="music"/>
30                         <context context="navigation"/>
31                         <context context="voice_command"/>
32                         <context context="call_ring"/>
33                         <context context="call"/>
34                         <context context="alarm"/>
35                         <context context="notification"/>
36                         <context context="system_sound"/>
37                     </device>
38                 </group>
39             </volumeGroups>
40         </zone>
41     </zones>
42 </carAudioConfiguration>

audio_policy_configuration.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2 <!-- Copyright (C) 2018 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 
17 <audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
18     <!-- version section contains a “version” tag in the form “major.minor” e.g version=”1.0” -->
19 
20     <!-- Global configuration Decalaration -->
21     <globalConfiguration speaker_drc_enabled="true"/>
22 
23     <!-- Modules section:
24         There is one section per audio HW module present on the platform.
25         Each module section will contains two mandatory tags for audio HAL “halVersion” and “name”.
26         The module names are the same as in current .conf file:
27                 “primary”, “A2DP”, “remote_submix”, “USB”
28         Each module will contain the following sections:
29         “devicePorts”: a list of device descriptors for all input and output devices accessible via this
30         module.
31         This contains both permanently attached devices and removable devices.
32             "gain": constraints applied to the millibel values:
33                 - maxValueMB >= minValueMB
34                 - defaultValueMB >= minValueMB && defaultValueMB <= maxValueMB
35                 - (maxValueMB - minValueMB) % stepValueMB == 0
36                 - (defaultValueMB - minValueMB) % stepValueMB == 0
37         “mixPorts”: listing all output and input streams exposed by the audio HAL
38         “routes”: list of possible connections between input and output devices or between stream and
39         devices.
40             "route": is defined by an attribute:
41                 -"type": <mux|mix> means all sources are mutual exclusive (mux) or can be mixed (mix)
42                 -"sink": the sink involved in this route
43                 -"sources": all the sources than can be connected to the sink via vis route
44         “attachedDevices”: permanently attached devices.
45         The attachedDevices section is a list of devices names. The names correspond to device names
46         defined in <devicePorts> section.
47         “defaultOutputDevice”: device to be used by default when no policy rule applies
48     -->
49     <modules>
50         <!-- Primary Audio HAL -->
51         <module name="primary" halVersion="3.0">
52             <attachedDevices>
53                 <!-- One bus per context -->
54                 <item>bus0_media_out</item>
55                 <item>bus1_navigation_out</item>
56                 <item>bus2_voice_command_out</item>
57                 <item>bus3_call_ring_out</item>
58                 <item>bus4_call_out</item>
59                 <item>bus5_alarm_out</item>
60                 <item>bus6_notification_out</item>
61                 <item>bus7_system_sound_out</item>
62                 <item>bus100_rear_seat</item>
63                 <item>Built-In Mic</item>
64                 <item>Built-In Back Mic</item>
65                 <item>Echo-Reference Mic</item>
66                 <item>FM Tuner</item>
67             </attachedDevices>
68             <defaultOutputDevice>bus0_media_out</defaultOutputDevice>
69             <mixPorts>
70                 <mixPort name="mixport_bus0_media_out" role="source"
71                         flags="AUDIO_OUTPUT_FLAG_PRIMARY">
72                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
73                              samplingRates="48000"
74                              channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
75                 </mixPort>
76                 <mixPort name="mixport_bus1_navigation_out" role="source"
77                         flags="AUDIO_OUTPUT_FLAG_PRIMARY">
78                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
79                              samplingRates="48000"
80                              channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
81                 </mixPort>
82                 <mixPort name="mixport_bus2_voice_command_out" role="source"
83                         flags="AUDIO_OUTPUT_FLAG_PRIMARY">
84                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
85                              samplingRates="48000"
86                              channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
87                 </mixPort>
88                 <mixPort name="mixport_bus3_call_ring_out" role="source"
89                         flags="AUDIO_OUTPUT_FLAG_PRIMARY">
90                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
91                              samplingRates="48000"
92                              channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
93                 </mixPort>
94                 <mixPort name="mixport_bus4_call_out" role="source"
95                         flags="AUDIO_OUTPUT_FLAG_PRIMARY">
96                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
97                              samplingRates="48000"
98                              channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
99                 </mixPort>
100                 <mixPort name="mixport_bus5_alarm_out" role="source"
101                         flags="AUDIO_OUTPUT_FLAG_PRIMARY">
102                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
103                              samplingRates="48000"
104                              channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
105                 </mixPort>
106                 <mixPort name="mixport_bus6_notification_out" role="source"
107                         flags="AUDIO_OUTPUT_FLAG_PRIMARY">
108                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
109                              samplingRates="48000"
110                              channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
111                 </mixPort>
112                 <mixPort name="mixport_bus7_system_sound_out" role="source"
113                         flags="AUDIO_OUTPUT_FLAG_PRIMARY">
114                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
115                              samplingRates="48000"
116                              channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
117                 </mixPort>
118                 <mixPort name="mixport_bus100_rear_seat" role="source"
119                         flags="AUDIO_OUTPUT_FLAG_PRIMARY">
120                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
121                              samplingRates="48000"
122                              channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
123                 </mixPort>
124                 <mixPort name="primary input" role="sink">
125                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
126                              samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
127                              channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
128                 </mixPort>
129                 <mixPort name="mixport_tuner0" role="sink">
130                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
131                              samplingRates="48000"
132                              channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
133                 </mixPort>
134             </mixPorts>
135             <devicePorts>
136                 <devicePort tagName="bus0_media_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
137                         address="bus0_media_out">
138                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
139                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
140                     <gains>
141                         <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
142                                 minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
143                     </gains>
144                 </devicePort>
145                 <devicePort tagName="bus1_navigation_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
146                         address="bus1_navigation_out">
147                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
148                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
149                     <gains>
150                         <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
151                                 minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
152                     </gains>
153                 </devicePort>
154                 <devicePort tagName="bus2_voice_command_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
155                         address="bus2_voice_command_out">
156                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
157                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
158                     <gains>
159                         <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
160                                 minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
161                     </gains>
162                 </devicePort>
163                 <devicePort tagName="bus3_call_ring_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
164                         address="bus3_call_ring_out">
165                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
166                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
167                     <gains>
168                         <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
169                                 minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
170                     </gains>
171                 </devicePort>
172                 <devicePort tagName="bus4_call_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
173                         address="bus4_call_out">
174                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
175                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
176                     <gains>
177                         <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
178                                 minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
179                     </gains>
180                 </devicePort>
181                 <devicePort tagName="bus5_alarm_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
182                         address="bus5_alarm_out">
183                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
184                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
185                     <gains>
186                         <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
187                                 minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
188                     </gains>
189                 </devicePort>
190                 <devicePort tagName="bus6_notification_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
191                         address="bus6_notification_out">
192                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
193                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
194                     <gains>
195                         <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
196                                 minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
197                     </gains>
198                 </devicePort>
199                 <devicePort tagName="bus7_system_sound_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
200                         address="bus7_system_sound_out">
201                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
202                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
203                     <gains>
204                         <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
205                                 minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
206                     </gains>
207                 </devicePort>
208                 <devicePort tagName="bus100_rear_seat" role="sink" type="AUDIO_DEVICE_OUT_BUS"
209                         address="bus100_rear_seat">
210                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
211                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
212                     <gains>
213                         <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
214                                 minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
215                     </gains>
216                 </devicePort>
217                 <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
218                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
219                             samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
220                             channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
221                 </devicePort>
222                 <devicePort tagName="Built-In Back Mic" type="AUDIO_DEVICE_IN_BACK_MIC" role="source">
223                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
224                             samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
225                             channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
226                 </devicePort>
227                 <devicePort tagName="Echo-Reference Mic" type="AUDIO_DEVICE_IN_ECHO_REFERENCE" role="source">
228                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
229                             samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
230                             channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
231                 </devicePort>
232                 <devicePort tagName="FM Tuner" type="AUDIO_DEVICE_IN_FM_TUNER" role="source"
233                         address="tuner0">
234                     <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
235                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
236                     <gains>
237                         <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
238                                 minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/>
239                     </gains>
240                 </devicePort>
241             </devicePorts>
242             <!-- route declaration, i.e. list all available sources for a given sink -->
243             <routes>
244                 <route type="mix" sink="bus0_media_out" sources="mixport_bus0_media_out"/>
245                 <route type="mix" sink="bus1_navigation_out" sources="mixport_bus1_navigation_out"/>
246                 <route type="mix" sink="bus2_voice_command_out" sources="mixport_bus2_voice_command_out"/>
247                 <route type="mix" sink="bus3_call_ring_out" sources="mixport_bus3_call_ring_out"/>
248                 <route type="mix" sink="bus4_call_out" sources="mixport_bus4_call_out"/>
249                 <route type="mix" sink="bus5_alarm_out" sources="mixport_bus5_alarm_out"/>
250                 <route type="mix" sink="bus6_notification_out" sources="mixport_bus6_notification_out"/>
251                 <route type="mix" sink="bus7_system_sound_out" sources="mixport_bus7_system_sound_out"/>
252                 <route type="mix" sink="bus100_rear_seat" sources="mixport_bus100_rear_seat"/>
253                 <route type="mix" sink="primary input" sources="Built-In Mic,Built-In Back Mic,Echo-Reference Mic"/>
254                 <route type="mix" sink="mixport_tuner0" sources="FM Tuner"/>
255             </routes>
256 
257         </module>
258 
259         <!-- A2dp Audio HAL -->
260         <xi:include href="a2dp_audio_policy_configuration.xml"/>
261 
262         <!-- Usb Audio HAL -->
263         <xi:include href="usb_audio_policy_configuration.xml"/>
264 
265         <!-- Remote Submix Audio HAL -->
266         <xi:include href="r_submix_audio_policy_configuration.xml"/>
267 
268     </modules>
269     <!-- End of Modules section -->
270 
271     <!-- Volume section -->
272 
273     <xi:include href="audio_policy_volumes.xml"/>
274     <xi:include href="default_volume_tables.xml"/>
275 
276     <!-- End of Volume section -->
277     <!-- End of Modules section -->
278 
279 </audioPolicyConfiguration>

通过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<Integer> 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就关联上了。

<think>嗯,用户现在问的是关于Android音频开发的内容。首先,我需要回想一下之前的对话历史,看看有没有相关的信息或者用户之前的需求。之前用户询问的是智能驾驶数据上传,现在转向Android音频开发,可能是同一个项目中的不同部分,或者用户有更广泛的技术兴趣。 接下来,我需要分析用户的需求。用户可能是一位开发者,正在学习Android开发,特别是音频相关的部分。也有可能是在开发一个需要音频功能的应用程序,比如音乐播放器、语音识别、实时通信应用等。因此,回答需要覆盖基础知识、核心模块、常见场景以及开发中的注意事项。 考虑到用户之前的问题是关于数据上传,可能需要考虑音频数据的处理与传输是否有关联,但当前问题更专注于Android音频开发本身。因此,回答应重点放在Android平台的音频框架、API使用、常见问题及解决方案上。 用户可能希望了解的内容包括:Android音频系统的架构、常用的API(如AudioTrack、MediaPlayer等)、音频流处理、延迟问题、兼容性处理、以及高级功能如回声消除或低延迟音频。此外,可能还需要提供实际开发中的技巧和最佳实践。 需要确保回答结构清晰,分步骤解析,帮助用户逐步理解。同时,按照之前的系统指令,需要使用中文,数学公式用$$...$$格式,行内公式用$...$,并且符合LaTeX语法。 在整理信息时,要确保技术细节准确,例如AudioManager的不同音频流类型,AudioTrack和MediaPlayer的区别,以及ExoPlayer的优势。同时,注意提到Android不同版本的变化,比如Android 10引入的动态延迟调整,Android 12的低延迟模式,这些对开发者来说都是重要的知识点。 另外,用户可能需要了解如何处理常见的兼容性问题,比如不同设备的音频参数不一致,如何选择合适的采样率和缓冲区大小。还有性能优化方面,比如避免主线程阻塞,使用JNI进行本地音频处理等。 安全与权限也是不可忽视的部分,需要提到Android的权限机制,如RECORD_AUDIO权限的必要性,以及用户隐私保护的措施。 最后,案例部分可以举一些实际应用场景,如直播应用的音频处理,或者语音助手的关键技术点,帮助用户更好地将理论应用到实践中。 总结来说,回答需要覆盖Android音频开发的基础知识、核心模块、常见场景、开发技巧、安全权限以及未来趋势,结构清晰,内容详实,满足不同层次开发者的需求。</think>### Android音频开发分步解析 --- #### 一、Android音频系统架构 1. **层次结构** Android音频系统基于**分层设计**,从应用层到硬件层分为: - **应用层**:通过Java/Kotlin API调用(如`MediaPlayer`、`AudioTrack`)。 - **框架层**:`AudioManager`、`AudioService`管理音频策略和路由。 - **本地层(Native)**:`AAudio`(低延迟)、`OpenSL ES`(高性能)。 - **硬件抽象层(HAL)**:`audio.primary`驱动适配不同硬件。 2. **关键组件** - **AudioFlinger**:负责混音和音频流分发(如混合多个应用的音频流)。 - **AudioPolicyManager**:决定音频输出路径(如耳机切换、蓝牙优先级)。 --- #### 二、核心开发模块 1. **音频播放** - **MediaPlayer**:封装高层API,支持本地/网络音频(MP3、AAC等): ```java MediaPlayer player = MediaPlayer.create(context, R.raw.sample); player.start(); // 播放 ``` - **AudioTrack**:低层级控制,适合实时音频流(PCM数据): ```java AudioTrack track = new AudioTrack( AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM ); track.write(pcmData, 0, pcmData.length); // 写入PCM数据 ``` 2. **音频录制** - **MediaRecorder**:快速录制压缩格式(AMR、AAC): ```java MediaRecorder recorder = new MediaRecorder(); recorder.setAudioSource(MediaRecorder.AudioSource.MIC); recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); recorder.setOutputFile("record.mp4"); recorder.prepare(); recorder.start(); ``` - **AudioRecord**:获取原始PCM数据(用于语音识别或自定义处理): ```java AudioRecord record = new AudioRecord( MediaRecorder.AudioSource.MIC, 16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize ); byte[] buffer = new byte[bufferSize]; record.read(buffer, 0, bufferSize); // 读取原始音频数据 ``` 3. **音频特效** - **音效API**:`Equalizer`、`BassBoost`(需`AudioSessionId`绑定): ```java Equalizer eq = new Equalizer(0, audioSessionId); eq.setEnabled(true); eq.setBandLevel((short)0, (short)1000); // 调节频段 ``` --- #### 三、关键开发场景与解决方案 1. **低延迟音频(游戏/实时通信)** - **使用AAudio**(Android 8.0+): - 延迟可降至$<10\text{ms}$(传统AudioTrack约为$50\text{ms}$)。 - 通过`AAudioStreamBuilder`设置性能模式:`AAUDIO_PERFORMANCE_MODE_LOW_LATENCY`。 - **缓冲区优化**:根据设备能力动态调整(公式): $$ \text{缓冲区大小 } B = \frac{\text{采样率 } f_s \times \text{延迟要求 } L}{1000} $$ 例如:$f_s=48\text{kHz}, L=5\text{ms} \Rightarrow B=240$帧。 2. **多音频流混合** - **焦点管理**:通过`AudioManager.requestAudioFocus()`处理通话与音乐冲突。 - **音量独立控制**:对不同音频流(如`STREAM_MUSIC`、`STREAM_ALARM`)单独调节。 3. **音频处理链** - **自定义效果**:通过`AudioEffect`或NDK实现(如回声消除、降噪): ```cpp // NDK中使用OpenSL ES SLresult result = (*player)->Enqueue(player, buffer, bufferSize); ``` - **可视化分析**:对PCM数据做FFT(快速傅里叶变换)生成频谱: $$ X(k) = \sum_{n=0}^{N-1} x(n)e^{-j2\pi kn/N} \quad (0 \leq k \leq N-1) $$ --- #### 四、常见问题与调试技巧 1. **延迟问题** - **测试工具**:使用`adb shell dumpsys audio`查看音频流状态。 - **设备兼容性**:不同厂商HAL实现差异(如某些设备强制重采样到48kHz)。 2. **杂音/破音** - **缓冲区下溢(Underrun)**:增大缓冲区或优化数据写入线程优先级。 - **采样率不匹配**:确保输入输出采样率一致(如`AudioTrack`与音频文件参数对齐)。 3. **权限与隐私** - **动态权限申请**:Android 6.0+需运行时请求`RECORD_AUDIO`权限。 - **隐私合规**:录音前需明确提示用户(如弹出对话框说明用途)。 --- #### 五、高级特性与未来趋势 1. **Android 12+新特性** - **低延迟模式**:支持`AudioTrack.setLowLatency(true)`。 - **空间音频**:基于陀螺仪数据的3D音效(`Spatializer`类)。 2. **机器学习集成** - **TensorFlow Lite音频分类**:实时识别环境声音(如婴儿哭声、玻璃破碎)。 - **端侧语音增强**:使用TFLite模型降噪(输入PCM,输出滤波后数据)。 3. **无线音频协议** - **蓝牙LE Audio**:支持多设备同步(如多个耳机共享音频流)。 - **Android Auto**:车载场景下的音频路由与降噪优化。 --- #### 六、典型应用案例 1. **直播应用音频处理** - **回声消除(AEC)**:通过`AcousticEchoCanceler`抑制扬声器回传。 - **音频编码**:使用`MediaCodec`将PCM实时压缩为AAC(码率控制公式): $$ \text{码率 } R = \text{采样率 } \times \text{位深 } \times \text{通道数 } \times \text{压缩比} $$ 2. **语音助手实现** - **关键词唤醒**:通过`VoiceInteractionService`监听特定语音指令。 - **音频焦点抢占**:在用户说话时暂停其他音频播放。 --- ### 总结 Android音频开发需平衡**性能**、**延迟**与**兼容性**,核心在于: 1. 根据场景选择API(高层`MediaPlayer` vs 底层`AAudio`)。 2. 优化音频线程优先级与缓冲区策略。 3. 遵循隐私与权限规范。 未来随着AI与无线技术的演进,音频处理将更智能化(如环境感知降噪)与无缝化(多设备协同)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值