基于Android O平台Audio Focus分析(主要结合Car)(上)

基于Android O平台Audio Focus分析(主要结合Car)

1.调用示例

packages/apps/Car/LocalMediaPlayer/src/com/android/car/media/localmediaplayer/Player.java

private boolean requestAudioFocus(Runnable onSuccess) {
        int result = mAudioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC,AudioManager.AUDIOFOCUS_GAIN);
        if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
            onSuccess.run();
            return true;
        }
        return false;
    }

2.根据调用参数,找到对应的实现

media/java/android/media/AudioManager.java

public int requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint) {
        ...
            status = requestAudioFocus(l,
                    new AudioAttributes.Builder()
                            .setInternalLegacyStreamType(streamType).build(),
                    durationHint,
                    0 /* flags, legacy behavior */);
        ...
        return status;
    }

接口前面的英文注释没贴上来,挑选重要的

参数:durationHint

AUDIOFOCUS_GAIN_TRANSIENT

表明请求是短暂的,临时的,焦点很快会被放弃。比如播放驾驶转向,或者notifications sounds.

AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK

表明之前的焦点owner可以继续播放,但是需要ducks自己的output,压低自己的声音,供申请者做一个短暂的播放。(想象电台主播那种效果)

AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE

对于从系统中获益的临时请求,不会播放诸如通知之类的破坏性声音,用于诸如语音备忘录录音或语音识别之类的场景)。EXCLUSIVE是独家的意思,TRANSIENT是短暂的意思

简单来理解就是

我的请求是短暂的,但是我不希望这时候有嘟嘟之类的notifications声音打扰,我要独占

AUDIOFOCUS_GAIN

for a focus request of unknown duration such as the playback of a song or a video.

中间经过各种弯弯绕绕,如果按照最普通的调用方式,都可以不用关注了。

直接可以去看实现了:

services/core/java/com/android/server/audio/AudioService.java

public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,
            IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,IAudioPolicyCallback pcb, int sdk) {
        // 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, sdk);
    }

暂时没搞懂这个AUDIOFOCUS_FLAG_LOCK意味着什么。所以,继续走

services/core/java/com/android/server/audio/MediaFocusControl.java


//requestAudioFocus@MediaFocusControl
protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
            IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,int sdk) {
    ...
      if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) {
                //取出栈顶
                final FocusRequester fr = mFocusStack.peek();
                if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) {
                  //栈顶的GainRequest和GrantFlags和完全和新来的请求一致
                    cb.unlinkToDeath(afdh, 0);
                    notifyExtPolicyFocusGrant_syncAf(fr.toAudioFocusInfo(),
                            AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
                    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
                }
                //前面的if没有return
                //说明请求原因不一致,把栈顶拿掉
                if (!focusGrantDelayed) {
                    mFocusStack.pop();
                    // the entry that was "popped" is the same that was "peeked" above
                    fr.release();
                }
            }

  //到这,栈顶之前的焦点owner已经不存在了
  *******************************
      // 底下这个函数干的事情主要是这样
      // 栈顶的ClientID同新请求一致(reason不一致),pop it,
        //然后,(注意这个)发出失去焦点的通知...
        //else 遍历整个stack,有一致的ClientId(reason不一致),pop it,
        //因为不在栈顶,所以不持有焦点,所以不用通知!
      removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/);
      
      final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,
                    clientId, afdh, callingPackageName, Binder.getCallingUid(), this, sdk);
      if (focusGrantDelayed) {
                ...
            } else {
                if (!mFocusStack.empty()) {
                    //翻译下函数名字:广播_焦点失去_从获得
                    propagateFocusLossFromGain_syncAf(focusChangeHint, nfr);
                }
                // 将焦点请求nrf推到mFocusStack栈顶
                mFocusStack.push(nfr);
                //处理_焦点获得_从请求
                //焦点的状态切换
                //请求-获得-失去
                nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
            }
    ...
}

我们来研究下这个mFocusStack


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

它是一个Stack,Stack里存的是FocusRequester对象。栈顶是winner?

FocusRequester是一个er,用在人上是xx者,在这里姑且叫做(音频)焦点请求’器‘吧!

这个请求器,为MediaFocusControl专用!

说回刚才函数的调用:

广播焦点从获得到失去

propagateFocusLossFromGain_syncAf@MediaFocusControl


