音频焦点AudioFocus

文章目录

    • 简介-AudioFocus
      • AudioFocus机制
      • 实现流程
        • requestAudioFocus方法:
          • registerAudioFocusListener()
            • OnAudioFocusChangeListener:
        • AudioManager中还有一个电话相关的调用
          • requestAudioFocus方法实际的实现在AudioService.java
        • MediaFocusControl.java中的requestAudioFocus方法过程
        • FocusRequester处理FocusRequester对象:
          • handleFocusLoss()
            • dispatchAudioFocusChange()
        • 释放焦点
          • abandonAudioFocus()
            • unregisterAudioFocusListener()
            • removeFocusStackEntry

简介-AudioFocus

AudioFocus是一个Audio协调机制,当多方需要使用Audio资源时可以通过AudioFocus机制来协调配合。

AudioFocus机制

使用AudioFocus机制主要通过android.media.AudioManager中的requestAudioFocus方法请求获取焦点,若获取成功返回int值AudioManager.AUDIOFOCUS_REQUEST_GRANTED,失败则返回AudioManager.AUDIOFOCUS_REQUEST_FAILED

实现流程

音频焦点AudioFocus_第1张图片

requestAudioFocus方法:

  1. OnAudioFocusChangeListener: 是一个接口,仅定义了一个方法onAudioFocusChange,具体实现如下文
  2. requestAttributes: 根据传进来的streamType构造AudioAttributes对象向下传递,该对象存储了一些音频流信息的属性,对flag进行&操作,(由于之前传入为0,则转换后还是0)
  3. durationHint: 获取焦点的时长,同时通知其他获取音频焦点的OnAudioFocusChangeListener该相互配合,对应值信息:
    • AUDIOFOCUS_GAIN 代表此次申请的音频焦点需要长时间持有,原本获取了音频焦点的OnAudioFocusChangeListener 接口将会回调onAudioFocusChange(int focusChange) 方法,传入的参数为AUDIOFOCUS_LOSS。
    • AUDIOFOCUS_GAIN_TRANSIENT 代表此次申请的音频焦点只需短暂持有,原本获取了音频焦点的OnAudioFocusChangeListener 接口将会回调onAudioFocusChange(int focusChange) 方法,传入的参数为AUDIOFOCUS_LOSS_TRANSIENT。按照官方注释:适用于短暂的音频,在接收到事件通知等情景时可使用该durationHint。
    • AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE 代表此次申请的音频焦点只需短暂持有,原本获取了音频焦点的OnAudioFocusChangeListener 接口将会回调onAudioFocusChange(int focusChange) 方法,传入的参数为AUDIOFOCUS_LOSS_TRANSIENT。按照官方注释:在需要录音或语音识别等情景时可使用该durationHint。
    • AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 代表此次申请不需要暂停其它申请的音频播放,应用跟其他应用共用焦点但播放的时候其他音频会降低音量。原本获取了音频焦点的OnAudioFocusChangeListener 接口将会回调onAudioFocusChange(int focusChange) 方法,传入的参数为AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK。
- frameworks/base/media/java/com/android/media/AudioManager.java
 @SystemApi
public int requestAudioFocus(OnAudioFocusChangeListener l,
        @NonNull AudioAttributes requestAttributes,
        int durationHint,
        int flags,
        AudioPolicy ap) throws IllegalArgumentException {
    // parameter checking
    if (requestAttributes == null) {
        throw new IllegalArgumentException("Illegal null AudioAttributes argument");
    }
    // durationHint 表示获取焦点的时长
    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");
    }

    int status = AUDIOFOCUS_REQUEST_FAILED;
    // 注册所有的AudioFocusRequest,具体方法见下
    registerAudioFocusListener(l);
    IAudioService service = getService();
    try {
        // 通过IAudioService通信实现audio焦点的获取
        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;
}
---------------------------------------------------------------
registerAudioFocusListener()
/**
 * @hide
 * Registers a listener to be called when audio focus changes. Calling this method is optional
 * before calling {@link #requestAudioFocus(OnAudioFocusChangeListener, int, int)}, as it
 * will register the listener as well if it wasn't registered already.
 * @param l the listener to be notified of audio focus changes.
 */
