2017-09-28 | learn

Android Audio Focus

API 23 - android.media.AudioManager

请求 Audio Focus

1
public int requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint)

请求 audio focus 需要几个参数

  • OnAudioFocusChangeListener: 用于接收当前 audio focus 的变换
  • streamType: 代表所请求的 stream 类型
  • durationHint: 表示请求 focus 后,将要播放内容的形式
  • 返回值:AUDIOFOCUS_REQUEST_FAILED / AUDIOFOCUS_REQUEST_GRANTED

StreamType

相关的 Stream 类型包括 电话系统声音系统响铃音乐闹钟通知蓝牙 SCO(面向连接的同步连接 ,主要用于话音传输),强制系统音效(比如日本法律要求的相机音效),双音多频TTS 人工语音

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/** The audio stream for phone calls */
public static final int STREAM_VOICE_CALL = AudioSystem.STREAM_VOICE_CALL;
/** The audio stream for system sounds */
public static final int STREAM_SYSTEM = AudioSystem.STREAM_SYSTEM;
/** The audio stream for the phone ring */
public static final int STREAM_RING = AudioSystem.STREAM_RING;
/** The audio stream for music playback */
public static final int STREAM_MUSIC = AudioSystem.STREAM_MUSIC;
/** The audio stream for alarms */
public static final int STREAM_ALARM = AudioSystem.STREAM_ALARM;
/** The audio stream for notifications */
public static final int STREAM_NOTIFICATION = AudioSystem.STREAM_NOTIFICATION;
/** @hide The audio stream for phone calls when connected to bluetooth */
public static final int STREAM_BLUETOOTH_SCO = AudioSystem.STREAM_BLUETOOTH_SCO;
/** @hide The audio stream for enforced system sounds in certain countries (e.g camera in Japan) */
public static final int STREAM_SYSTEM_ENFORCED = AudioSystem.STREAM_SYSTEM_ENFORCED;
/** The audio stream for DTMF Tones */
public static final int STREAM_DTMF = AudioSystem.STREAM_DTMF;
/** @hide The audio stream for text to speech (TTS) */
public static final int STREAM_TTS = AudioSystem.STREAM_TTS;

DurationHint

请求操作

  • AUDIOFOCUS_GAIN_TRANSIENT: 临时播放请求
  • AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:临时播放请求,前台正在播放的程序可自行决定是否继续播放(通常会降低音量)
  • AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE :临时播放请求,独占。前台程序应停止播放
  • AUDIOFOCUS_GAIN:请求播放未知长度的音视频

接收到的通知

  • AUDIOFOCUS_LOSS:焦点丢失,应该停止播放
  • AUDIOFOCUS_LOSS_TRANSIENT:焦点临时丢失
  • AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:焦点临时丢失,可选择继续播放(通常会降低音量)
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
* @hide
* Used to indicate no audio focus has been gained or lost.
*/
public static final int AUDIOFOCUS_NONE = 0;

