当前位置:   article > 正文

android10 AudioService之音频输出通道切换_android 音频输出

android 音频输出

前言:android系统中,音频输出的方式有很多种,外放即扬声器(Speaker)、听筒(Telephone Receiver)、有线耳机(WiredHeadset)、蓝牙音箱(Bluetooth A2DP)等,

android 系统默认有自己的音频输出优先级,那我们可以按照需求定制自己的音频切换方式么?答案是可以的。即可以在应用层修改,也可以在Framework修改,今天讲的就是Framework层的修改,也就是修改AudioService。接下来我们先了解下基本的使用

一音频输出通道

  1. //base/media/java/android/media/AudioSystem.java下
  2. @UnsupportedAppUsage
  3. public static final int FORCE_NONE = 0;//默认通道
  4. public static final int FORCE_SPEAKER = 1;//扬声器通道
  5. public static final int FORCE_HEADPHONES = 2;//耳机通道
  6. //下面两个是蓝牙耳机通道
  7. public static final int FORCE_BT_SCO = 3;//是一种双向的音频数据的传输链路,只能用于普通语音的传输,不能用于播放音乐
  8. public static final int FORCE_BT_A2DP = 4;//是一种单向的高品质音频数据传输链路,通常用于播放立体声音乐
  9. public static final int FORCE_WIRED_ACCESSORY = 5;//有线设备通道,如有线耳机
  10. @UnsupportedAppUsage
  11. public static final int FORCE_BT_CAR_DOCK = 6;
  12. @UnsupportedAppUsage
  13. public static final int FORCE_BT_DESK_DOCK = 7;
  14. @UnsupportedAppUsage
  15. public static final int FORCE_ANALOG_DOCK = 8;
  16. @UnsupportedAppUsage
  17. public static final int FORCE_DIGITAL_DOCK = 9;
  18. public static final int FORCE_NO_BT_A2DP = 10;
  19. public static final int FORCE_SYSTEM_ENFORCED = 11;
  20. public static final int FORCE_HDMI_SYSTEM_AUDIO_ENFORCED = 12;
  21. public static final int FORCE_ENCODED_SURROUND_NEVER = 13;
  22. public static final int FORCE_ENCODED_SURROUND_ALWAYS = 14;
  23. public static final int FORCE_ENCODED_SURROUND_MANUAL = 15;
  24. public static final int NUM_FORCE_CONFIG = 16;
  25. public static final int FORCE_DEFAULT = FORCE_NONE;

常用的也就是:扬声器,有线耳机,听筒,蓝牙耳机等; 

