故障现场
这里的首次开机也包括恢复出厂设置后的第一次开机。出现故障后,配合机发送一条短信过来,一直没有提示音。打开AudioHW的PCM DUMP之后发现写下去的音频流为全0数据,查看dumpsys media.audio_policy的信息如下:
Streams dump:
Stream Can be muted Index Min Index Max Index Cur [device : index]...
00 true 01 05 0001 : 04, 40000000 : 04,
01 true 00 07 0001 : 00, 0002 : 00, 40000000 : 05,
02 true 00 07 0001 : 05, 0002 : 05, 40000000 : 05,
03 true 00 15 0004 : 10, 0008 : 10, 40000000 : 11,
04 true 00 07 40000000 : 06,
05 true 00 07 0001 : 00, 0002 : 00, 40000000 : 05,
06 true 00 15 40000000 : 07,
07 true 00 07 0001 : 00, 0002 : 00, 40000000 : 05,
08 true 00 15 0001 : 11, 0002 : 11, 40000000 : 11,
09 true 00 15 0002 : 11, 40000000 : 11,
10 true 00 15 0004 : 10, 0008 : 10, 40000000 : 11,
11 true 00 01 40000000 : 00,
12 true 00 01 40000000 : 00,
可以看到SYSTEM、NOTIFICATION和ENFORCED_AUDIBLE的当前音量值被设置0。查看log可以发现在来电前后有如下的几行可疑打印:
05-03 21:06:09.419 2826 2932 I vol.Events: writeEvent internal_ringer_mode_changed silent
05-03 21:06:09.420 2826 2932 I vol.Events: writeEvent mute_changed STREAM_RING true
05-03 21:06:09.433 2826 2932 I vol.Events: writeEvent mute_changed STREAM_SYSTEM true
05-03 21:06:09.433 2826 2932 I vol.Events: writeEvent external_ringer_mode_changed silent
来电铃音播放结束后对应有:
05-03 21:06:18.479 2826 2932 I vol.Events: writeEvent mute_changed STREAM_RING false
05-03 21:06:18.525 2826 2932 I vol.Events: writeEvent mute_changed STREAM_SYSTEM false
05-03 21:06:18.532 2826 2932 I vol.Events: writeEvent internal_ringer_mode_changed normal
这些代码是电话接通后切成勿扰模式相关的调用,是在systemUI中控制的。
进一步分析
增加AudioFlinger、Thread和APM的log之后,来电之后开始播放短信提示音时将Stream 5设为了Mute。
05-03 21:06:36.752 1517 2963 I ExtendedNuUtils: printFileName fd(13) -> /data/system_de/0/ringtones/notification_sound_cache
05-03 21:06:37.055 1504 1690 V APM_AudioPolicyManager: startOutput() output 13, stream 5, session 425
05-03 21:06:37.057 1504 1690 V APM_AudioPolicyManager: setStrategyMute() strategy 3, mute 1, output ID 3
05-03 21:06:37.057 1504 1690 V APM_AudioPolicyManager: setStreamMute() stream 5, mute 1, mMuteCount 0 device 0002
05-03 21:06:37.058 1504 1690 V APM_AudioPolicyManager: setStrategyMute() strategy 3, mute 0, output ID 3
05-03 21:06:37.058 1504 1690 V APM_AudioPolicyManager: setStreamMute() stream 5, mute 0, mMuteCount 1 device 0002
整体搜索Stream 5,发现了如下两条打印:
05-03 21:06:17.097 1504 9147 V APM_AudioPolicyManager: setStreamVolumeIndex() stream 5, device 00000001, index 0
05-03 21:06:17.953 1504 1882 V APM_AudioPolicyManager: setStreamVolumeIndex() stream 5, device 00000002, index 0
下面分别从两个方向来分析这个故障的原因:
1)调用setStreamVolumeIndex将音量设为0
发起该调用的地方来自AudioService.java,总共有applyDeviceVolume_syncVSS(int device)和applyAllVolumes()两个函数,它们的逻辑是从mIsMuted、mFullVolumeDevices和mIndexMap获取各个设备和Stream的音量值并设置下去。结合另一个故障的细节(即发生故障后,上面直接从AudioManager读取出来的音量值为5,且按下音量上键时直接跳到6级音量,因此可以判断,这里一定是mIsMuted起了效果),继续检查mIsMuted的来源。发现它的设置只有一个地方就是VolumeStreamState的mute函数,继续向上的调用函数有adjustStreamVolume()、onUnmuteStream()、onSetStreamVolume()、muteRingerModeStreams(),直接导致被设为音量0的log在结束通话之后,mute_changed STREAM_RING false之前,目前来看分析受阻。
2)调用setStreamMute()将音量设为0
经过查看代码发现,这个调用只是setOutputDevice函数中,如果设备确实发生切换的情况下,在checkDeviceMuteStrategies中暂时将Stream静音一下子避免设备切换时的POP音问题,因此不是问题所在。
返回到第一个方向,虽然分析受阻,不过通过分析代码逻辑有一个怀疑对象,就是如下调用/frameworks/base/services/core/java/com/android/server/notification/ZenModeHelper.java中的updateRingerModeAffectedStreams(),这里调用updateRingerModeAffectedStreamsInternal,如果该调用比将RingerMode切回Normal早的话,就会发现这个问题了。不过,还是加调用栈来定位最直接了,比猜的来得快(上述代码应该就是勿打扰相关的实现)。
增加log后,得到调用栈如下:
05-04 14:31:05.094 1945 2680 E AudioService: applyDeviceVolume_syncVSS mIsMuted : true Index : 50
05-04 14:31:05.096 1945 2680 E AudioService: java.lang.Throwable
05-04 14:31:05.096 1945 2680 E AudioService:
at com.android.server.audio.AudioService$VolumeStreamState.applyDeviceVolume_syncVSS(AudioService.java:4162)
05-04 14:31:05.096 1945 2680 E AudioService:
at com.android.server.audio.AudioService$AudioHandler.setDeviceVolume(AudioService.java:4462)
05-04 14:31:05.096 1945 2680 E AudioService:
at com.android.server.audio.AudioService$AudioHandler.handleMessage(AudioService.java:4746)
05-04 14:31:05.096 1945 2680 E AudioService:
at android.os.Handler.dispatchMessage(Handler.java:102)
05-04 14:31:05.096 1945 2680 E AudioService:
at android.os.Looper.loop(Looper.java:154)
05-04 14:31:05.096 1945 2680 E AudioService:
at
05-04 14:31:05.097 1436 8963 V APM_AudioPolicyManager: setStreamVolumeIndex() stream 5, device 00000001, index 0
而在进入通话状态时,有从ZenModeHelper发起的经过muteRingerModeStreams到mute的调用,因此是因此勿扰模式的设置是音量为0的原因,但是发起setDeviceVolume调用的另有流程。再添加log后的新场景分析如下:
挂断电话时,经过如下的setMode调用栈,发送MSG_SET_DEVICE_VOLUME消息。
05-04 15:05:46.517 1940 3150 E AudioService: java.lang.Throwable
05-04 15:05:46.517 1940 3150 E AudioService:
at com.android.server.audio.AudioService.setStreamVolumeInt(AudioService.java:1896)
05-04 15:05:46.517 1940 3150 E AudioService:
at com.android.server.audio.AudioService.setModeInt(AudioService.java:2525)
05-04 15:05:46.517 1940 3150 E AudioService:
at com.android.server.audio.AudioService.setMode(AudioService.java:2431)
05-04 15:05:46.517 1940 3150 E AudioService:
at android.media.AudioManager.setMode(AudioManager.java:1618)
05-04 15:05:46.517 1940 3150 E AudioService:
at com.android.server.telecom.CallAudioModeStateMachine$UnfocusedState.enter(CallAudioModeStateMachine.java:152)
05-04 15:05:46.517 1940 3150 E AudioService:
at com.android.internal.util.StateMachine$SmHandler.invokeEnterMethods(StateMachine.java:1021)
05-04 15:05:46.517 1940 3150 E AudioService:
at com.android.internal.util.StateMachine$SmHandler.performTransitions(StateMachine.java:867)
05-04 15:05:46.517 1940 3150 E AudioService:
at com.android.internal.util.StateMachine$SmHandler.handleMessage(StateMachine.java:809)
05-04 15:05:46.517 1940 3150 E AudioService:
at android.os.Handler.dispatchMessage(Handler.java:102)
05-04 15:05:46.517 1940 3150 E AudioService:
at android.os.Looper.loop(Looper.java:154)
05-04 15:05:46.517 1940 3150 E AudioService:
at android.os.HandlerThread.run(HandlerThread.java:61)
然后经过如下调用栈,将Stream 5的扬声器设为静音
05-04 15:05:46.568 1940 2684 E AudioService: java.lang.Throwable
05-04 15:05:46.568 1940 2684 E AudioService:
at com.android.server.audio.AudioService$VolumeStreamState.applyDeviceVolume_syncVSS(AudioService.java:4164)
05-04 15:05:46.568 1940 2684 E AudioService:
at com.android.server.audio.AudioService$AudioHandler.setDeviceVolume(AudioService.java:4464)
05-04 15:05:46.568 1940 2684 E AudioService:
at com.android.server.audio.AudioService$AudioHandler.handleMessage(AudioService.java:4748)
05-04 15:05:46.568 1940 2684 E AudioService:
at android.os.Handler.dispatchMessage(Handler.java:102)
05-04 15:05:46.568 1940 2684 E AudioService:
at android.os.Looper.loop(Looper.java:154)
05-04 15:05:46.568 1940 2684 E AudioService:
at com.android.server.audio.AudioService$AudioSystemThread.run(AudioService.java:4439)
05-04 15:05:46.572 3562 3562 I LauncherUnreadService: Call log content changed
05-04 15:05:46.577 1439 2686 V APM_AudioPolicyManager: setStreamVolumeIndex() stream 5, device 00000002, index 0
进一步在勿扰模式的后续处理中,会调用经过如下调用栈将需要的Stream的音量值给恢复,但是异常时Stream 5的扬声器对应音量没有设置。
05-04 15:05:46.973 1940 1940 E AudioService: java.lang.Throwable
05-04 15:05:46.973 1940 1940 E AudioService:
at com.android.server.audio.AudioService$VolumeStreamState.mute(AudioService.java:4320)
05-04 15:05:46.973 1940 1940 E AudioService:
at com.android.server.audio.AudioService.muteRingerModeStreams(AudioService.java:2289)
05-04 15:05:46.973 1940 1940 E AudioService:
at com.android.server.audio.AudioService.setRingerModeInt(AudioService.java:2306)
05-04 15:05:46.973 1940 1940 E AudioService:
at com.android.server.audio.AudioService.setRingerMode(AudioService.java:2230)
05-04 15:05:46.973 1940 1940 E AudioService:
at com.android.server.audio.AudioService.setRingerModeInternal(AudioService.java:2200)
05-04 15:05:46.973 1940 1940 E AudioService:
at com.android.server.audio.AudioService$AudioServiceInternal.setRingerModeInternal(AudioService.java:6396)
05-04 15:05:46.973 1940 1940 E AudioService:
at com.android.server.notification.ZenModeHelper.applyZenToRingerMode(ZenModeHelper.java:786)
05-04 15:05:46.973 1940 1940 E AudioService:
at com.android.server.notification.ZenModeHelper.evaluateZenMode(ZenModeHelper.java:699)
05-04 15:05:46.973 1940 1940 E AudioService:
at com.android.server.notification.ZenModeHelper.applyConfig(ZenModeHelper.java:665)
05-04 15:05:46.973 1940 1940 E AudioService:
at com.android.server.notification.ZenModeHelper.-wrap3(ZenModeHelper.java)
05-04 15:05:46.973 1940 1940 E AudioService:
at com.android.server.notification.ZenModeHelper$H.handleMessage(ZenModeHelper.java:1135)
05-04 15:05:46.973 1940 1940 E AudioService:
at android.os.Handler.dispatchMessage(Handler.java:102)
05-04 15:05:46.973 1940 1940 E AudioService:
at android.os.Looper.loop(Looper.java:154)
05-04 15:05:46.973 1940 1940 E AudioService:
at com.android.server.SystemServer.run(SystemServer.java:366)
05-04 15:05:46.973 1940 1940 E AudioService:
at com.android.server.SystemServer.main(SystemServer.java:236)
05-04 15:05:46.973 1940 1940 E AudioService:
at java.lang.reflect.Method.invoke(Native Method)
05-04 15:05:46.973 1940 1940 E AudioService:
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
05-04 15:05:46.973 1940 1940 E AudioService:
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
05-04 15:05:46.974 1940 2684 E AudioService: java.lang.Throwable
05-04 15:05:46.974 1940 2684 E AudioService:
at com.android.server.audio.AudioService$VolumeStreamState.applyAllVolumes(AudioService.java:4178)
05-04 15:05:46.974 1940 2684 E AudioService:
at com.android.server.audio.AudioService$AudioHandler.setAllVolumes(AudioService.java:4482)
05-04 15:05:46.974 1940 2684 E AudioService:
at com.android.server.audio.AudioService$AudioHandler.handleMessage(AudioService.java:4752)
05-04 15:05:46.974 1940 2684 E AudioService:
at android.os.Handler.dispatchMessage(Handler.java:102)
05-04 15:05:46.974 1940 2684 E AudioService:
at android.os.Looper.loop(Looper.java:154)
05-04 15:05:46.974 1940 2684 E AudioService:
at com.android.server.audio.AudioService$AudioSystemThread.run(AudioService.java:4439)
05-04 15:05:46.974 1940 2684 E AudioService: applyAllVolumes Muted:false index:107
Stream 5异常的设置栈如下:
05-04 15:05:47.097 1940 2684 E AudioService: java.lang.Throwable
05-04 15:05:47.097 1940 2684 E AudioService:
at com.android.server.audio.AudioService$VolumeStreamState.applyAllVolumes(AudioService.java:4178)
05-04 15:05:47.097 1940 2684 E AudioService:
at com.android.server.audio.AudioService$AudioHandler.setAllVolumes(AudioService.java:4482)
05-04 15:05:47.097 1940 2684 E AudioService:
at com.android.server.audio.AudioService$AudioHandler.handleMessage(AudioService.java:4752)
05-04 15:05:47.097 1940 2684 E AudioService:
at android.os.Handler.dispatchMessage(Handler.java:102)
05-04 15:05:47.097 1940 2684 E AudioService:
at android.os.Looper.loop(Looper.java:154)
05-04 15:05:47.097 1940 2684 E AudioService:
at com.android.server.audio.AudioService$AudioSystemThread.run(AudioService.java:4439)
05-04 15:05:47.097 1439 1810 V APM_AudioPolicyManager: setStreamVolumeIndex() stream 5, device 40000000, index 5
也就是说,将音量设回到非静音,也是先要经过ZenModeHelper的mute调用。要注意的一点是,VolumeStreamState.applyAllVolumes对象是每个Stream一个,并不是所有Stream的音量只要调用一个applyAllVolumes即可。上述两个调用栈的衔接就是mute函数,因此从muteRingerModeStreams入手分析。从效果上看,就是VolumeStreamState对象(Stream 5对应的)的mIndexMap包含的没有了扬声器这样的设备,而这些对象全部来自于mStreamStates[streamType],正常情况下,mIndexMap只有添加的流程,并没有删除的流程。因此我们要关注一下,会添加这些信息的调用有哪些。
分别是readSettings()、setIndex、setAllIndexes、setAllIndexesToMax、checkFixedVolumeDevices,根据首次开机这样的场景来说,最可能实现这个功能的是readSettings函数,在这里发现了一个可能的问题:
public void readSettings() {
// retrieve current volume for device
String name = getSettingNameForDevice(device);
// if no volume stored for current stream and device, use default volume if default
// device, continue otherwise
int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
AudioSystem.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
int index = Settings.System.getIntForUser(
mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
if (index == -1) {
continue;
}
mIndexMap.put(device, getValidIndex(10 * index));
}
缺省的音量值来自Settings的设置,索引值System.VOLUME_SETTINGS[mStreamVolumeAlias[i]]加设备名,对应开机时VolumeStreamState对象创建时的入参(如下)。
private void createStreamStates() {
int numStreamTypes = AudioSystem.getNumStreamTypes();
VolumeStreamState[] streams = mStreamStates =
new VolumeStreamState[numStreamTypes];
for (int i = 0; i < numStreamTypes; i++) {
streams[i] =
new VolumeStreamState(System.VOLUME_SETTINGS[mStreamVolumeAlias[i]], i);
}
}
数据库索引值的字符串定义在/frameworks/base/core/java/android/provider/Settings.java中,而数据库System是运行中创建的/data/system/users/0/settings_system.xml文件。创建函数是AudioService:: AudioHandler:: persistVolume函数,向上追溯就是MSG_PERSIST_VOLUME消息,会发送该消息的一个函数就是setDeviceVolume,综合起来可以初步得出结论:
---在手机开机时,会通过readSettings()读取settings_system.xml创建mIndexMap数组内容;如果运行期间有发送MSG_PERSIST_VOLUME消息的函数被调用时,可能会在settings_system.xml中新建新的条目;
---settings_system.xml中条目的索引值是volume_notification以及volume_notification加设备名,前者设为DEFAULT_DEVICE的音量值,后者对应各个设备的音量值;
---setModeInt函数中会调用setStreamVolumeInt更新Stream和Device的音量,这里有一个不对称的处理,一个是通过HAL层修改mStreamType的音量值,另一个是增加上述xml的设置,但是缺少了一个添加mIndexMap的动作,导致后续的恢复处理回出现跳过新增设备的音量恢复的故障。
不过,对于这个故障,按照上述思路,很快就发现了代码遗漏,缺少了一个资源配置:
frameworks/base/packages/SettingsProvider/res/values/customize.xml
<resources>
<integer name="def_notification_speaker_volume" translatable="false">5</integer>
</resources>
/frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java中的loadCustomizedVolumeLevels调用systemSettings.updateSettingLocked设置Stream的音量值,也是给settings_system.xml增加数据库条目的途径之一,增加以上配置后,扬声器播放短信提示音的故障就可以解决了。