public void registerAudioFocusListener(OnAudioFocusChangeListener l) {
    synchronized(mFocusListenerLock) {
        // mAudioFocusIdListenerMap根据AudioFocusRequest中OnAudioFocusChangeListener对象生成一个key,value存储在mAudioFocusIdListenerMap对象中
        if (mAudioFocusIdListenerMap.containsKey(getIdForAudioFocusListener(l))) {
            return;
        }
        mAudioFocusIdListenerMap.put(getIdForAudioFocusListener(l), l);
    }
}

OnAudioFocusChangeListener:

是一个接口,仅定义了一个方法onAudioFocusChange(int focusChange),该方法在焦点状态变化时被调用,参数focusChange代表变化后当前状态,共有四个值:

  • AUDIOFOCUS_GAIN 重新获取到音频焦点时触发的状态。
  • AUDIOFOCUS_LOSS 失去音频焦点时触发的状态,且该状态应该会长期保持,此时应当暂停音频并释放音频相关的资源。
  • AUDIOFOCUS_LOSS_TRANSIENT 失去音频焦点时触发的状态,但是该状态不会长时间保持,此时应该暂停音频,且当重新获取音频焦点的时候继续播放。
  • AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK 失去音频焦点时触发的状态,在该状态的时候不需要暂停音频,但是应该降低音频的声音
/**
 * Interface definition for a callback to be invoked when the audio focus of the system is
 * updated.
 */
public interface OnAudioFocusChangeListener {
    /**
     * Called on the listener to notify it the audio focus for this listener has been changed.
     * The focusChange value indicates whether the focus was gained,
     * whether the focus was lost, and whether that loss is transient, or whether the new focus
     * holder will hold it for an unknown amount of time.
     * When losing focus, listeners can use the focus change information to decide what
     * behavior to adopt when losing focus. A music player could for instance elect to lower
     * the volume of its music stream (duck) for transient focus losses, and pause otherwise.
     * @param focusChange the type of focus change, one of {@link AudioManager#AUDIOFOCUS_GAIN},
     *   {@link AudioManager#AUDIOFOCUS_LOSS}, {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT}
     *   and {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}.
     */
    public void onAudioFocusChange(int focusChange);
}

AudioManager中还有一个电话相关的调用

AudioService中的requestAudioFocus并且往其中设置了一个clientId为AudioSystem.IN_VOICE_COMM_FOCUS_ID的状态。

进入到AudioService.requestAudioFocus中首先会进行权限检查,这里面就用到了AudioSystem.IN_VOICE_COMM_FOCUS_ID 也就是说如果clientId等于AudioSystem.IN_VOICE_COMM_FOCUS_ID,且要申请到MODIFY_PHONE_STATE的权限,否则会申请焦点失败。

/**
 * @hide
 * Used internally by telephony package to request audio focus. Will cause the focus request
 * to be associated with the "voice communication" identifier only used in AudioService
 * to identify this use case.
 * @param streamType use STREAM_RING for focus requests when ringing, VOICE_CALL for
 *    the establishment of the call
 * @param durationHint the type of focus request. AUDIOFOCUS_GAIN_TRANSIENT is recommended so
 *    media applications resume after a call
 */
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();
    }
}
requestAudioFocus方法实际的实现在AudioService.java
- frameworks/base/service/core/java/com/android/sever/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
    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;
                }
            }
        }
    }

    return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
            clientId, callingPackageName, flags);
}

MediaFocusControl.java中的requestAudioFocus方法过程

