本文共 15792 字,大约阅读时间需要 52 分钟。
在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 函数里运行的顺序有差别;可以具体情况具体分析。