/**
     * 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
     */
//注释的翻译:焦点被请求了(过去时态),在整个stack中广播 相关的 loss.
//参数:focusGain:新的稍后会被添加到stack栈顶的focus gain(求往回看,确实push了)
    private void propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr) {
        // 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, fr);
        }
    }

本例入参

focusGain:追溯上去是客户端传下来的AudioManager.AUDIOFOCUS_GAIN

fr:前面新创建的nfr

那这个广播干的事情就是把mFocusStack(除栈顶以外的)所有FocusRequester取出来,挨个调用handleExternalFocusGain

且看handleExternalFocusGain@FocusRequester


void handleExternalFocusGain(int focusGain, final FocusRequester fr) {
        int focusLoss = focusLossForGainRequest(focusGain);
        handleFocusLoss(focusLoss, fr);
    }

首先,调用focusLossForGainRequest


/**
     * 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
     */
//对于给定的音频焦点gain请求,返回将导致的音频焦点丢失类型
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:
                        //winner发起的请求是AudioManager.AUDIOFOCUS_GAIN(前面说过,一般
                        //用在不确定要持有多久的,比如播放视频音频)
                        //之前就包含loss的,现在
                        //返回LOSS(你之前的暂时失去什么的已经没意义了)
                        return AudioManager.AUDIOFOCUS_LOSS;
                }
            case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
            case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
                //winner发起暂时或者暂时独占请求
                switch(mFocusLossReceived) {
                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                    case AudioManager.AUDIOFOCUS_NONE:
                        //原来是暂时的,或者暂时的而且可以被duck
                        //返回,暂时丢失(请求的人不希望duck)
                        return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
                    case AudioManager.AUDIOFOCUS_LOSS:
                        //原来是loss,保持原状
                        return AudioManager.AUDIOFOCUS_LOSS;
                }
            case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
                //winner请求暂时获得focus,而且可以duck
                switch(mFocusLossReceived) {
                    case AudioManager.AUDIOFOCUS_NONE:
                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                        //原来none,或者can_duck返回可以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;
        }
    }

focusLossForGainRequest方法相当于是根据请求,对自己focusLoss tpye做下调整,谁让请求的人是winner呢!

然后,看看handleFocusLoss是要规避winner的锋芒吗?

void handleFocusLoss(int focusLoss, @Nullable final FocusRequester fr) {
    ...
    //检查三点
    //1.framework是不是应该通知loser on a DUCK losss
    //2.他是一个 DUCK loss
    //3.the loser 没有被标记为pausing in a DUCK loss
    //三点成立,不通知loser
    //否则,调用这个,然后返回
    mFocusController.notifyExtPolicyFocusLoss_syncAf(
                            toAudioFocusInfo(), false /* wasDispatched */); 
    return;
    ...
    boolean handled = false;
    ...
    //满足一个复杂的条件我们就duckPlayers
    //复杂的条件如下
      //满足这个:
      //focusLoss == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
      //MediaFocusControl.ENFORCE_DUCKING
      //而且不是底下这些
      //mGrantFlags& AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) != 0
      //MediaFocusControl.ENFORCE_DUCKING_FOR_NEW &&
        //this.getSdkTarget() <= MediaFocusControl.DUCKING_IN_APP_SDK_LEVEL
      //简单来说就是,被检索到的需要handleFocusLoss的APP没有特殊要求,而且它处在
      //AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK这种,暂时可以被压制的状态
      //framework就要做以下的处理了
    handled = mFocusController.duckPlayers(fr, this);
    if (handled) {
        //如果handled(完成时态)
        //跟上面一样,通知,返回
        mFocusController.notifyExtPolicyFocusLoss_syncAf(
                            toAudioFocusInfo(), false /* wasDispatched */);
                    return; // with mFocusLossWasNotified = false
    }
    //前面这些都不满足,而且mFocusDispatcher不为空
    mFocusController.notifyExtPolicyFocusLoss_syncAf(
                            toAudioFocusInfo(), true /* wasDispatched */);
    mFocusLossWasNotified = true;
    //分发音频焦点的变化
    fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId);
}

我们看看mFocusController.duckPlayers干了些什么

mFocusController是一个MediaFocusControl,赋值是从构造函数来的