AudioService.java相当于中转站将参数传入MediaFocusControl类中的requestAudioFocus方法,返回结果

  1. 判断mFocusStack值的size不能超过100
  2. 检查当前栈顶的元素是否是Phone应用占用状态,若Phone处于占用状态,那么focusGrantDelayed = true
  3. 压栈之前,需要检查当前栈中是否已经有这个应用的记录,如含有则删除。
    若mFocusStrack不为null(!mFocusStack.empty()),且栈顶的clientId与需要申请焦点的clientId相同(mFocusStack.peek().hasSameClient(clientId)),得到栈顶元素即FocusRequester对象。若申请的时长(focusChangeHint)及flag相同,则表示申请重复,直接返回AUDIOFOCUS_REQUEST_GRANTED;若二者有一个不同则表示需要重新申请,同时此时focusGrantDelayed为false需要将栈顶元素出栈并将其释放
  4. removeFocusStackEntry-- 移除可能在栈中(栈顶或栈中)其他位置存在着相同的clientId的元素
    • 栈顶:释放后通知栈顶应用,使其获得audioFocus
    • 非栈顶:只是移除该记录,无需更改当前audioFocus的占有情况
  5. 创建FocusRequester实例将其请求包含的各种信息传入
  6. 如果focusGrantDelayed = true会延迟申请,进入pushBelowLockedFocusOwners方法,在方法中把此次请求FocusRequester实例入栈,而非被压在栈顶,放在lastLockedFocusOwnerIndex位置,即电话记录之后,则电话的焦点释放后则该请求开始申请; 如果focusGrantDelayed = false则无需延迟获取焦点,同样创建FocusRequester实例,但需先通知栈中其他记录失去焦点,然后入栈,最后通知自己获得焦点
  7. 继续会进入propagateFocusLossFromGain_syncAf方法,遍历mFocusStack,调用FocusRequester对象的handleExternalFocusGain方法,通知栈中其他元素丢失焦点流程。通过while(stackIterator.hasNext())得到FocusRequester对象,进入handleExternalFocusGain方法
  8. notifyExtPolicyFocusGrant_syncAf做相应的通知
// 将FocusRequester存入栈中,后续的获取及释放焦点则是对该栈的出栈入栈处理,用以维护各个client的申请和释放
private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>();
-frameworks/base/services/core/java/com/android/server/audio/MediaFocusControl.java
/** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int, int) */
protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
        IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags) {
    Log.i(TAG, " AudioFocus  requestAudioFocus() from uid/pid " + Binder.getCallingUid()
            + "/" + Binder.getCallingPid()
            + " clientId=" + 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;
        }
        // 注解3
        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);
                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
        // 注解4
        removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/);
        // 注解5
        final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,
                clientId, afdh, callingPackageName, Binder.getCallingUid(), this);
        // 注解6
        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;
}
----------------------------------------------------------------------------
/**
 * Helper function
 * Pre-conditions: focus stack is not empty, there is one or more locked focus owner
 *                 at the top of the focus stack
 * Push the focus requester onto the audio focus stack at the first position immediately
 * following the locked focus owners.
 * @return {@link AudioManager#AUDIOFOCUS_REQUEST_GRANTED} or
 *     {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED}
 */
private int pushBelowLockedFocusOwners(FocusRequester nfr) {
    int lastLockedFocusOwnerIndex = mFocusStack.size();
    for (int index = mFocusStack.size()-1; index >= 0; index--) {
        if (isLockedFocusOwner(mFocusStack.elementAt(index))) {
            lastLockedFocusOwnerIndex = index;
        }
    }
    if (lastLockedFocusOwnerIndex == mFocusStack.size()) {
        // this should not happen, but handle it and log an error
        Log.e(TAG, "No exclusive focus owner found in propagateFocusLossFromGain_syncAf()",
                new Exception());
        // no exclusive owner, push at top of stack, focus is granted, propagate change
        propagateFocusLossFromGain_syncAf(nfr.getGainRequest());
        mFocusStack.push(nfr);
        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
    } else {
        mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex);
        return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
    }
}
----------------------------------------------------------------------------

/**
 * Focus is requested, propagate the associated loss throughout the stack.
 * @param focusGain the new focus gain that will later be added at the top of the stack
 */
private void propagateFocusLossFromGain_syncAf(int focusGain) {
    // going through the audio focus stack to signal new focus, traversing order doesn't
    // matter as all entries respond to the same external focus gain
    Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
    while(stackIterator.hasNext()) {
        stackIterator.next().handleExternalFocusGain(focusGain);
    }
}
----------------------------------------------------------------------------------
// 通知操作
/**
 * Called synchronized on mAudioFocusLock
 */
void notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult) {
    for (IAudioPolicyCallback pcb : mFocusFollowers) {
        try {
            // oneway
            pcb.notifyAudioFocusGrant(afi, requestResult);
        } catch (RemoteException e) {
            Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback "
                    + pcb.asBinder(), e);
        }
    }
}

/**
 * Called synchronized on mAudioFocusLock
 */
void notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched) {
    for (IAudioPolicyCallback pcb : mFocusFollowers) {
        try {
            // oneway
            pcb.notifyAudioFocusLoss(afi, wasDispatched);
        } catch (RemoteException e) {
            Log.e(TAG, "Can't call notifyAudioFocusLoss() on IAudioPolicyCallback "
                    + pcb.asBinder(), e);
        }
    }
}