二 音频模式的理解和使用

  • 音频模式设置的使用:
    1. public static void setSpeakerNormal(Context context, int mode) {
    2. Log.e(TAG, "setSpeakerNormal: " + on);
    3. AudioManager am=(AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
    4. am.setMode(mode);
    5. }
  •  音频模式的选项:
    在使用音频输出通道时,需要指定播放模式,设置音频模式的方法audioManager.setMode()方法的值有如下几种:
     
    1. //base/media/java/android/media/AudioManager.java下
    2. /* modes for setMode/getMode/setRoute/getRoute */
    3. /**
    4. * Audio harware modes.
    5. */
    6. /**
    7. * Invalid audio mode.
    8. */
    9. public static final int MODE_INVALID = AudioSystem.MODE_INVALID;
    10. /**
    11. * Current audio mode. Used to apply audio routing to current mode.
    12. */
    13. public static final int MODE_CURRENT = AudioSystem.MODE_CURRENT;
    14. /**
    15. * Normal audio mode: not ringing and no call established.
    16. */
    17. public static final int MODE_NORMAL = AudioSystem.MODE_NORMAL;
    18. /**
    19. * Ringing audio mode. An incoming is being signaled.
    20. */
    21. public static final int MODE_RINGTONE = AudioSystem.MODE_RINGTONE;
    22. /**
    23. * In call audio mode. A telephony call is established.
    24. */
    25. public static final int MODE_IN_CALL = AudioSystem.MODE_IN_CALL;
    26. /**
    27. * In communication audio mode. An audio/video chat or VoIP call is established.
    28. */
    29. public static final int MODE_IN_COMMUNICATION = AudioSystem.MODE_IN_COMMUNICATION;
    可用的模式有:MODE_NORMAL,//默认(平时)状态
                             MODE_RINGTONE,//响玲模式
                             MODE_IN_CALL,//通话模式
                             MODE_IN_COMMUNICATION//(非通话)切换至听筒模式

    其引用的AudioSystem,该类定义如下
    1. AudioSystem.java
    2. /* modes for setPhoneState, must match AudioSystem.h audio_mode */
    3. public static final int MODE_INVALID = -2;
    4. public static final int MODE_CURRENT = -1;
    5. public static final int MODE_NORMAL = 0;//待机模式,既不是铃声模式也不是通话模式,如music
    6. public static final int MODE_RINGTONE = 1;//铃声模式
    7. public static final int MODE_IN_CALL = 2;//音频通话模式
    8. public static final int MODE_IN_COMMUNICATION = 3;//通信模式,包括音/视频,VoIP通话.(3.0加入的,与通话模式类似)
    9. public static final int NUM_MODES = 4;
  • 设备默认模式:MODE_NORMAL:

    1. public static void setSpeakerNormal(Context context) {
    2. AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    3. am.setMode(AudioManager.MODE_NORMAL);
    4. }
  •   设置听筒模式(非通话):MODE_IN_COMMUNICATION
  1. public static void setTingtong(Context context) {
  2. Log.e(TAG, "setTingtong start ");
  3. AudioManager am = (AudioManager)context.
  4. getSystemService(Context.AUDIO_SERVICE);
  5. am.setSpeakerphoneOn(false);
  6. am.setMode(AudioManager.MODE_IN_COMMUNICATION);
  7. }
  •  设置通话模式
    1. public static void setInCall(Context context) {
    2. Log.e(TAG, "setTingtong start ");
    3. AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    4. am.setSpeakerphoneOn(false);
    5. am.setMode(AudioManager.MODE_IN_CALL);
    6. }

三 流类型

设置播放模式的时候,需要考虑流类型,常用的流类型有:

  1. //base/media/java/android/media/AudioSystem.java下
  2. /* These values must be kept in sync with system/audio.h */
  3. /*
  4. * If these are modified, please also update Settings.System.VOLUME_SETTINGS
  5. * and attrs.xml and AudioManager.java.
  6. */
  7. /** Used to identify the default audio stream volume */
  8. public static final int STREAM_DEFAULT = -1;
  9. /** Used to identify the volume of audio streams for phone calls */
  10. public static final int STREAM_VOICE_CALL = 0;//用于电话通话的音频流。
  11. /** Used to identify the volume of audio streams for system sounds */
  12. public static final int STREAM_SYSTEM = 1;//用于系统声音的音频流
  13. /** Used to identify the volume of audio streams for the phone ring and message alerts */
  14. public static final int STREAM_RING = 2;//用于电话铃声的音频流
  15. /** Used to identify the volume of audio streams for music playback */
  16. public static final int STREAM_MUSIC = 3;//用于音乐播放的音频流
  17. /** Used to identify the volume of audio streams for alarms */
  18. public static final int STREAM_ALARM = 4;//用于警报的音频流
  19. /** Used to identify the volume of audio streams for notifications */
  20. public static final int STREAM_NOTIFICATION = 5;//用于通知的音频流
  21. /** Used to identify the volume of audio streams for phone calls when connected on bluetooth */
  22. public static final int STREAM_BLUETOOTH_SCO = 6;//用于连接到蓝牙电话的手机音频流
  23. /** Used to identify the volume of audio streams for enforced system sounds in certain
  24. * countries (e.g camera in Japan) */
  25. @UnsupportedAppUsage
  26. public static final int STREAM_SYSTEM_ENFORCED = 7;//在某些国家实施的系统声音的音频流
  27. /** Used to identify the volume of audio streams for DTMF tones */
  28. public static final int STREAM_DTMF = 8;//DTMF音调的音频流。
  29. /** Used to identify the volume of audio streams exclusively transmitted through the
  30. * speaker (TTS) of the device */
  31. public static final int STREAM_TTS = 9;//文本到语音转换(TTS)的音频流。
  32. /** Used to identify the volume of audio streams for accessibility prompts */
  33. public static final int STREAM_ACCESSIBILITY = 10;//辅助功能提示音频流

四 音频输出通道,播放模式和流类型的关系

音频通道是与播放模式一起用的,而播放模式与音频流类型有关系;
(1)音频通道是指声音从哪里出来,这个容易理解;
(2)播放模式,也叫音频状态,手机有4种音频状态:待机状态音视频通话状态视频/VoIP通话状态响铃状态。这4种状态对底层的音频输出设备的选择影响很大,相应的情景下就得使用相应的模式,如视频情景的播放模式就是MODE_IN_COMMUNICATION,或者,播放音乐情景的播放模式就是MODE_NORMAL,什么样的情形就得用什么样的播放模式,不能搞混,比如MODE_IN_CALL,就只能由通话时才能使用;
(3)音频流类型,我们操作手机的音频时需要指定操作的是哪一个流,虽然手机的中音频流类型有很多,但是一旦进入到属性里,android就会将其整理成几种类型,这才是实际的类型,与上面的播放模式对应;

  五 audioManager.setMode(int mode)的源码实现

  1.    在AudioManager类中的实现
    1. /**
    2. * Sets the audio mode.
    3. * <p>
    4. * The audio mode encompasses audio routing AND the behavior of
    5. * the telephony layer. Therefore this method should only be used by applications that
    6. * replace the platform-wide management of audio settings or the main telephony application.
    7. * In particular, the {@link #MODE_IN_CALL} mode should only be used by the telephony
    8. * application when it places a phone call, as it will cause signals from the radio layer
    9. * to feed the platform mixer.
    10. *
    11. * @param mode the requested audio mode ({@link #MODE_NORMAL}, {@link #MODE_RINGTONE},
    12. * {@link #MODE_IN_CALL} or {@link #MODE_IN_COMMUNICATION}).
    13. * Informs the HAL about the current audio state so that
    14. * it can route the audio appropriately.
    15. */
    16. public void setMode(int mode) {
    17. final IAudioService service = getService();
    18. try {
    19. service.setMode(mode, mICallBack, mApplicationContext.getOpPackageName());
    20. } catch (RemoteException e) {
    21. throw e.rethrowFromSystemServer();
    22. }
    23. }
  2.  接下来是AudioService中方法的实现
    1. /** @see AudioManager#setMode(int) */
    2. public void setMode(int mode, IBinder cb, String callingPackage) {
    3. if (DEBUG_MODE) { Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")"); }
    4. //检测权限不合法,return
    5. if (!checkAudioSettingsPermission("setMode()")) {
    6. return;
    7. }
    8. //通话模式下,无权限return
    9. if ( (mode == AudioSystem.MODE_IN_CALL) &&
    10. (mContext.checkCallingOrSelfPermission(
    11. android.Manifest.permission.MODIFY_PHONE_STATE)
    12. != PackageManager.PERMISSION_GRANTED)) {
    13. Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid="
    14. + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
    15. return;
    16. }
    17. //新的mode值不合法return
    18. if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
    19. return;
    20. }
    21. int oldModeOwnerPid = 0;
    22. int newModeOwnerPid = 0;
    23. synchronized (mDeviceBroker.mSetModeLock) {
    24. if (!mSetModeDeathHandlers.isEmpty()) {
    25. oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
    26. }
    27. if (mode == AudioSystem.MODE_CURRENT) {
    28. mode = mMode;
    29. }
    30. //设置新的模式值,如果此次设置的音频播放模式和上一次的不同,返回这次使用新音频播放模式的进程的pid
    31. newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid(), callingPackage);
    32. }
    33. // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
    34. // SCO connections not started by the application changing the mode when
    35. pid changes
    36. ///如果进入了RINGTONE, IN_CALL 或者IN_COMMUNICATION模式,清除掉当前更改音频模式的应用进程的蓝牙SCO连接
    37. if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) {
    38. mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
    39. }
    40. }