FocusRequester(AudioAttributes aa, int focusRequest, int grantFlags,
            IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
               String pn, int uid, @NonNull MediaFocusControl ctlr, int sdk) {
  ...
        mFocusController = ctlr;
  ...
}

注意到前面的调用


final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,
                    clientId, afdh, callingPackageName, Binder.getCallingUid(), this, sdk);

对应着this.也即mFocusController即是AudioService创建的mMediaFocusControl

//AudioService.java
mPlaybackMonitor = new PlaybackActivityMonitor(context,              MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]);
mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);

第二个参数mPlaybackMonitor是一个PlaybackActivityMonitor,而


public final class PlaybackActivityMonitor
        implements AudioPlaybackConfiguration.PlayerDeathMonitor, PlayerFocusEnforcer {
  ...

那这个调用mFocusController.duckPlayers指向的方法是:

duckPlayers@PlaybackActivityMonitor

public boolean duckPlayers(FocusRequester winner, FocusRequester loser) {
    ...
        //对mPlayers做个遍历
        //如果正在播放(AudioPlaybackConfiguration.PLAYER_STATE_STARTED),通过uid判断如果它不是winner,它是loser,往下走
        //如果apc的ContentType是CONTENT_TYPE_SPEECH,说明人家在speech return false
        //如果apc的PlayerType是UNDUCKABLE_PLAYER_TYPES,return false
        //如果都不是,放入apcsToDuck(ArrayList)
        //...they will be ducked by DuckingManager.checkDuck())
        //他们将被ducked被checkDuck
        mDuckingManager.duckUid(loser.getClientUid(), apcsToDuck);
    ...
}

duckUid@DuckingManager@PlaybackActivityMonitor


synchronized void duckUid(int uid, ArrayList<AudioPlaybackConfiguration> apcsToDuck) {
    f (!mDuckers.containsKey(uid)) {
                mDuckers.put(uid, new DuckedApp(uid));
            }
            final DuckedApp da = mDuckers.get(uid);
            for (AudioPlaybackConfiguration apc : apcsToDuck) {
                da.addDuck(apc, false /*skipRamp*/);
            }
}

void addDuck(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
    ...
        apc.getPlayerProxy().applyVolumeShaper(
                            DUCK_VSHAPE,
                            //传入的是false,所以是PLAY_CREATE_IF_NEEDED
                            skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED);
                    mDuckedPlayers.add(piid);
    ...
}

处理焦点从请求到获得

复习一下:


mFocusStack.push(nfr);
nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED);

刚刚被存入mFocusStack的nfr调用了handleFocusGainFromRequest

void handleFocusGainFromRequest(int focusRequestResult) {
        if (focusRequestResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
            //参数是this,我的理解就是,获得了焦点,之前被压制的话,要做一个反压制?
            mFocusController.unduckPlayers(this);
        }
    }

前面已经分析了mFocusController就是MediaFocusControl

services/core/java/com/android/server/audio/MediaFocusControl.java


public void unduckPlayers(FocusRequester winner) {
        mFocusEnforcer.unduckPlayers(winner);
    }

即是

services/core/java/com/android/server/audio/PlaybackActivityMonitor.java


public void unduckPlayers(FocusRequester winner) {
        if (DEBUG) { Log.v(TAG, "unduckPlayers: uids winner=" + winner.getClientUid()); }
        synchronized (mPlayerLock) {
            mDuckingManager.unduckUid(winner.getClientUid(), mPlayers);
        }
    }

unduckUid的调用


synchronized void unduckUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players) {
            final DuckedApp da = mDuckers.remove(uid);
            ...
            da.removeUnduckAll(players);
        }

传入的第一个参数winner.getClientUid()名字很有意思,获胜者?抢占AudioFocus的winner?

指向AudioService的mMediaFocusControl。getClientUid从字面意思就可以明白,就是调用本接口的APP的uid.在本例中是LocalMediaPlayer的uid。

第二个参数mPlayers(PlaybackActivityMonitor的成员):


private final HashMap<Integer, AudioPlaybackConfiguration> mPlayers =
            new HashMap<Integer, AudioPlaybackConfiguration>();

是一个存储AudioPlaybackConfiguration对象的HashMap

The AudioPlaybackConfiguration class collects the information describing an audio playback

session.

寻找mPlayers发现了以下情况