FocusRequester处理FocusRequester对象:

根据MediaFocusControl.java中propagateFocusLossFromGain_syncAf传递来的nfr.getGainRequest()值做焦点的获取/丢失处理

变量:

  1. gainRequest: 根据传入值做对应的返回值
  • 若传进来的参数是 AudioManager.AUDIOFOCUS_GAIN,return AudioManager.AUDIOFOCUS_LOSS
  • 若传进来的参数是 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
  1. mFocusLossReceived: 方法handleFocusLoss中赋值,默认为AudioManager.AUDIOFOCUS_NONE
-frameworks/base/services/core/java/com/android/server/audio/FocusRequester.java
/**
 * Called synchronized on MediaFocusControl.mAudioFocusLock
 */
void handleExternalFocusGain(int focusGain) {
    int focusLoss = focusLossForGainRequest(focusGain);
    handleFocusLoss(focusLoss);
}

/**
 * For a given audio focus gain request, return the audio focus loss type that will result
 * from it, taking into account any previous focus loss.
 * @param gainRequest
 * @return the audio focus loss type that matches the gain request
 */
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;
    }
}
handleFocusLoss()

fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId);通过IAudioFocusDispatcher调用dispatchAudioFocusChange方法传入mFocusLossReceived, mClientId;此处IAudioFocusDispatcher最终是在AudioManager中定义实现。IAudioFocusDispatcher回调dispatchAudioFocusChange方法

在IAudioFocusDispatcher回调dispatchAudioFocusChange方法中发送MSSG_FOCUS_CHANGE消息;在ServiceEventHandlerDelegate中创建handleMessage在其中根据MSSG_FOCUS_CHANGE回调,其中msg.arg1为focusChange即为之前传入的mFocusLossReceived,通过listener.onAudioFocusChange(msg.arg1);实现音频焦点的变化监听

 /**
 * Called synchronized on MediaFocusControl.mAudioFocusLock
 */
void handleFocusLoss(int focusLoss) {
    try {
        if (focusLoss != mFocusLossReceived) {
            mFocusLossReceived = focusLoss;
            // before dispatching a focus loss, check if the following conditions are met:
            // 1/ the framework is not supposed to notify the focus loser on a DUCK loss
            // 2/ it is a DUCK loss
            // 3/ the focus loser isn't flagged as pausing in a DUCK loss
            // if they are, do not notify the focus loser
            if (!mFocusController.mustNotifyFocusOwnerOnDuck()
                    && mFocusLossReceived == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
                    && (mGrantFlags
                            & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) == 0) {
                if (DEBUG) {
                    Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
                            + " to " + mClientId + ", to be handled externally");
                }
                mFocusController.notifyExtPolicyFocusLoss_syncAf(
                        toAudioFocusInfo(), false /* wasDispatched */);
                return;
            }
            final IAudioFocusDispatcher fd = mFocusDispatcher;
            if (fd != null) {
                if (DEBUG) {
                    Log.v(TAG, "dispatching " + focusChangeToString(mFocusLossReceived) + " to "
                        + mClientId);
                }
                mFocusController.notifyExtPolicyFocusLoss_syncAf(
                        toAudioFocusInfo(), true /* wasDispatched */);
                fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId);
            }
        }
    } catch (android.os.RemoteException e) {
        Log.e(TAG, "Failure to signal loss of audio focus due to:", e);
    }
}

-----------------------------------------------------------------------
dispatchAudioFocusChange()
- frameworks/base/media/java/com/android/media/AudioManager.java
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);
    }

};

/**
 * Helper class to handle the forwarding of audio service events to the appropriate listener
 */
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;
                        case MSSG_RECORDING_CONFIG_CHANGE:
                            ...
                            break;
                        default:
                            Log.e(TAG, "Unknown event " + msg.what);
                    }
                }
            };
        } else {
            mHandler = null;
        }
    }

    Handler getHandler() {
        return mHandler;
    }
}

释放焦点

通过abandonAudioFocus()方法释放焦点。由于音频焦点是唯一的,故可以在需要播放音乐时申请音频焦点,如获取到了则播放,同时正在播放的音频在失去焦点时停止播放或做降音处理,以此协调

abandonAudioFocus()

unregisterAudioFocusListener – remove掉mAudioFocusIdListenerMap中的OnAudioFocusChangeListener