//设置新的模式值
newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid(), callingPackage);

       3.  setModeInt的实现

  1. // setModeInt() returns a valid PID if the audio mode was successfully set to
  2. // any mode other than NORMAL.
  3. @GuardedBy("mDeviceBroker.mSetModeLock")
  4. private int setModeInt(int mode, IBinder cb, int pid, String caller) {
  5. if (DEBUG_MODE) { Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid + ", caller="
  6. + caller + ")"); }
  7. int newModeOwnerPid = 0;
  8. if (cb == null) {
  9. Log.e(TAG, "setModeInt() called with null binder");
  10. return newModeOwnerPid;
  11. }
  12. if(caller != null && caller.equals("com.android.soundrecorder") && mode == AudioSystem.MODE_IN_CALL) {
  13. mIsSoundRecorderPlayInEARPIECE = true;
  14. }else{
  15. mIsSoundRecorderPlayInEARPIECE = false;
  16. }
  17. SetModeDeathHandler hdlr = null;
  18. Iterator iter = mSetModeDeathHandlers.iterator();
  19. //循环遍历mSetModeDeathHandlers,找到与传递进来的相同pid的SetModeDeathHandler,并赋值给hdlr,
  20. //相同的pid也就是相同的应用,即找到相同的应用
  21. while (iter.hasNext()) {
  22. SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
  23. if (h.getPid() == pid) {
  24. hdlr = h;
  25. // Remove from client list so that it is re-inserted at top of list
  26. iter.remove();
  27. hdlr.getBinder().unlinkToDeath(hdlr, 0);
  28. break;
  29. }
  30. }
  31. final int oldMode = mMode;
  32. int status = AudioSystem.AUDIO_STATUS_OK;
  33. int actualMode;
  34. do {
  35. //将传递进来的mode赋值初始化实际的播放模式
  36. actualMode = mode;
  37. //如果设置的模式是正常的播放模式,那就从mSetModeDeathHandlers列表的顶端获取一个模式给actualMode,
  38. //最近一次设置非正常音频模式的应用都会被放在mSetModeDeathHandlers的顶端
  39. if (mode == AudioSystem.MODE_NORMAL) {
  40. // get new mode from client at top the list if any
  41. if (!mSetModeDeathHandlers.isEmpty()) {
  42. hdlr = mSetModeDeathHandlers.get(0);
  43. cb = hdlr.getBinder();
  44. actualMode = hdlr.getMode();
  45. if (DEBUG_MODE) {
  46. Log.w(TAG, " using mode=" + mode + " instead due to death hdlr at pid="
  47. + hdlr.mPid);
  48. }
  49. }
  50. } else {
  51. if (hdlr == null) {
  52. hdlr = new SetModeDeathHandler(cb, pid);
  53. }
  54. // Register for client death notification
  55. try {
  56. cb.linkToDeath(hdlr, 0);
  57. } catch (RemoteException e) {
  58. // Client has died!
  59. Log.w(TAG, "setMode() could not link to "+cb+" binder death");
  60. }
  61. // Last client to call setMode() is always at top of client list
  62. // as required by SetModeDeathHandler.binderDied()
  63. //将hdlr加到mSetModeDeathHandlers中,并放到首位,也就是最后一个调用setMode()的进程位于列表的顶部
  64. mSetModeDeathHandlers.add(0, hdlr);
  65. //设置当前进程的音频播放模式,hdlr.setMode()会将mode设置给mMode,这个要注意,要不然很容易跟下面的"actualMode != mMode"混淆
  66. hdlr.setMode(mode);
  67. }
  68. if (actualMode != mMode) {
  69. final long identity = Binder.clearCallingIdentity();
  70. //通过AudioSystem将当前的音频模式设置到底层去,status返回设置的结果
  71. status = AudioSystem.setPhoneState(actualMode);
  72. Binder.restoreCallingIdentity(identity);
  73. if (status == AudioSystem.AUDIO_STATUS_OK) {
  74. if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + actualMode); }
  75. //如果设置成功,保存当前的音频播放模式
  76. mMode = actualMode;
  77. } else {
  78. if (hdlr != null) {
  79. //如果设置不成功,从mSetModeDeathHandlers中删除该应用
  80. mSetModeDeathHandlers.remove(hdlr);
  81. cb.unlinkToDeath(hdlr, 0);
  82. }
  83. // force reading new top of mSetModeDeathHandlers stack
  84. if (DEBUG_MODE) { Log.w(TAG, " mode set to MODE_NORMAL after phoneState pb"); }
  85. mode = AudioSystem.MODE_NORMAL;
  86. }
  87. } else {
  88. status = AudioSystem.AUDIO_STATUS_OK;
  89. }
  90. } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty());
  91. if (status == AudioSystem.AUDIO_STATUS_OK) {
  92. if (actualMode != AudioSystem.MODE_NORMAL) {
  93. if (mSetModeDeathHandlers.isEmpty()) {
  94. Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
  95. } else {
  96. //如果这个进程设置的音频模式为非正常模式,那就返回这个进程的pid
  97. newModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
  98. }
  99. }
  100. // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL
  101. mModeLogger.log(
  102. new PhoneStateEvent(caller, pid, mode, newModeOwnerPid, actualMode));
  103. //下面的代码用来设置当前音频流类型的音量
  104. int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
  105. int device = getDeviceForStream(streamType);
  106. int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device);
  107. setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, caller);
  108. updateStreamVolumeAlias(true /*updateVolumes*/, caller);
  109. // change of mode may require volume to be re-applied on some devices
  110. updateAbsVolumeMultiModeDevices(oldMode, actualMode);
  111. }
  112. return newModeOwnerPid;
  113. }

