Android音频焦点详解(下)——源码详解

耽搁了几天,最近一直在忙找工作的事情,今天把这篇文章补上。


本文基于Android7.1.1版本进行分析,主要涉及以下几个文件: 
1 AudioManager –> /frameworks/base/media/java/android/media/ 
2 AudioService –> /frameworks/base/services/core/java/com/android/server/audio/ 
3 MediaFocusControl –>/frameworks/base/services/core/java/com/android/server/audio/ 
4 FocusRequester –> /frameworks/base/services/core/java/com/android/server/audio/ 
5 AudioAttributes –> /frameworks/base/media/java/android/media/ 
6 AudioFocusInfo –> /frameworks/base/media/java/android/media/ 
7 AudioSystem –> /frameworks/base/media/java/android/media/

之前一直用的是4.4的源码,这两天看了下7.1的源码发现这块内容改动还是挺大的,主要是新增了几个文件,并且对MediaFocusControl类进行了瘦身,代码从2700多行减到了500多行。 
我们从入口方法requestAudioFocus开始,还记得我们是怎么使用该方法的么? 
通过Audio Manager的对象来调用

mAudioManager.requestAudioFocus(mAudioFocusChange, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
  • 1

具体请参考我的上篇博客点击这里


从AudioManager开始(有些方法里代码较多,只贴出来部分关键代码,下同)

public int requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint) {
    int status = AUDIOFOCUS_REQUEST_FAILED;
    try {
        //调用内部重载后的方法
        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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

根据传进来的streamType,构造了一个AudioAttributes对象向下传递,这个AudioAttributes主要是存储了一些音频流信息的属性,后面会用到。接着看

@SystemApi
public int requestAudioFocus(OnAudioFocusChangeListener l,
                             @NonNull AudioAttributes requestAttributes,
                             int durationHint,
                             int flags) throws IllegalArgumentException {
    return requestAudioFocus(l, requestAttributes, durationHint,
            flags & AUDIOFOCUS_FLAGS_APPS,
            null /* no AudioPolicy*/);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这里面对falgs进行了与操作,由于之前传进来的是0,所以转换后的结果还是0。接着看

@SystemApi
public int requestAudioFocus(OnAudioFocusChangeListener l,
                             @NonNull AudioAttributes requestAttributes,
                             int durationHint,
                             int flags,
                             AudioPolicy ap) throws IllegalArgumentException {
    // 参数检查
    //...

    int status = AUDIOFOCUS_REQUEST_FAILED;
    registerAudioFocusListener(l);
    //获取AudioService实例,这里采用了binder通信,我们只需要知道从此处开始将会进入AudioServie的requestAudioFocus方法
    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) {
        throw e.rethrowFromSystemServer();
    }
    return status;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

这里面重点看一下 registerAudioFocusListener(l);

private final HashMap<String, OnAudioFocusChangeListener> mAudioFocusIdListenerMap =
            new HashMap<String, OnAudioFocusChangeListener>();

public void registerAudioFocusListener(OnAudioFocusChangeListener l) {
    synchronized (mFocusListenerLock) {
        if (mAudioFocusIdListenerMap.containsKey(getIdForAudioFocusListener(l))) {
            return;
        }
        mAudioFocusIdListenerMap.put(getIdForAudioFocusListener(l), l);
    }
}

private String getIdForAudioFocusListener(OnAudioFocusChangeListener l) {
    if (l == null) {
        return new String(this.toString());
    } else {
        return new String(this.toString() + l.toString());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

这段代码比较好理解了,我们根据“l”来生成一个key,存储在了mAudioFocusIdListenerMap对象中,而值就是OnAudioFocusChangeListener的对象。这里用了HashMap以保证key的唯一性。至于这个map有什么用呢,先不要着急,在后面会用到的。 
我们在调用AudioService的requestAudioFocus时传入了一个 mAudioFocusDispatcher参数,这个又有什么用呢?先不要着急等后面用到的时候再来看。

好了,我们现在进入AudioService的requestAudioFocus继续分析。

慢着,AudioManager中还有一个和requestAudioFocus相关的方法,那就是requestAudioFocusForCall,通过名字可以知道这是跟电话相关的接口,看下源码

public void requestAudioFocusForCall(int streamType, int durationHint) {
    IAudioService service = getService();
    try {
        service.requestAudioFocus(new AudioAttributes.Builder()
                        .setInternalLegacyStreamType(streamType).build(),
                durationHint, mICallBack, null,
                AudioSystem.IN_VOICE_COMM_FOCUS_ID,
                getContext().getOpPackageName(),
                AUDIOFOCUS_FLAG_LOCK,
                null /* policy token */);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在代码中全局搜索requestAudioFocusForCall,发现只有CallManager中有调用,而该方法被增加了@hide注解,不过第三方应用可以通过一些特殊方式来调用,这里就不展开讲解了。 
留意一下AudioSystem.IN_VOICE_COMM_FOCUS_ID和AUDIOFOCUS_FLAG_LOCK后面会用到。

不知道大家有没有晕呢!我们先来梳理一下吧!以上的代码均是在AudioManager中,其中requestAudioFocus重载了三次,但只有一个是对外开放的。额外看到了一个为电话而生的requestAudioFocusForCall。


AudioService直接上源码

public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,
                             IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
                             IAudioPolicyCallback pcb) {
    //权限检查,这里面就用到了AudioSystem.IN_VOICE_COMM_FOCUS_ID
    //也就是说如果我们的clientId等于AudioSystem.IN_VOICE_COMM_FOCUS_ID
    //要申请MODIFY_PHONE_STATE的权限,否则会申请焦点失败。
    return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
            clientId, callingPackageName, flags);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

AudioService只是做了中转,并没有做实际的操作,具体实现都是在MediaFocusControl中 
下面我们进入MediaFocusControl中

protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
                                IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags) {
    //...基础检查

    //这一块定义了局部变量focusGrantDelayed,从名字上可以知道延迟申请焦点的意思
    //而且只有当canReassignAudioFocus()返回true的时候,focusGrantDelayed才为true,也就是需要延迟申请,详见下方注解1.
    synchronized (mAudioFocusLock) {
            boolean focusGrantDelayed = false;
            if (!canReassignAudioFocus()) {
            if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) {
                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
            } else {
                focusGrantDelayed = true;
            }
        }
        //如果mFocusStack不为空,并且栈顶的clientId与要申请焦点的clientId相同
        if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) {
            //得到栈顶元素的FocusRequester对象
            final FocusRequester fr = mFocusStack.peek();
            if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) {
                //如果申请的时长和flags都相同,则表示重复申请,直接返回成功
                return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
            }
           //非延迟申请
            if (!focusGrantDelayed) {
                //如果如果申请的时长和flags有一个不相同,则认为需要重新申请,此时需要将栈顶的元素出栈
                mFocusStack.pop();
                fr.release();
            }
        }
        /*说了这么多可能不太好理解,这里举个��
        * 先调用mAudioManager.requestAudioFocus(mAudioFocusChange, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
        * 再调用mAudioManager.requestAudioFocus(mAudioFocusChange, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
        * 两次的申请时长不同,因此会将前一次的申请出栈,然后再处理新的申请
        **/

        //移除可能在栈中其他位置存在着相同clientId的元素
        removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/);
        //构造FocusRequester对象,详见下方注解2.
        final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,
                clientId, afdh, callingPackageName, Binder.getCallingUid(), this);
        //我们前面分析了什么情况下focusGrantDelayed为true,这里重复一遍,也就是我们在打电话的过程中,音乐去申请焦点。
        if (focusGrantDelayed) {
            //将其插入栈中,什么位置呢?遍历mFocusStack,从栈顶开始isLockedFocusOwner(前面介绍过该放法)为true的元素的下方。
            final int requestResult = pushBelowLockedFocusOwners(nfr);
            return requestResult;
        } else {
            if (!mFocusStack.empty()) {
                //该方法很重要,通知栈中其他元素丢失焦点,详见下方注解3.
                propagateFocusLossFromGain_syncAf(focusChangeHint);
            }
            //将FocusRequester对象压入栈中
            mFocusStack.push(nfr);
        }
    }
    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
  • 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

注解: 
1 这里面出现了mFocusStack变量,栈结构后进先出,用来维护各client的申请和释放, 
当满足mFocusStack不为空,并且当前栈顶(peek得到栈顶元素,但是并未出栈)的clientId为AudioSystem.IN_VOICE_COMM_FOCUS_ID,这个熟悉吧!或者fr.isLockedFocusOwner,这个FocusOwner又是什么鬼呢!我们需要进入FocusRequester类中来看,这里的mGrantFlags是在FocusRequester的构造方法中初始化的,其实就是前面传进来的flags,而AUDIOFOCUS_FLAG_LOCK也熟悉吧,没印象的回头看一下requestAudioFocusForCall方法我叫你们留意的两个参数。这段代码的最终含义就是如果正在打电话的过程中,其他应用申请焦点会延迟申请。

//MediaFocusControl.java
private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>();
private boolean canReassignAudioFocus() {
    if (!mFocusStack.isEmpty() && isLockedFocusOwner(mFocusStack.peek())) {
        return false;
    }
    return true;
}

private boolean isLockedFocusOwner(FocusRequester fr) {
    return (fr.hasSameClient(AudioSystem.IN_VOICE_COMM_FOCUS_ID) || fr.isLockedFocusOwner());
}

//FocusRequester.java
boolean isLockedFocusOwner() {
    return ((mGrantFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

2 该方法中初始化了很多个变量,大概有个印象就好,我们在注解3中会详细讲解其中几个关键变量

FocusRequester(AudioAttributes aa, int focusRequest, int grantFlags,
               IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
               String pn, int uid, @NonNull MediaFocusControl ctlr) {
    mAttributes = aa;
    mFocusDispatcher = afl;
    mSourceRef = source;
    mClientId = id;
    mDeathHandler = hdlr;
    mPackageName = pn;
    mCallingUid = uid;
    mFocusGainRequest = focusRequest;
    mGrantFlags = grantFlags;
    mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
    mFocusController = ctlr;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

3 通知栈中其他元素丢失焦点流程

//遍历mFocusStack,调用FocusRequester对象的handleExternalFocusGain方法
private void propagateFocusLossFromGain_syncAf(int focusGain) {
    Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
    while (stackIterator.hasNext()) {
        stackIterator.next().handleExternalFocusGain(focusGain);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

stackIterator.next()得到的是FocusRequester对象,因此查看FocusRequester中handleExternalFocusGain的源码,这里面假设我们传进来的参数是AudioManager.AUDIOFOCUS_GAIN

void handleExternalFocusGain(int focusGain) {
    //下面分别看一下focusLossForGainRequest和handleFocusLoss
    int focusLoss = focusLossForGainRequest(focusGain);
    handleFocusLoss(focusLoss);
}
/**
 * 这个方法比较长,主要关注两个变量gainRequest和mFocusLossReceived
 * mFocusLossReceived这个值是多少呢!我们发现在注解2中FocusRequester的构造方法中进行的赋值
 * mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
 * gainRequest这个是我们传进来的,例如AudioManager.AUDIOFOCUS_GAIN
 * return AudioManager.AUDIOFOCUS_LOSS
 * 若我们传进来的参数是AudioManager.AUDIOFOCUS_GAIN_TRANSIENT
 * 则return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
 */
private int focusLossForGainRequest(int gainRequest) {
    switch (gainRequest) {
        case AudioManager.AUDIOFOCUS_GAIN:
            switch (mFocusLossReceived) {
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                case AudioManager.AUDIOFOCUS_LOSS:
                case AudioManager.AUDIOFOCUS_NONE:
                    return AudioManager.AUDIOFOCUS_LOSS;
            }
        case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
        case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
            switch (mFocusLossReceived) {
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                case AudioManager.AUDIOFOCUS_NONE:
                    return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
                case AudioManager.AUDIOFOCUS_LOSS:
                    return AudioManager.AUDIOFOCUS_LOSS;
            }
        case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
            switch (mFocusLossReceived) {
                case AudioManager.AUDIOFOCUS_NONE:
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                    return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                    return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
                case AudioManager.AUDIOFOCUS_LOSS:
                    return AudioManager.AUDIOFOCUS_LOSS;
            }
        default:
            Log.e(TAG, "focusLossForGainRequest() for invalid focus request " + gainRequest);
            return AudioManager.AUDIOFOCUS_NONE;
    }
}

void handleFocusLoss(int focusLoss) {
    try {
        if (focusLoss != mFocusLossReceived) {
            mFocusLossReceived = focusLoss;
            //...
            final IAudioFocusDispatcher fd = mFocusDispatcher;
            if (fd != null) {
                fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId);
            }
        }
    } catch (android.os.RemoteException e) {
        Log.e(TAG, "Failure to signal loss of audio focus due to:", e);
    }
}
  • 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
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

重点看一下handleFocusLoss方法的

fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId);
  • 1

通过mFocusDispatcher对象调用了dispatchAudioFocusChange方法,将mFocusLossReceived和mClientId传了进去。现在我们回头一步步看mFocusDispatcher是如何传进来的。 
3.1 FocusRequester构造方法的第四个参数IAudioFocusDispatcher afl 
3.2 MediaFocusControl的requestAudioFocus方法的第四个参数IAudioFocusDispatcher fd 
3.3 AudioService的requestAudioFocus方法的第四个参数IAudioFocusDispatcher fd 
最终我们在AudioManager中找到了

private final IAudioFocusDispatcher mAudioFocusDispatcher = new IAudioFocusDispatcher.Stub() {
    public void dispatchAudioFocusChange(int focusChange, String id) {
        final Message m = mServiceEventHandlerDelegate.getHandler().obtainMessage(
                MSSG_FOCUS_CHANGE/*what*/, focusChange/*arg1*/, 0/*arg2 ignored*/, id/*obj*/);
        mServiceEventHandlerDelegate.getHandler().sendMessage(m);
    }
};

private class ServiceEventHandlerDelegate {
    private final Handler mHandler;
    ServiceEventHandlerDelegate(Handler handler) {
        Looper looper;
        if (handler == null) {
            if ((looper = Looper.myLooper()) == null) {
                looper = Looper.getMainLooper();
            }
        } else {
            looper = handler.getLooper();
        }
        if (looper != null) {
            // implement the event handler delegate to receive events from audio service
            mHandler = new Handler(looper) {
                @Override
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                        case MSSG_FOCUS_CHANGE:
                            OnAudioFocusChangeListener listener = null;
                            synchronized (mFocusListenerLock) {
                                listener = findFocusListener((String) msg.obj);
                            }
                            if (listener != null) {
                                Log.d(TAG, "AudioManager dispatching onAudioFocusChange("
                                        + msg.arg1 + ") for " + msg.obj);
                                listener.onAudioFocusChange(msg.arg1);
                            }
                            break;
                         //***
                    }
                }
            };
        } else {
            mHandler = null;
        }
    }
    Handler getHandler() {
        return mHandler;
    }
}
  • 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

在dispatchAudioFocusChange方法中通过mServiceEventHandlerDelegate将事件分发到了另外的线程中,这也是让AudioService从事件分发中抽离出来

OnAudioFocusChangeListener listener = null;
synchronized (mFocusListenerLock) {
    //msg.obj就是clientId
    listener = findFocusListener((String) msg.obj);
}
if (listener != null) {
    //我们得到了listener之后回调onAudioFocusChange
    //如果当前申请的焦点时长为AudioManager.AUDIOFOCUS_GAIN,则msg.arg1=AudioManager.AUDIOFOCUS_LOSS
    //如果当前申请的焦点时长为AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,则msg.arg1=AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
    //这个转换是在FocusRequester的focusLossForGainRequest方法中进行的
    listener.onAudioFocusChange(msg.arg1);
}
//根据clientId在mAudioFocusIdListenerMap中返回对应的OnAudioFocusChangeListener
//mAudioFocusIdListenerMap是在我们最初调用requestAudioFocus时存储的,不记得的童鞋可以回头看一下
private OnAudioFocusChangeListener findFocusListener(String id) {
    return mAudioFocusIdListenerMap.get(id);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

至此我们终于将焦点改变的消息通知到了应用层注册的onAudioFocusChange方法中。 
requestAudioFocus方法分析完了,abandonAudioFocus方法完全相同的流程,这里就不做过多介绍了,有兴趣的童鞋可以自己看这源码走一遍流程就可以理解了。


好了,综合前一篇的焦点机制的应用,加上这篇的源码分析,音频焦点也就告一段落了,如果发现有分析错误的地方请及时指出。后续有时间还会再写一些关于音频的文章。

前两天拍的

版权声明:本文为博主原创文章,未经博主允许不得转载。

耽搁了几天,最近一直在忙找工作的事情,今天把这篇文章补上。


本文基于Android7.1.1版本进行分析,主要涉及以下几个文件: 
1 AudioManager –> /frameworks/base/media/java/android/media/ 
2 AudioService –> /frameworks/base/services/core/java/com/android/server/audio/ 
3 MediaFocusControl –>/frameworks/base/services/core/java/com/android/server/audio/ 
4 FocusRequester –> /frameworks/base/services/core/java/com/android/server/audio/ 
5 AudioAttributes –> /frameworks/base/media/java/android/media/ 
6 AudioFocusInfo –> /frameworks/base/media/java/android/media/ 
7 AudioSystem –> /frameworks/base/media/java/android/media/

之前一直用的是4.4的源码,这两天看了下7.1的源码发现这块内容改动还是挺大的,主要是新增了几个文件,并且对MediaFocusControl类进行了瘦身,代码从2700多行减到了500多行。 
我们从入口方法requestAudioFocus开始,还记得我们是怎么使用该方法的么? 
通过Audio Manager的对象来调用

mAudioManager.requestAudioFocus(mAudioFocusChange, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
  • 1

具体请参考我的上篇博客点击这里


从AudioManager开始(有些方法里代码较多,只贴出来部分关键代码,下同)

public int requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint) {
    int status = AUDIOFOCUS_REQUEST_FAILED;
    try {
        //调用内部重载后的方法
        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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

根据传进来的streamType,构造了一个AudioAttributes对象向下传递,这个AudioAttributes主要是存储了一些音频流信息的属性,后面会用到。接着看

@SystemApi
public int requestAudioFocus(OnAudioFocusChangeListener l,
                             @NonNull AudioAttributes requestAttributes,
                             int durationHint,
                             int flags) throws IllegalArgumentException {
    return requestAudioFocus(l, requestAttributes, durationHint,
            flags & AUDIOFOCUS_FLAGS_APPS,
            null /* no AudioPolicy*/);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这里面对falgs进行了与操作,由于之前传进来的是0,所以转换后的结果还是0。接着看

@SystemApi
public int requestAudioFocus(OnAudioFocusChangeListener l,
                             @NonNull AudioAttributes requestAttributes,
                             int durationHint,
                             int flags,
                             AudioPolicy ap) throws IllegalArgumentException {
    // 参数检查
    //...

    int status = AUDIOFOCUS_REQUEST_FAILED;
    registerAudioFocusListener(l);
    //获取AudioService实例,这里采用了binder通信,我们只需要知道从此处开始将会进入AudioServie的requestAudioFocus方法
    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) {
        throw e.rethrowFromSystemServer();
    }
    return status;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

这里面重点看一下 registerAudioFocusListener(l);

private final HashMap<String, OnAudioFocusChangeListener> mAudioFocusIdListenerMap =
            new HashMap<String, OnAudioFocusChangeListener>();

public void registerAudioFocusListener(OnAudioFocusChangeListener l) {
    synchronized (mFocusListenerLock) {
        if (mAudioFocusIdListenerMap.containsKey(getIdForAudioFocusListener(l))) {
            return;
        }
        mAudioFocusIdListenerMap.put(getIdForAudioFocusListener(l), l);
    }
}

private String getIdForAudioFocusListener(OnAudioFocusChangeListener l) {
    if (l == null) {
        return new String(this.toString());
    } else {
        return new String(this.toString() + l.toString());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

这段代码比较好理解了,我们根据“l”来生成一个key,存储在了mAudioFocusIdListenerMap对象中,而值就是OnAudioFocusChangeListener的对象。这里用了HashMap以保证key的唯一性。至于这个map有什么用呢,先不要着急,在后面会用到的。 
我们在调用AudioService的requestAudioFocus时传入了一个 mAudioFocusDispatcher参数,这个又有什么用呢?先不要着急等后面用到的时候再来看。

好了,我们现在进入AudioService的requestAudioFocus继续分析。

慢着,AudioManager中还有一个和requestAudioFocus相关的方法,那就是requestAudioFocusForCall,通过名字可以知道这是跟电话相关的接口,看下源码

public void requestAudioFocusForCall(int streamType, int durationHint) {
    IAudioService service = getService();
    try {
        service.requestAudioFocus(new AudioAttributes.Builder()
                        .setInternalLegacyStreamType(streamType).build(),
                durationHint, mICallBack, null,
                AudioSystem.IN_VOICE_COMM_FOCUS_ID,
                getContext().getOpPackageName(),
                AUDIOFOCUS_FLAG_LOCK,
                null /* policy token */);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在代码中全局搜索requestAudioFocusForCall,发现只有CallManager中有调用,而该方法被增加了@hide注解,不过第三方应用可以通过一些特殊方式来调用,这里就不展开讲解了。 
留意一下AudioSystem.IN_VOICE_COMM_FOCUS_ID和AUDIOFOCUS_FLAG_LOCK后面会用到。

不知道大家有没有晕呢!我们先来梳理一下吧!以上的代码均是在AudioManager中,其中requestAudioFocus重载了三次,但只有一个是对外开放的。额外看到了一个为电话而生的requestAudioFocusForCall。


AudioService直接上源码

public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,
                             IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
                             IAudioPolicyCallback pcb) {
    //权限检查,这里面就用到了AudioSystem.IN_VOICE_COMM_FOCUS_ID
    //也就是说如果我们的clientId等于AudioSystem.IN_VOICE_COMM_FOCUS_ID
    //要申请MODIFY_PHONE_STATE的权限,否则会申请焦点失败。
    return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
            clientId, callingPackageName, flags);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

AudioService只是做了中转,并没有做实际的操作,具体实现都是在MediaFocusControl中 
下面我们进入MediaFocusControl中

protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
                                IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags) {
    //...基础检查

    //这一块定义了局部变量focusGrantDelayed,从名字上可以知道延迟申请焦点的意思
    //而且只有当canReassignAudioFocus()返回true的时候,focusGrantDelayed才为true,也就是需要延迟申请,详见下方注解1.
    synchronized (mAudioFocusLock) {
            boolean focusGrantDelayed = false;
            if (!canReassignAudioFocus()) {
            if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) {
                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
            } else {
                focusGrantDelayed = true;
            }
        }
        //如果mFocusStack不为空,并且栈顶的clientId与要申请焦点的clientId相同
        if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) {
            //得到栈顶元素的FocusRequester对象
            final FocusRequester fr = mFocusStack.peek();
            if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) {
                //如果申请的时长和flags都相同,则表示重复申请,直接返回成功
                return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
            }
           //非延迟申请
            if (!focusGrantDelayed) {
                //如果如果申请的时长和flags有一个不相同,则认为需要重新申请,此时需要将栈顶的元素出栈
                mFocusStack.pop();
                fr.release();
            }
        }
        /*说了这么多可能不太好理解,这里举个��
        * 先调用mAudioManager.requestAudioFocus(mAudioFocusChange, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
        * 再调用mAudioManager.requestAudioFocus(mAudioFocusChange, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
        * 两次的申请时长不同,因此会将前一次的申请出栈,然后再处理新的申请
        **/

        //移除可能在栈中其他位置存在着相同clientId的元素
        removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/);
        //构造FocusRequester对象,详见下方注解2.
        final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,
                clientId, afdh, callingPackageName, Binder.getCallingUid(), this);
        //我们前面分析了什么情况下focusGrantDelayed为true,这里重复一遍,也就是我们在打电话的过程中,音乐去申请焦点。
        if (focusGrantDelayed) {
            //将其插入栈中,什么位置呢?遍历mFocusStack,从栈顶开始isLockedFocusOwner(前面介绍过该放法)为true的元素的下方。
            final int requestResult = pushBelowLockedFocusOwners(nfr);
            return requestResult;
        } else {
            if (!mFocusStack.empty()) {
                //该方法很重要,通知栈中其他元素丢失焦点,详见下方注解3.
                propagateFocusLossFromGain_syncAf(focusChangeHint);
            }
            //将FocusRequester对象压入栈中
            mFocusStack.push(nfr);
        }
    }
    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
  • 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

注解: 
1 这里面出现了mFocusStack变量,栈结构后进先出,用来维护各client的申请和释放, 
当满足mFocusStack不为空,并且当前栈顶(peek得到栈顶元素,但是并未出栈)的clientId为AudioSystem.IN_VOICE_COMM_FOCUS_ID,这个熟悉吧!或者fr.isLockedFocusOwner,这个FocusOwner又是什么鬼呢!我们需要进入FocusRequester类中来看,这里的mGrantFlags是在FocusRequester的构造方法中初始化的,其实就是前面传进来的flags,而AUDIOFOCUS_FLAG_LOCK也熟悉吧,没印象的回头看一下requestAudioFocusForCall方法我叫你们留意的两个参数。这段代码的最终含义就是如果正在打电话的过程中,其他应用申请焦点会延迟申请。

//MediaFocusControl.java
private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>();
private boolean canReassignAudioFocus() {
    if (!mFocusStack.isEmpty() && isLockedFocusOwner(mFocusStack.peek())) {
        return false;
    }
    return true;
}

private boolean isLockedFocusOwner(FocusRequester fr) {
    return (fr.hasSameClient(AudioSystem.IN_VOICE_COMM_FOCUS_ID) || fr.isLockedFocusOwner());
}

//FocusRequester.java
boolean isLockedFocusOwner() {
    return ((mGrantFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

2 该方法中初始化了很多个变量,大概有个印象就好,我们在注解3中会详细讲解其中几个关键变量

FocusRequester(AudioAttributes aa, int focusRequest, int grantFlags,
               IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
               String pn, int uid, @NonNull MediaFocusControl ctlr) {
    mAttributes = aa;
    mFocusDispatcher = afl;
    mSourceRef = source;
    mClientId = id;
    mDeathHandler = hdlr;
    mPackageName = pn;
    mCallingUid = uid;
    mFocusGainRequest = focusRequest;
    mGrantFlags = grantFlags;
    mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
    mFocusController = ctlr;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

3 通知栈中其他元素丢失焦点流程

//遍历mFocusStack,调用FocusRequester对象的handleExternalFocusGain方法
private void propagateFocusLossFromGain_syncAf(int focusGain) {
    Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
    while (stackIterator.hasNext()) {
        stackIterator.next().handleExternalFocusGain(focusGain);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

stackIterator.next()得到的是FocusRequester对象,因此查看FocusRequester中handleExternalFocusGain的源码,这里面假设我们传进来的参数是AudioManager.AUDIOFOCUS_GAIN

void handleExternalFocusGain(int focusGain) {
    //下面分别看一下focusLossForGainRequest和handleFocusLoss
    int focusLoss = focusLossForGainRequest(focusGain);
    handleFocusLoss(focusLoss);
}
/**
 * 这个方法比较长,主要关注两个变量gainRequest和mFocusLossReceived
 * mFocusLossReceived这个值是多少呢!我们发现在注解2中FocusRequester的构造方法中进行的赋值
 * mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
 * gainRequest这个是我们传进来的,例如AudioManager.AUDIOFOCUS_GAIN
 * return AudioManager.AUDIOFOCUS_LOSS
 * 若我们传进来的参数是AudioManager.AUDIOFOCUS_GAIN_TRANSIENT
 * 则return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
 */
private int focusLossForGainRequest(int gainRequest) {
    switch (gainRequest) {
        case AudioManager.AUDIOFOCUS_GAIN:
            switch (mFocusLossReceived) {
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                case AudioManager.AUDIOFOCUS_LOSS:
                case AudioManager.AUDIOFOCUS_NONE:
                    return AudioManager.AUDIOFOCUS_LOSS;
            }
        case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
        case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
            switch (mFocusLossReceived) {
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                case AudioManager.AUDIOFOCUS_NONE:
                    return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
                case AudioManager.AUDIOFOCUS_LOSS:
                    return AudioManager.AUDIOFOCUS_LOSS;
            }
        case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
            switch (mFocusLossReceived) {
                case AudioManager.AUDIOFOCUS_NONE:
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                    return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                    return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
                case AudioManager.AUDIOFOCUS_LOSS:
                    return AudioManager.AUDIOFOCUS_LOSS;
            }
        default:
            Log.e(TAG, "focusLossForGainRequest() for invalid focus request " + gainRequest);
            return AudioManager.AUDIOFOCUS_NONE;
    }
}

void handleFocusLoss(int focusLoss) {
    try {
        if (focusLoss != mFocusLossReceived) {
            mFocusLossReceived = focusLoss;
            //...
            final IAudioFocusDispatcher fd = mFocusDispatcher;
            if (fd != null) {
                fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId);
            }
        }
    } catch (android.os.RemoteException e) {
        Log.e(TAG, "Failure to signal loss of audio focus due to:", e);
    }
}
  • 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
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

重点看一下handleFocusLoss方法的

fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId);
  • 1

通过mFocusDispatcher对象调用了dispatchAudioFocusChange方法,将mFocusLossReceived和mClientId传了进去。现在我们回头一步步看mFocusDispatcher是如何传进来的。 
3.1 FocusRequester构造方法的第四个参数IAudioFocusDispatcher afl 
3.2 MediaFocusControl的requestAudioFocus方法的第四个参数IAudioFocusDispatcher fd 
3.3 AudioService的requestAudioFocus方法的第四个参数IAudioFocusDispatcher fd 
最终我们在AudioManager中找到了

private final IAudioFocusDispatcher mAudioFocusDispatcher = new IAudioFocusDispatcher.Stub() {
    public void dispatchAudioFocusChange(int focusChange, String id) {
        final Message m = mServiceEventHandlerDelegate.getHandler().obtainMessage(
                MSSG_FOCUS_CHANGE/*what*/, focusChange/*arg1*/, 0/*arg2 ignored*/, id/*obj*/);
        mServiceEventHandlerDelegate.getHandler().sendMessage(m);
    }
};

private class ServiceEventHandlerDelegate {
    private final Handler mHandler;
    ServiceEventHandlerDelegate(Handler handler) {
        Looper looper;
        if (handler == null) {
            if ((looper = Looper.myLooper()) == null) {
                looper = Looper.getMainLooper();
            }
        } else {
            looper = handler.getLooper();
        }
        if (looper != null) {
            // implement the event handler delegate to receive events from audio service
            mHandler = new Handler(looper) {
                @Override
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                        case MSSG_FOCUS_CHANGE:
                            OnAudioFocusChangeListener listener = null;
                            synchronized (mFocusListenerLock) {
                                listener = findFocusListener((String) msg.obj);
                            }
                            if (listener != null) {
                                Log.d(TAG, "AudioManager dispatching onAudioFocusChange("
                                        + msg.arg1 + ") for " + msg.obj);
                                listener.onAudioFocusChange(msg.arg1);
                            }
                            break;
                         //***
                    }
                }
            };
        } else {
            mHandler = null;
        }
    }
    Handler getHandler() {
        return mHandler;
    }
}
  • 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

在dispatchAudioFocusChange方法中通过mServiceEventHandlerDelegate将事件分发到了另外的线程中,这也是让AudioService从事件分发中抽离出来

OnAudioFocusChangeListener listener = null;
synchronized (mFocusListenerLock) {
    //msg.obj就是clientId
    listener = findFocusListener((String) msg.obj);
}
if (listener != null) {
    //我们得到了listener之后回调onAudioFocusChange
    //如果当前申请的焦点时长为AudioManager.AUDIOFOCUS_GAIN,则msg.arg1=AudioManager.AUDIOFOCUS_LOSS
    //如果当前申请的焦点时长为AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,则msg.arg1=AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
    //这个转换是在FocusRequester的focusLossForGainRequest方法中进行的
    listener.onAudioFocusChange(msg.arg1);
}
//根据clientId在mAudioFocusIdListenerMap中返回对应的OnAudioFocusChangeListener
//mAudioFocusIdListenerMap是在我们最初调用requestAudioFocus时存储的,不记得的童鞋可以回头看一下
private OnAudioFocusChangeListener findFocusListener(String id) {
    return mAudioFocusIdListenerMap.get(id);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

至此我们终于将焦点改变的消息通知到了应用层注册的onAudioFocusChange方法中。 
requestAudioFocus方法分析完了,abandonAudioFocus方法完全相同的流程,这里就不做过多介绍了,有兴趣的童鞋可以自己看这源码走一遍流程就可以理解了。


好了,综合前一篇的焦点机制的应用,加上这篇的源码分析,音频焦点也就告一段落了,如果发现有分析错误的地方请及时指出。后续有时间还会再写一些关于音频的文章。

前两天拍的

猜你喜欢

转载自blog.csdn.net/mafei852213034/article/details/77740382