- frameworks/base/media/java/com/android/media/AudioManager.java
/**
 * @hide
 * Abandon audio focus. Causes the previous focus owner, if any, to receive focus.
 * @param l the listener with which focus was requested.
 * @param aa the {@link AudioAttributes} with which audio focus was requested
 * @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
 */
@SystemApi
public int abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa) {
    int status = AUDIOFOCUS_REQUEST_FAILED;
    unregisterAudioFocusListener(l);
    IAudioService service = getService();
    try {
        status = service.abandonAudioFocus(mAudioFocusDispatcher,
                getIdForAudioFocusListener(l), aa);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
    return status;
}
------------------------------------------------------------------------------
unregisterAudioFocusListener()
/**
 * @hide
 * Causes the specified listener to not be called anymore when focus is gained or lost.
 * @param l the listener to unregister.
 */
public void unregisterAudioFocusListener(OnAudioFocusChangeListener l) {

    // remove locally
    synchronized(mFocusListenerLock) {
        mAudioFocusIdListenerMap.remove(getIdForAudioFocusListener(l));
    }
}

进入实际实现方法的AudioService.java的abandonAudioFocus方法

- frameworks/base/service/core/java/com/android/sever/audio/AudioService.java
public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa) {
        return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa);
    }

将参数传入MediaFocusControl类中的abandonAudioFocus方法

removeFocusStackEntry

同以上一直移除栈中的当前需要移除的Audio焦点,mFocusStack.pop(),遍历FocusRequester实例对象,release相应的焦点

notifyExtPolicyFocusLoss_syncAf:发送失去焦点的通知操作

-frameworks/base/services/core/java/com/android/server/audio/MediaFocusControl.java
/**
 * @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener, AudioAttributes)
 * */
protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa) {
    // AudioAttributes are currently ignored, to be used for zones
    Log.i(TAG, " AudioFocus  abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
            + "/" + Binder.getCallingPid()
            + " clientId=" + clientId);
    try {
        // this will take care of notifying the new focus owner if needed
        synchronized(mAudioFocusLock) {
            removeFocusStackEntry(clientId, true /*signal*/, true /*notifyFocusFollowers*/);
        }
    } catch (java.util.ConcurrentModificationException cme) {
        // Catching this exception here is temporary. It is here just to prevent
        // a crash seen when the "Silent" notification is played. This is believed to be fixed
        // but this try catch block is left just to be safe.
        Log.e(TAG, "FATAL EXCEPTION AudioFocus  abandonAudioFocus() caused " + cme);
        cme.printStackTrace();
    }

    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}

protected void unregisterAudioFocusClient(String clientId) {
    synchronized(mAudioFocusLock) {
        removeFocusStackEntry(clientId, false, true /*notifyFocusFollowers*/);
    }
}

/**
 * Helper function:
 * Called synchronized on mAudioFocusLock
 * Remove a focus listener from the focus stack.
 * @param clientToRemove the focus listener
 * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
 *   focus, notify the next item in the stack it gained focus.
 */
private void removeFocusStackEntry(String clientToRemove, boolean signal,
        boolean notifyFocusFollowers) {
    // is the current top of the focus stack abandoning focus? (because of request, not death)
    if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientToRemove))
    {
        //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
        FocusRequester fr = mFocusStack.pop();
        fr.release();
        if (notifyFocusFollowers) {
            final AudioFocusInfo afi = fr.toAudioFocusInfo();
            afi.clearLossReceived();
            notifyExtPolicyFocusLoss_syncAf(afi, false);
        }
        if (signal) {
            // notify the new top of the stack it gained focus
            notifyTopOfAudioFocusStack();
        }
    } else {
        // focus is abandoned by a client that's not at the top of the stack,
        // no need to update focus.
        // (using an iterator on the stack so we can safely remove an entry after having
        //  evaluated it, traversal order doesn't matter here)
        Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
        while(stackIterator.hasNext()) {
            FocusRequester fr = stackIterator.next();
            if(fr.hasSameClient(clientToRemove)) {
                Log.i(TAG, "AudioFocus  removeFocusStackEntry(): removing entry for "
                        + clientToRemove);
                stackIterator.remove();
                // stack entry not used anymore, clear references
                fr.release();
            }
        }
    }
}

你可能感兴趣的:(Android之旅,音频焦点AudioFocus,FocusRequester)