AudioTrack类(莫名其妙从这说起,有点事后诸葛亮的感觉,尴尬!)

在Android 6.0上,AudioTrack没有父类,而到了Android 8.0


public class AudioTrack extends PlayerBase
                        implements AudioRouting
                                 , VolumeAutomation
                                 ...

目前要讲的是PlayerBase这个类


/**
 * Class to encapsulate a number of common player operations:
 *   - AppOps for OP_PLAY_AUDIO
 *   - more to come (routing, transport control)
 * @hide
 */

第一个发现的是


zidongqing@ZDQ-Ubuntu:~/data/src/android-8.0/frameworks/base$ grep -nir "extends PlayerBase"
media/java/android/media/MediaPlayer.java:577:public class MediaPlayer extends PlayerBase
media/java/android/media/SoundPool.java:114:public class SoundPool extends PlayerBase {
media/java/android/media/AudioTrack.java:80:public class AudioTrack extends PlayerBase

AudioTrack,SoundPool,MediaPlayer都继承了这个类,而且三个类都在构造函数里调用了这么一个方法

baseRegisterPlayer@PlayerBase


/**
     * Call from derived class when instantiation / initialization is successful
     */
    protected void baseRegisterPlayer() {
      //用AppOps去管控AppOpsManager.OP_PLAY_AUDIO
        int newPiid = AudioPlaybackConfiguration.PLAYER_PIID_INVALID;
        IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
        mAppOps = IAppOpsService.Stub.asInterface(b);
        // initialize mHasAppOpsPlayAudio
        updateAppOpsPlayAudio();
        // register a callback to monitor whether the OP_PLAY_AUDIO is still allowed
        mAppOpsCallback = new IAppOpsCallbackWrapper(this);
        try {
            mAppOps.startWatchingMode(AppOpsManager.OP_PLAY_AUDIO,
                    ActivityThread.currentPackageName(), mAppOpsCallback);
        } catch (RemoteException e) {
            mHasAppOpsPlayAudio = false;
        }
      //AudioService android O新加的方法
        try {
            newPiid = getService().trackPlayer(
                    new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this)));
        } catch (RemoteException e) {
            Log.e(TAG, "Error talking to audio service, player will not be tracked", e);
        }
        mPlayerIId = newPiid;
    }

我们来看看getService().trackPlayer


public int trackPlayer(PlayerBase.PlayerIdCard pic) {
        return mPlaybackMonitor.trackPlayer(pic);
    }

再次回到

trackPlayer@PlaybackActivityMonitor


public int trackPlayer(PlayerBase.PlayerIdCard pic) {
        final int newPiid = AudioSystem.newAudioPlayerId();
        if (DEBUG) { Log.v(TAG, "trackPlayer() new piid=" + newPiid); }
        final AudioPlaybackConfiguration apc =
                new AudioPlaybackConfiguration(pic, newPiid,
                        Binder.getCallingUid(), Binder.getCallingPid());
        apc.init();
        sEventLogger.log(new NewPlayerEvent(apc));
        synchronized(mPlayerLock) {
            mPlayers.put(newPiid, apc);
        }
        return newPiid;
    }

前面说到的mPlayers是在这个地方进行put的

基本上目前能看到的,这个mPlayers就是存放了

AudioTrack,SoundPool,MediaPlayer

这三种对象。在本例中,装了一个MediaPlayer,请看:

LocalMediaPlayer/src/com/android/car/media/localmediaplayer/Player.java

Player构造函数


public Player(Context context, MediaSession session, DataModel dataModel) {
    ...
        mMediaPlayer = new MediaPlayer();//这里调用了baseRegisterPlayer
    ...
}

即,在这种请看下,如果没有其他MediaPlayer,SoundPool,AudioTrack的话,mPlayer中存的就是Player的

mMediaPlayer成员(准确说应该是关联的)!

再回到前面的调用过程

mDuckingManager.unduckUid


//unduckUid@DuckingManager@PlaybackActivityMonitor
synchronized void unduckUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players) {
            if (DEBUG) {  Log.v(TAG, "DuckingManager: unduckUid() uid:"+ uid); }
            final DuckedApp da = mDuckers.remove(uid);
            if (da == null) {
                return;
            }
            da.removeUnduckAll(players);
        }

DuckingManager是一个Class to handle ducking related operations for a given UID。

da.removeUnduckAll(players)