AudioService用mMode来保存当前的音频播放模式。

六 设置音频输出管道

 设置音频输出管道的方法有两个分别是setSpeakerphoneOn()和setBluetoothScoOn(),我们来就看下最常用的设置扬声器播放;

  1. /**
  2. * Sets the speakerphone on or off.
  3. * <p>
  4. * This method should only be used by applications that replace the platform-wide
  5. * management of audio settings or the main telephony application.
  6. *
  7. * @param on set <var>true</var> to turn on speakerphone;
  8. * <var>false</var> to turn it off
  9. *true 是开启扬声器,false关闭扬声器
  10. */
  11. public void setSpeakerphoneOn(boolean on){
  12. final IAudioService service = getService();
  13. try {
  14. service.setSpeakerphoneOn(on);
  15. } catch (RemoteException e) {
  16. throw e.rethrowFromSystemServer();
  17. }
  18. }

以上方法是调用AudioService中的setSpeakerphoneOn(boolean on)

看下AudioSevice中该方法的实现

  1. /** @see AudioManager#setSpeakerphoneOn(boolean) */
  2. public void setSpeakerphoneOn(boolean on){
  3. //检查android.Manifest.permission.MODIFY_AUDIO_SETTINGS是否允许
  4. if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
  5. return;
  6. }
  7. //通话模式,检查权限是否允许
  8. if (mContext.checkCallingOrSelfPermission(
  9. android.Manifest.permission.MODIFY_PHONE_STATE)
  10. != PackageManager.PERMISSION_GRANTED) {
  11. synchronized (mSetModeDeathHandlers) {
  12. for (SetModeDeathHandler h : mSetModeDeathHandlers) {
  13. if (h.getMode() == AudioSystem.MODE_IN_CALL) {
  14. Log.w(TAG, "getMode is call, Permission Denial: setSpeakerphoneOn from pid="
  15. + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
  16. return;
  17. }
  18. }
  19. }
  20. }
  21. // for logging only
  22. final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on)
  23. .append(") from u/pid:").append(Binder.getCallingUid()).append("/")
  24. .append(Binder.getCallingPid()).toString();
  25. //设置扬声器开/关
  26. final boolean stateChanged = mDeviceBroker.setSpeakerphoneOn(on, eventSource);
  27. if (stateChanged) {
  28. //设置成功
  29. final long ident = Binder.clearCallingIdentity();
  30. try {
  31. //发送成功后,发送扬声器状态改变的广播
  32. mContext.sendBroadcastAsUser(
  33. new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
  34. .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
  35. } finally {
  36. Binder.restoreCallingIdentity(ident);
  37. }
  38. }
  39. }