/**
* Used to indicate a gain of audio focus, or a request of audio focus, of unknown duration.
* @see OnAudioFocusChangeListener#onAudioFocusChange(int)
* @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
*/
public static final int AUDIOFOCUS_GAIN = 1;
/**
* Used to indicate a temporary gain or request of audio focus, anticipated to last a short
* amount of time. Examples of temporary changes are the playback of driving directions, or an
* event notification.
* @see OnAudioFocusChangeListener#onAudioFocusChange(int)
* @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
*/
public static final int AUDIOFOCUS_GAIN_TRANSIENT = 2;
/**
* Used to indicate a temporary request of audio focus, anticipated to last a short
* amount of time, and where it is acceptable for other audio applications to keep playing
* after having lowered their output level (also referred to as "ducking").
* Examples of temporary changes are the playback of driving directions where playback of music
* in the background is acceptable.
* @see OnAudioFocusChangeListener#onAudioFocusChange(int)
* @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
*/
public static final int AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK = 3;
/**
* Used to indicate a temporary request of audio focus, anticipated to last a short
* amount of time, during which no other applications, or system components, should play
* anything. Examples of exclusive and transient audio focus requests are voice
* memo recording and speech recognition, during which the system shouldn't play any
* notifications, and media playback should have paused.
* @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
*/
public static final int AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE = 4;
/**
* Used to indicate a loss of audio focus of unknown duration.
* @see OnAudioFocusChangeListener#onAudioFocusChange(int)
*/
public static final int AUDIOFOCUS_LOSS = -1 * AUDIOFOCUS_GAIN;
/**
* Used to indicate a transient loss of audio focus.
* @see OnAudioFocusChangeListener#onAudioFocusChange(int)
*/
public static final int AUDIOFOCUS_LOSS_TRANSIENT = -1 * AUDIOFOCUS_GAIN_TRANSIENT;
/**
* Used to indicate a transient loss of audio focus where the loser of the audio focus can
* lower its output volume if it wants to continue playing (also referred to as "ducking"), as
* the new focus owner doesn't require others to be silent.
* @see OnAudioFocusChangeListener#onAudioFocusChange(int)
*/
public static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK =
-1 * AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;

requestAudioFocus 做了什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public int requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint) {
int status = AUDIOFOCUS_REQUEST_FAILED;

try {
// status is guaranteed to be either AUDIOFOCUS_REQUEST_FAILED or
// AUDIOFOCUS_REQUEST_GRANTED as focus is requested without the
// AUDIOFOCUS_FLAG_DELAY_OK flag
status = requestAudioFocus(l,
new AudioAttributes.Builder()
.setInternalLegacyStreamType(streamType).build(),
durationHint,
0 /* flags, legacy behavior */);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Audio focus request denied due to ", e);
}

return status;
}

这段代码主要只有一个操作,将 streamType 封装成 AudioAttributes,接着调用自己的重载。中间各种重载略过不说,直接看最后一步。等下,有一步重载还是要说下的:在这里 ( AudioManager#L2398) AudioManager 将你传入的 listener 进行保存,使用 listener.toString() 作为 key,放到一个 HashMap 里,因为我们后面会把这个 key 传到 AudioService 里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// fun requestAudioFocus()
int status = AUDIOFOCUS_REQUEST_FAILED;
registerAudioFocusListener(l);
IAudioService service = getService();

// fun registerAudioFocusListener()
public void registerAudioFocusListener(OnAudioFocusChangeListener l) {
synchronized(mFocusListenerLock) {
if (mAudioFocusIdListenerMap.containsKey(getIdForAudioFocusListener(l))) {
return;
}
mAudioFocusIdListenerMap.put(getIdForAudioFocusListener(l), l);
}
}
// fun getIdForAudioFocusListener()
private String getIdForAudioFocusListener(OnAudioFocusChangeListener l) {
if (l == null) {
return new String(this.toString());
} else {
return new String(this.toString() + l.toString());
}
}

拿我们看下最后一步:

AudioManager#L2401

1
2
3
4
5
6
7
8
9
10
...
try {
status = service.requestAudioFocus(requestAttributes, durationHint, mICallBack,
mAudioFocusDispatcher, getIdForAudioFocusListener(l),
getContext().getOpPackageName() /* package name */, flags,
ap != null ? ap.cb() : null);
} catch (RemoteException e) {
Log.e(TAG, "Can't call requestAudioFocus() on AudioService:", e);
}
...

这个 service 是通过 AIDL 获取到的系统 Context.AUDIO_SERVICE 代理。对应实现代码在此 AudioService.java

1
private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>();

我们看到 AudioService 用一个 Stack 保存了 FocusRequester 对象,整个系统的 audio focus 请求都是保存在这里进行统一管理的。脑补一下应该是做些新请求来时,根据 clientId(就是那个 listener.toString()) 通知其他 client 改变状态啊,一个 client 释放焦点后从 stack 拿出一个授予 focus 啊这样的操作。


先写到这。