在android6.0.1版本上,使用了audioFocus的方式来抢占音频使用权;那么各个音频apk就要遵守 audioFocus的各种规定和用法。
下面分几个流程说明一下各个情况下的 audioFocus使用。
在audio系统中,上层各个功能部分的分层大概如下图所示:
其中,针对audioFocus功能,在当打开Music 的 apk时,这个apk主动向 AudioManager 申请 audioFocus,AudioManager再向audioService申请audioFocus;在这个机制中,AudioManager只是一个接口,是audio系统对上层 apk 的接口的封装,便于上层 apk调用。
具体流程看下面的代码分析:
一、直接播放音乐时,申请 audioFocus
1. Music 的 apk部分代码分析:
代码路径是:/packages/apps/Music/下;
当打开Music.apk,播放音乐的时候,首先执行初始化工作,这个初始化是整个播放器的初始化,不仅仅是audioFocus;
public void onCreate() {
super.onCreate();
if(!PermissionUtils.hasPermissions(this,PermissionUtils.getDesiredPermissions())){
System.out.println("==========cuijc3=========servcie oncreate() stop self");
stopSelf();
Log.e(LOGTAG, "zsm onCreate error");
return;
}
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
ComponentName rec = new ComponentName(getPackageName(),
MediaButtonIntentReceiver.class.getName());
mAudioManager.registerMediaButtonEventReceiver(rec);
从AudioManager接收消息的函数,包括其它功能;不仅仅是 audioFocus:
private Handler mMediaplayerHandler = new Handler() {
float mCurrentVolume = 1.0f;
@Override
public void handleMessage(Message msg) {
MusicUtils.debugLog("mMediaplayerHandler.handleMessage " + msg.what);
Log.e(LOGTAG, "zsm handleMessange");
switch (msg.what) {
case FADEDOWN:
mCurrentVolume -= .05f;
if (mCurrentVolume > .2f) {
mMediaplayerHandler.sendEmptyMessageDelayed(FADEDOWN, 10);
} else {
mCurrentVolume = .2f;
}
mPlayer.setVolume(mCurrentVolume);
break;
case FADEUP:
mCurrentVolume += .01f;
if (mCurrentVolume < 1.0f) {
mMediaplayerHandler.sendEmptyMessageDelayed(FADEUP, 10);
} else {
mCurrentVolume = 1.0f;
}
mPlayer.setVolume(mCurrentVolume);
break;
case SERVER_DIED:
if (mIsSupposedToBePlaying) {
gotoNext(true);
} else {
// the server died when we were idle, so just
// reopen the same song (it will start again
// from the beginning though when the user
// restarts)
openCurrentAndNext();
}
break;
case TRACK_WENT_TO_NEXT:
mPlayPos = mNextPlayPos;
if (mCursor != null) {
mCursor.close();
mCursor = null;
}
if (mPlayPos >= 0 && mPlayPos < mPlayList.length) {
mCursor = getCursorForId(mPlayList[mPlayPos]);
}
notifyChange(META_CHANGED);
updateNotification();
setNextTrack();
break;
case TRACK_ENDED:
if (mRepeatMode == REPEAT_CURRENT) {
seek(0);
play();
} else {
gotoNext(false);
}
break;
case RELEASE_WAKELOCK:
mWakeLock.release();
break;
case FOCUSCHANGE: // 这部分是audioFocus的状态改变
// This code is here so we can better synchronize it with the code that
// handles fade-in
Log.e(LOGTAG, "zsm AudioFocus: received FOCUSCHANGE");
switch (msg.arg1) {
case AudioManager.AUDIOFOCUS_LOSS:
Log.v(LOGTAG, "zsm AudioFocus: received AUDIOFOCUS_LOSS");
if(isPlaying()) {
mPausedByTransientLossOfFocus = false;
}
pause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK");
mMediaplayerHandler.removeMessages(FADEUP);
mMediaplayerHandler.sendEmptyMessage(FADEDOWN);
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
Log.v(LOGTAG, "zsm AudioFocus: received AUDIOFOCUS_LOSS_TRANSIENT");
if(isPlaying()) {
Log.v(LOGTAG, "zsm AudioFocus: received AUDIOFOCUS_LOSS_TRANSIENT-1");
mPausedByTransientLossOfFocus = true;
}
pause();
break;
case AudioManager.AUDIOFOCUS_GAIN:
Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_GAIN");
if(!isPlaying() && mPausedByTransientLossOfFocus) {
Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_GAIN-1");
mPausedByTransientLossOfFocus = false;
mCurrentVolume = 0f;
mPlayer.setVolume(mCurrentVolume);
play(); // also queues a fade-in
} else {
Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_GAIN-2");
mMediaplayerHandler.removeMessages(FADEDOWN);
mMediaplayerHandler.sendEmptyMessage(FADEUP);
}
break;
default:
Log.e(LOGTAG, "Unknown audio focus change code");
}
break;
default:
break;
}
}
};
设置,接收消息的代码:
private OnAudioFocusChangeListener mAudioFocusListener = new OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
mMediaplayerHandler.obtainMessage(FOCUSCHANGE, focusChange, 0).sendToTarget();
}
};
这样,当底层AudioManager有什么消息来的话,就会调用那个消息函数,再根据不同的消息进行处理。
在播放之前首先申请audioFocus:
mAudioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
2. AudioManager部分的代码分析:
上面 apk 申请audioFocus时会调用到audioManager里的函数,代码如下:
代码路径:/frameworks/base/media/java/android/media/AudioManager.java
经过几次 AudioManager.java 调用后会调用到以下代码函数:
@SystemApi
public int requestAudioFocus(OnAudioFocusChangeListener l,
@NonNull AudioAttributes requestAttributes,
int durationHint,
int flags,
AudioPolicy ap) throws IllegalArgumentException {
// parameter checking
Log.e(TAG, "requestAudioFocus-3: durationHint:" + durationHint);
if (requestAttributes == null) {
throw new IllegalArgumentException("Illegal null AudioAttributes argument");
}
if ((durationHint < AUDIOFOCUS_GAIN) ||
(durationHint > AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)) {
throw new IllegalArgumentException("Invalid duration hint");
}
if (flags != (flags & AUDIOFOCUS_FLAGS_SYSTEM)) {
throw new IllegalArgumentException("Illegal flags 0x"
+ Integer.toHexString(flags).toUpperCase());
}
if (((flags & AUDIOFOCUS_FLAG_DELAY_OK) == AUDIOFOCUS_FLAG_DELAY_OK) && (l == null)) {
throw new IllegalArgumentException(
"Illegal null focus listener when flagged as accepting delayed focus grant");
}
if (((flags & AUDIOFOCUS_FLAG_LOCK) == AUDIOFOCUS_FLAG_LOCK) && (ap == null)) {
throw new IllegalArgumentException(
"Illegal null audio policy when locking audio focus");
}
Log.e(TAG, "requestAudioFocus-4");
int status = AUDIOFOCUS_REQUEST_FAILED;
registerAudioFocusListener(l);
IAudioService service = getService();
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);
}
return status;
}
在以上函数中,注册 AudioFocusListener; 然后调用AudioService 里的 requestAudioFocus函数进一步申请。
3. AudioService 里的代码:
代码路径:/frameworks/base/services/core/java/com/android/server/audio/AudioService.java
顺序调用到的函数如下:
//==========================================================================================
// Audio Focus
//==========================================================================================
public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,
IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
IAudioPolicyCallback pcb) {
// permission checks
Log.e(TAG, "zsm: requestAudioFocus-1");
if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) == AudioManager.AUDIOFOCUS_FLAG_LOCK) {
if (AudioSystem.IN_VOICE_COMM_FOCUS_ID.equals(clientId)) {
if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)) {
Log.e(TAG, "Invalid permission to (un)lock audio focus", new Exception());
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
} else {
// only a registered audio policy can be used to lock focus
synchronized (mAudioPolicies) {
if (!mAudioPolicies.containsKey(pcb.asBinder())) {
Log.e(TAG, "Invalid unregistered AudioPolicy to (un)lock audio focus");
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
}
}
}
Log.e(TAG, "zsm: requestAudioFocus-2: durationHint: " + durationHint);
return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
clientId, callingPackageName, flags);
}
在最后一行,调用到 MediaFocusControl 里的 requestAudioFocus 函数。 真正的申请audioFocus在 MediaFocusControl里。
4. MediaFocusControl里的代码:
代码路径:/frameworks/base/services/core/java/com/android/server/audio/MediaFocusControl.java
protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags) {
Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId + " req=" + focusChangeHint +
"flags=0x" + Integer.toHexString(flags));
// we need a valid binder callback for clients
if (!cb.pingBinder()) {
Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
if (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
callingPackageName) != AppOpsManager.MODE_ALLOWED) {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
synchronized(mAudioFocusLock) {
boolean focusGrantDelayed = false;
if (!canReassignAudioFocus()) {
if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
} else {
// request has AUDIOFOCUS_FLAG_DELAY_OK: focus can't be
// granted right now, so the requester will be inserted in the focus stack
// to receive focus later
focusGrantDelayed = true;
}
}
// handle the potential premature death of the new holder of the focus
// (premature death == death before abandoning focus)
// Register for client death notification
AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
try {
cb.linkToDeath(afdh, 0);
} catch (RemoteException e) {
// client has already died!
Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death");
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) {
// if focus is already owned by this client and the reason for acquiring the focus
// hasn't changed, don't do anything
final FocusRequester fr = mFocusStack.peek();
if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) {
// unlink death handler so it can be gc'ed.
// linkToDeath() creates a JNI global reference preventing collection.
cb.unlinkToDeath(afdh, 0);
notifyExtPolicyFocusGrant_syncAf(fr.toAudioFocusInfo(),
AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
Log.e(TAG, "zsm: requestAudioFocus -> GRANTED");
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
// the reason for the audio focus request has changed: remove the current top of
// stack and respond as if we had a new focus owner
if (!focusGrantDelayed) {
mFocusStack.pop();
// the entry that was "popped" is the same that was "peeked" above
fr.release();
}
}
// focus requester might already be somewhere below in the stack, remove it
removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/);
final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,
clientId, afdh, callingPackageName, Binder.getCallingUid(), this);
if (focusGrantDelayed) {
// focusGrantDelayed being true implies we can't reassign focus right now
// which implies the focus stack is not empty.
final int requestResult = pushBelowLockedFocusOwners(nfr);
if (requestResult != AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), requestResult);
}
return requestResult;
} else {
// propagate the focus change through the stack
if (!mFocusStack.empty()) {
propagateFocusLossFromGain_syncAf(focusChangeHint);
}
// push focus requester at the top of the audio focus stack
mFocusStack.push(nfr);
}
notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(),
AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
}//synchronized(mAudioFocusLock)
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
首次申请audioFocus,因队列为空,故会执行到最后那行代码,返回 AudioManager.AUDIOFOCUS_REQUEST_GRANTED. 表明申请成功。
AudioManager里的申请成功后,会回到播放器apk里的代码处,这整个过程是阻塞式的。
至此,播放器申请audioFocus结束,代码继续向下执行,开始播放音乐。
如果在本播放apk申请之前已经有其它的播放器正在播放音乐,则本播放器申请audioFocus的时候,上面的主要流程都一样;只是在 MediaFocusControl.java 里的这个requestAudioFocus 函数里运行的顺序有差别;可以具体情况具体分析。