以上方法体中使用 final boolean stateChanged = mDeviceBroker.setSpeakerphoneOn(on, eventSource); 修改扬声器开关。返回true,则修改成功。该方法在AudioDeviceBroker中实现

  1. /**
  2. * Turns speakerphone on/off
  3. * @param on
  4. * @param eventSource for logging purposes
  5. * @return true if speakerphone state changed
  6. */
  7. /*package*/ boolean setSpeakerphoneOn(boolean on, String eventSource) {
  8. synchronized (mDeviceStateLock) {
  9. final boolean wasOn = isSpeakerphoneOn();
  10. if (on) {
  11. //开启扬声器
  12. if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
  13. setForceUse_Async(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource);
  14. }
  15. //进入扬声器播放的标志AudioSystem.FORCE_SPEAKER
  16. mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
  17. } else if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
  18. //取消扬声器
  19. mForcedUseForComm = AudioSystem.FORCE_NONE;
  20. }
  21. mForcedUseForCommExt = mForcedUseForComm;
  22. //此时是语音模式AudioSystem.FOR_COMMUNICATION,mForcedUseForComm表示当前是哪种音频通道
  23. setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
  24. return (wasOn != isSpeakerphoneOn());
  25. }
  26. }

以上方法中可以看到AudioService用mForcedUseForComm和mForcedUseForCommExt保存了当前的音频通道。以上方法根据状态设置对应的模式使用方法 setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);

  1. /*package*/ void setForceUse_Async(int useCase, int config, String eventSource) {
  2. //传递进来的参数封装,交给mAudioHandler处理;
  3. //arg1是AudioSystem.FOR_COMMUNICATION, arg2是mForcedUseForComm,
  4. //obj是eventSource,eventSource就是调用扬声器的进程的信息
  5. sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
  6. useCase, config, eventSource);
  7. }

