前言:问题起源于客户通过SoundPool播放提示音,想要增大音量,硬件无法通过QACT查看音频参数,只能通过增大音源的办法,不过客户还是觉得轻了。中间需要我写个DEMO实验一下,但是上层怎样才能让硬件通过QACT看到音频参数,我实在是不知道,知道的同学可以留言指导我一下。
然后驱动就各种找碴,要我提供上层调节音量的流程,因为通过设置中拖动音量条是可以通过QACT看到参数的,并且也是短音频。所以我花了一番功夫追了下代码,现在记下来作为备用。(最后驱动通过一个列表查到SoundPool方法播放的是低延迟音频不经过DSP,明明驱动一眼能看到的东西非要我们上层查,真的烦!)
上层通过设置UI搜索到布局文件notification_settings.xml
notification_settings.xml(/packages/apps/Settings/res/xml)
<com.android.settings.notification.VolumeSeekBarPreference
android:key="media_volume"
android:icon="@*android:drawable/ic_audio_vol"
android:title="@string/media_volume_option_title" />
<com.android.settings.notification.VolumeSeekBarPreference
android:key="alarm_volume"
android:icon="@*android:drawable/ic_audio_alarm"
android:title="@string/alarm_volume_option_title" />
<com.android.settings.notification.VolumeSeekBarPreference
android:key="ring_volume"
android:icon="@*android:drawable/ic_audio_ring_notif"
android:title="@string/ring_volume_option_title" />
通过代码构造UI,初始化时会重写onProgressChanged方法
VolumeSeekBarPreference.java(/packages/apps/Settings/src/com/android/settings/notification)
private void init() {
...
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
if (mCallback != null) {
mCallback.onStreamValueChanged(mStream, progress);
}
}
...
}
下面这个callback找了很久,其实很简单
SeekBarVolumizer.java(/frameworks/base/core/java/android/preference)
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
if (fromTouch) {
postSetVolume(progress);
}
if (mCallback != null) {
mCallback.onProgressChanged(seekBar, progress, fromTouch);
}
}
public void onStopTrackingTouch(SeekBar seekBar) {
postStartSample();
}
private void postStartSample() {
if (mHandler == null) return;
mHandler.removeMessages(MSG_START_SAMPLE);
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_SAMPLE),
isSamplePlaying() ? CHECK_RINGTONE_PLAYBACK_DELAY_MS : 0);
}
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_START_SAMPLE:
onStartSample();
break;
}
}
private void onStartSample() {
if (!isSamplePlaying()) {
if (mCallback != null) {
mCallback.onSampleStarting(this);
}
if (mRingtone != null) {
try {
mRingtone.play(); //走这里
} catch (Throwable e) {
Log.w(TAG, "Error playing ringtone, stream " + mStreamType, e);
}
}
}
}
Ringtone.java(/frameworks/base/media/java/android/media)
/**
* Plays the ringtone.
*/
public void play() {
if (mLocalPlayer != null) {
// do not play ringtones if stream volume is 0
// (typically because ringer mode is silent).
if (mAudioManager.getStreamVolume(
AudioAttributes.toLegacyStreamType(mAudioAttributes)) != 0) {
mLocalPlayer.start(); //走这里
}
} else if (mAllowRemote && (mRemotePlayer != null)) {
final Uri canonicalUri = mUri.getCanonicalUri();
try {
mRemotePlayer.play(mRemoteToken, canonicalUri, mAudioAttributes);
} catch (RemoteException e) {
if (!playFallbackRingtone()) {
Log.w(TAG, "Problem playing ringtone: " + e);
}
}
} else {
if (!playFallbackRingtone()) {
Log.w(TAG, "Neither local nor remote playback available");
}
}
}
MediaPlayer.java(/frameworks/base/media/java/android/media)
public void start() throws IllegalStateException {
if (isRestricted()) {
_setVolume(0, 0);
}
stayAwake(true);
_start();
}
private native void _start() throws IllegalStateException;
android_media_MediaPlayer.cpp(/frameworks/base/media/jni)
{"_start", "()V", (void *)android_media_MediaPlayer_start},
static void
android_media_MediaPlayer_start(JNIEnv *env, jobject thiz)
{
sp mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
process_media_player_call( env, thiz, mp->start(), NULL, NULL ); //mp->start()
}
之后的流程请参考:
[Android] AudioTrack::start
另外,拖动音量条时会播放一段示例音,媒体、闹钟、铃声都不同,加载资源的地方在构造SeekBarVolumizer时就定了:
SeekBarVolumizer.java(/frameworks/base/core/java/android/preference)
public SeekBarVolumizer(Context context, int streamType, Uri defaultUri, Callback callback) {
mContext = context;
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mStreamType = streamType;
mAffectedByRingerMode = mAudioManager.isStreamAffectedByRingerMode(mStreamType);
mNotificationOrRing = isNotificationOrRing(mStreamType);
if (mNotificationOrRing) {
mRingerMode = mAudioManager.getRingerModeInternal();
}
mMaxStreamVolume = mAudioManager.getStreamMaxVolume(mStreamType);
mCallback = callback;
mOriginalStreamVolume = mAudioManager.getStreamVolume(mStreamType);
mMuted = mAudioManager.isStreamMute(mStreamType);
if (mCallback != null) {
mCallback.onMuted(mMuted);
}
if (defaultUri == null) { //根据不同流类型指定Uri资源
if (mStreamType == AudioManager.STREAM_RING) {
defaultUri = Settings.System.DEFAULT_RINGTONE_URI;
} else if (mStreamType == AudioManager.STREAM_NOTIFICATION) {
defaultUri = Settings.System.DEFAULT_NOTIFICATION_URI;
} else {
defaultUri = Settings.System.DEFAULT_ALARM_ALERT_URI;
}
}
mDefaultUri = defaultUri;
}