基於Android O平臺Audio Focus分析(主要結合Car)(上)

NO IMAGE

基於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


//[email protected]
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專用!

說回剛才函式的呼叫:

廣播焦點從獲得到失去

[email protected]


/**
    * 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

且看[email protected]


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指向的方法是:

[email protected]

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

[email protected]@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
*/

第一個發現的是


[email protected]:~/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都繼承了這個類,而且三個類都在建構函式裡呼叫了這麼一個方法

[email protected]


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

再次回到

[email protected]


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


//[email protected]@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);

轉到具體實現

[email protected]@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.所以

[email protected]


@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

首先是簡短的程式碼:

[email protected]


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操作。

[email protected]

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

加入mFocusFollowers(焦點跟隨者?)裡

然後

[email protected]

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)(下)