我们看下sendIILMsgNoDelay方法的实现

  1. private void sendIILMsgNoDelay(int msg, int existingMsgPolicy, int arg1, int arg2, Object obj) {
  2. sendIILMsg(msg, existingMsgPolicy, arg1, arg2, obj, 0);
  3. }
  4. private void sendIILMsg(int msg, int existingMsgPolicy, int arg1, int arg2, Object obj,
  5. int delay) {
  6. if (existingMsgPolicy == SENDMSG_REPLACE) {
  7. mBrokerHandler.removeMessages(msg);
  8. } else if (existingMsgPolicy == SENDMSG_NOOP && mBrokerHandler.hasMessages(msg)) {
  9. return;
  10. }
  11. if (isMessageHandledUnderWakelock(msg)) {
  12. final long identity = Binder.clearCallingIdentity();
  13. try {
  14. mBrokerEventWakeLock.acquire(BROKER_WAKELOCK_TIMEOUT_MS);
  15. } catch (Exception e) {
  16. Log.e(TAG, "Exception acquiring wakelock", e);
  17. }
  18. Binder.restoreCallingIdentity(identity);
  19. }
  20. synchronized (sLastDeviceConnectionMsgTimeLock) {
  21. long time = SystemClock.uptimeMillis() + delay;
  22. switch (msg) {
  23. case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
  24. case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE:
  25. case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
  26. case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
  27. case MSG_IL_BTA2DP_DOCK_TIMEOUT:
  28. case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
  29. case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
  30. if (sLastDeviceConnectMsgTime >= time) {
  31. // add a little delay to make sure messages are ordered as expected
  32. time = sLastDeviceConnectMsgTime + 30;
  33. }
  34. sLastDeviceConnectMsgTime = time;
  35. break;
  36. default:
  37. break;
  38. }
  39. private void sendIILMsgNoDelay(int msg, int existingMsgPolicy, int arg1, int arg2, Object obj) {
  40. sendIILMsg(msg, existingMsgPolicy, arg1, arg2, obj, 0);
  41. }
  42. private void sendIILMsg(int msg, int existingMsgPolicy, int arg1, int arg2, Object obj,
  43. int delay) {
  44. if (existingMsgPolicy == SENDMSG_REPLACE) {
  45. mBrokerHandler.removeMessages(msg);
  46. } else if (existingMsgPolicy == SENDMSG_NOOP && mBrokerHandler.hasMessages(msg)) {
  47. return;
  48. }
  49. if (isMessageHandledUnderWakelock(msg)) {
  50. final long identity = Binder.clearCallingIdentity();
  51. try {
  52. mBrokerEventWakeLock.acquire(BROKER_WAKELOCK_TIMEOUT_MS);
  53. } catch (Exception e) {
  54. Log.e(TAG, "Exception acquiring wakelock", e);
  55. }
  56. Binder.restoreCallingIdentity(identity);
  57. }
  58. synchronized (sLastDeviceConnectionMsgTimeLock) {
  59. long time = SystemClock.uptimeMillis() + delay;
  60. switch (msg) {
  61. case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
  62. case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE:
  63. case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
  64. case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
  65. case MSG_IL_BTA2DP_DOCK_TIMEOUT:
  66. case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
  67. case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
  68. if (sLastDeviceConnectMsgTime >= time) {
  69. // add a little delay to make sure messages are ordered as expected
  70. time = sLastDeviceConnectMsgTime + 30;
  71. }
  72. sLastDeviceConnectMsgTime = time;
  73. break;
  74. default:
  75. break;
  76. }
  77. //传递进来的参数封装,交给mAudioHandler处理;
  78. //arg1是AudioSystem.FOR_COMMUNICATION, arg2是mForcedUseForComm,
  79. //obj是eventSource,eventSource就是调用扬声器的进程的信息
  80. mBrokerHandler.sendMessageAtTime(mBrokerHandler.obtainMessage(msg, arg1, arg2, obj),
  81. time);
  82. }
  83. }
  84. mBrokerHandler.sendMessageAtTime(mBrokerHandler.obtainMessage(msg, arg1, arg2, obj),
  85. time);
  86. }
  87. }