da是个啥?mDuckers又是个啥?


private final HashMap<Integer, DuckedApp> mDuckers = new HashMap<Integer, DuckedApp>();
final DuckedApp da = mDuckers.remove(uid);

转到具体实现

removeUnduckAll@DuckedApp@PlaybackActivityMonitor


void removeUnduckAll(HashMap<Integer, AudioPlaybackConfiguration> players) {
                for (int piid : mDuckedPlayers) {
                    final AudioPlaybackConfiguration apc = players.get(piid);
                    
                            ...
                            apc.getPlayerProxy().applyVolumeShaper(
                                    DUCK_ID,
                                    VolumeShaper.Operation.REVERSE);
                        ...
                }
                mDuckedPlayers.clear();
            }

前面也是说到这个applyVolumeShaper就没继续,这次我们终于可以看看这个方法了。

绕来绕去,其实是这个:


public void applyVolumeShaper(
                @NonNull VolumeShaper.Configuration configuration,
                @NonNull VolumeShaper.Operation operation) {
            final PlayerBase pb = mWeakPB.get();
            if (pb != null) {
                pb.playerApplyVolumeShaper(configuration, operation);
            }
        }

PlayerBase是个抽象类,在本例中,它就是一个MediaPlayer.所以

playerApplyVolumeShaper@MediaPlayer


@Override
    /* package */ int playerApplyVolumeShaper(
            @NonNull VolumeShaper.Configuration configuration,
            @NonNull VolumeShaper.Operation operation) {
        return native_applyVolumeShaper(configuration, operation);
    }

在进入native之前,我们复习下这两个参数

propagateFocusLossFromGain_syncAf(广播焦点从获得到失去)这一支:

​ (DUCK_VSHAPE,PLAY_CREATE_IF_NEEDED)

​ DUCK_VSHAPE定义:

private static final VolumeShaper.Configuration DUCK_VSHAPE =
            new VolumeShaper.Configuration.Builder()
                .setId(VOLUME_SHAPER_SYSTEM_DUCK_ID)
                .setCurve(new float[] { 0.f, 1.f } /* times */,
                    new float[] { 1.f, 0.2f } /* volumes */)
                .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME)
                .setDuration(MediaFocusControl.getFocusRampTimeMs(
                    AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
                    new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION)
                            .build()))
                .build();

PLAY_CREATE_IF_NEEDED定义


private static final VolumeShaper.Operation PLAY_CREATE_IF_NEEDED =
            new VolumeShaper.Operation.Builder(VolumeShaper.Operation.PLAY)
                    //mFlags |= FLAG_CREATE_IF_NEEDED;
                    .createIfNeeded()
                    .build();

handleFocusGainFromRequest(处理焦点从请求到获得)这一支:

​ (DUCK_ID,VolumeShaper.Operation.REVERSE)

DUCK_ID定义


private static final VolumeShaper.Configuration DUCK_ID =
            //VOLUME_SHAPER_SYSTEM_DUCK_ID值就是个1
            new VolumeShaper.Configuration(VOLUME_SHAPER_SYSTEM_DUCK_ID);

VolumeShaper.Operation.REVERSE定义


public static final Operation REVERSE =
                new VolumeShaper.Operation.Builder()
                    //mFlags ^= FLAG_REVERSE;
                    //Reverses the VolumeShaper curve from its current position.
                    .reverse()
                    .build();

不知所云(补充下,在VolumeShaper.java中找到了注释

Reverse playback from current volume time position.

When the position reaches the start of the {@code VolumeShaper} curve,

the first volume value persists(仍然存在).

),还是继续看看native方法native_applyVolumeShaper吧

果然:


android-8.0/frameworks/base$find -name "*.cpp"|xargs grep "native_applyVolumeShaper"
./media/jni/android_media_MediaPlayer.cpp:    {"native_applyVolumeShaper",
./core/jni/android_media_AudioTrack.cpp:    {"native_applyVolumeShaper",

AudioTrack中也有,改天再研究吧(研究之后发现AudioTrack最终也是调用了VolumeShaper::Status applyVolumeShaper)

中间那些,看着不重要的都跳过吧,直接来到

av/media/libmediaplayerservice/MediaPlayerService.cpp


VolumeShaper::Status MediaPlayerService::AudioOutput::applyVolumeShaper(
                const sp<VolumeShaper::Configuration>& configuration,
                const sp<VolumeShaper::Operation>& operation)
{
  if (mTrack != 0) {
        //看来前面看了AudioTrack是有用的
        status = mTrack->applyVolumeShaper(configuration, operation);
        if (status >= 0) {
            (void)mVolumeHandler->applyVolumeShaper(configuration, operation);
            if (mTrack->isPlaying()) { // match local AudioTrack to properly restore.
                mVolumeHandler->setStarted();
            }
        }
    } 
} else {
  //mTrack 为空的情况,暂且认为很少出现吧
}

那就,先看看mTrack->applyVolumeShaper吧,跳过audioflinger的applyVolumeShaper,最终来到

av/include/media/VolumeShaper.h


//VolumeHandler::applyVolumeShaper
VolumeShaper::Status applyVolumeShaper(
            const sp<VolumeShaper::Configuration> &configuration,
            const sp<VolumeShaper::Operation> &operation_in) {
    ...
      //有点复杂,稍后看
      //(更新)鼓起勇气看完之后发现
      //主要干的事情就是根据两个参数,对当前的播放音轨的Volume做一些变化操作,
      //没听到具体效果,不过根据描述什么方波,正弦波...的音量效果.
      //最终实现API文档中描述的淡入淡出,cross fades,ducking,and other short automated volume       //transitions.
    ...
}

谷歌了下,VolumeShaper是Android 8.0之后新加入的API!

You can use a VolumeShaper in an audio app to perform fade-ins, fade-outs, cross fades, ducking, and other short automated volume transitions. The VolumeShaper class is available in Android 8.0 (API level 26) and later.

可以实现,淡入淡出,cross fades,ducking,and other short automated volume transitions.

简单做个小结,前面分析了duck和unduck这两种操作的函数调用流程。

现在再次回到MediaFocusControl,分析下这个方法notifyExtPolicyFocusGrant_syncAf

notifyExtPolicyFocusGrant_syncAf

首先是简短的代码:

notifyExtPolicyFocusGrant_syncAf@MediaFocusControl


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);
            }
        }
    }

简单来看,是遍历mFocusFollowers。然后分别调用notifyAudioFocusGrant。

先看看mFocusFollowers是个什么吧


private ArrayList<IAudioPolicyCallback> mFocusFollowers = new ArrayList<IAudioPolicyCallback>();

熟悉AIDL的,基本上都能猜到IAudioPolicyCallback是一个自动生成的binder相关的文件。

我们找到一个函数,会对mFocusFollowers做add操作。

addFocusFollower@MediaFocusControl

void addFocusFollower(IAudioPolicyCallback ff) {
    ...
      mFocusFollowers.add(ff);
      notifyExtPolicyCurrentFocusAsync(ff);
    ...
}

加入mFocusFollowers(焦点跟随者?)里

然后

notifyExtPolicyCurrentFocusAsync@MediaFocusControl

void notifyExtPolicyCurrentFocusAsync(IAudioPolicyCallback pcb) {
  final IAudioPolicyCallback pcb2 = pcb;
  //看来是耗时操作啊
  final Thread thread = new Thread() {
    @Override
    public void run() {
      ...
        pcb2.notifyAudioFocusGrant(mFocusStack.peek().toAudioFocusInfo(),
                                // top of focus stack always has focus
                                AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
      ...
    }
  }
}

我们看看这个自动生成的IAudioPolicyCallback吧(out目录下,find -name 很好找的)

没找到notifyAudioFocusGrant的实现,那实现应该在Server端,用Stub的方式

media/java/android/media/audiopolicy/AudioPolicy.java

private final IAudioPolicyCallback mPolicyCb = new IAudioPolicyCallback.Stub() {
    ...
        public void notifyAudioFocusGrant(AudioFocusInfo afi, int requestResult) {
            sendMsg(MSG_FOCUS_GRANT, afi, requestResult);
            ...
        }
    ...
}
...
    case MSG_FOCUS_GRANT:
                    if (mFocusListener != null) {
                        mFocusListener.onAudioFocusGrant(
                                (AudioFocusInfo) msg.obj, msg.arg1);
                    }
                    break;  
...

基于Android O平台Audio Focus分析(主要结合Car)(下)


你可能感兴趣的:(安卓,Audio)