根据代码逻辑看Message的参数arg1,arg2,obj 三个参数的意义

 arg1 = AudioSystem.FOR_COMMUNICATION
arg2  = mForcedUseForComm,
obj = eventSource//eventSource就是调用扬声器的进程的信息

以上方法发送消息 消息接收

  1. case MSG_IIL_SET_FORCE_USE: // intended fall-through
  2. case MSG_IIL_SET_FORCE_BT_A2DP_USE:
  3. onSetForceUse(msg.arg1, msg.arg2, (String) msg.obj);

 该方法的实现

  1. //---------------------------------------------------------------------
  2. // Internal handling of messages
  3. // These methods are ALL synchronous, in response to message handling in BrokerHandler
  4. // Blocking in any of those will block the message queue
  5. private void onSetForceUse(int useCase, int config, String eventSource) {
  6. Log.d(TAG, "onSetForceUse usage= "+useCase+" config= "+config+" eventSource="+eventSource);
  7. if (useCase == AudioSystem.FOR_MEDIA) {
  8. postReportNewRoutes();
  9. }
  10. AudioService.sForceUseLogger.log(
  11. new AudioServiceEvents.ForceUseEvent(useCase, config, eventSource));
  12. //将语音模式AudioSystem.FOR_COMMUNICATION,音频通道mForcedUseForComm交给AudioSystem,
  13. //AudioSystem会将其设置到HAL底层
  14. AudioSystem.setForceUse(useCase, config);
  15. }

AudioService在调用AudioSystem.setForceUse(usage, config)方法时,会将相应的音频播放模式和音频通道设置到底层,从这里可以就看出,为什么在调用setSpeakerphoneOn()时要结合setMode()一起使用了;

我们看到调用了AudioSystem的setForceUse方法,该在AudioSystem中的实现 是一个native方法。

  1. //base/media/java/android/media/AudioSystem.java
  2. @UnsupportedAppUsage
  3. public static native int setForceUse(int usage, int config);

总结:

疑惑:

前面介绍中就说了android手机中有很多的音频输出通道,为啥AudioService只提供了setSpeakerphoneOn()和setBluetoothScoOn这两个手动切换音频输出通道的方法呢?
之所以AudioService只提供这两个方法,是因为有些切换是系统自动完成的,比如有线耳机,蓝牙耳机的插入和拔出等,这些音频外设的切换在应用层是无法处理的;

听筒,扬声器,有线耳机这三个输出设备的切换

1)听筒通道

听筒模式一般只会在通话或者语音过程中才会用到,所以,要使用听筒模式,必须得指定播放模式为MODE_IN_CALL或者是MODE_IN_COMMUNICATION;MODE_IN_CALL只有在通话时才可以用到。非通话时使用MODE_IN_COMMUNICATION。

2)扬声器通道

在不插入音频外设如耳机的情况下,手机中的输出设备只有听筒和扬声器,要想在听筒和扬声器中切换是比较容易的,无非就是setSpeakerphoneOn(boolean)方法调用以及设置播放模式为MODE_IN_CALL或者MODE_IN_COMMUNICATION;

3)有线耳机

耳机是音频外设,此时手机中的音频输出设备有3个,除了耳机还有听筒和扬声器;那底层是怎样选择一个设备进行音频输出的呢?这就和音频系统中的音频路由策略有关,底层在播放音频时会选择一个设备,这个逻辑跟设备的优先级有关,代码在AudoPolicyServcie中,有时间在剖析这个具体原理;

所以,当手机中的音频输出设备有耳机,听筒和扬声器时,会根据设备的优先级来进行选择;

从测试的结果来看,3个当中,耳机的优先级最高,其次是听筒;

参考文章:android音频系统(6):AudioService之音频输出通道切换

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/597374
推荐阅读
相关标签
  

闽ICP备14008679号