当前位置: 首页 > news >正文

嵌入式音频开发(3)- AudioService核心功能

音量调节

TV/STB等产品的音量调节主要是通过遥控器上的音量键实现。具体到AudioService则是adjustSuggestedStreamVolume函数。

adjustSuggestedStreamVolume

 /** All callers come from platform apps/system server, so no attribution tag is needed */private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,String callingPackage, String caller, int uid, int pid, boolean hasModifyAudioSettings,int keyEventMode) {//判断是否有外部的音量控制器。这个功能没用过,但猜测是可以bypass Android的音量处理,有厂商自己定制音量调节的实现。boolean hasExternalVolumeController = notifyExternalVolumeController(direction);if (hasExternalVolumeController) {return;}final int streamType;synchronized (mForceControlStreamLock) {// Request lock in case mVolumeControlStream is changed by other thread.if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1streamType = mVolumeControlStream;} else {final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType);final boolean activeForReal;if (maybeActiveStreamType == AudioSystem.STREAM_RING|| maybeActiveStreamType == AudioSystem.STREAM_NOTIFICATION) {activeForReal = wasStreamActiveRecently(maybeActiveStreamType, 0);} else {activeForReal = mAudioSystem.isStreamActive(maybeActiveStreamType, 0);}if (activeForReal || mVolumeControlStream == -1) {streamType = maybeActiveStreamType;} else {streamType = mVolumeControlStream;}}}final boolean isMute = isMuteAdjust(direction);ensureValidStreamType(streamType);final int resolvedStream = mStreamVolumeAlias[streamType];// Play sounds on STREAM_RING only.if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&resolvedStream != AudioSystem.STREAM_RING) {flags &= ~AudioManager.FLAG_PLAY_SOUND;}// For notifications/ring, show the ui before making any adjustments// Don't suppress mute/unmute requests// Don't suppress adjustments for single volume deviceif (mVolumeController.suppressAdjustment(resolvedStream, flags, isMute)&& !mIsSingleVolume) {direction = 0;flags &= ~AudioManager.FLAG_PLAY_SOUND;flags &= ~AudioManager.FLAG_VIBRATE;if (DEBUG_VOL) Log.d(TAG, "Volume controller suppressed adjustment");}adjustStreamVolume(streamType, direction, flags, callingPackage, caller, uid, pid,null, hasModifyAudioSettings, keyEventMode);}

adjustSuggestedStreamVolume针对各种音量调节过程中涉及的场景做完相应的处理后(这部分代码有个大概的印象就可以了,如此琐碎很难记住),最后调用到adjustStreamVolume。

adjustStreamVolume

audioservice的代码是真的长,只讲解一些比较常见的逻辑。受限于开发经验,如果遗漏还请见谅。重点逻辑已使用中文做注释。

protected void adjustStreamVolume(int streamType, int direction, int flags,String callingPackage, String caller, int uid, int pid, String attributionTag,boolean hasModifyAudioSettings, int keyEventMode) {if (mUseFixedVolume) {return;}if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream=" + streamType + ", dir=" + direction+ ", flags=" + flags + ", caller=" + caller);ensureValidDirection(direction);ensureValidStreamType(streamType);boolean isMuteAdjust = isMuteAdjust(direction);if (isMuteAdjust && !isStreamAffectedByMute(streamType)) {return;}// If adjust is mute and the stream is STREAM_VOICE_CALL or STREAM_BLUETOOTH_SCO, make sure// that the calling app have the MODIFY_PHONE_STATE permission.if (isMuteAdjust &&(streamType == AudioSystem.STREAM_VOICE_CALL ||streamType == AudioSystem.STREAM_BLUETOOTH_SCO) &&mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)!= PackageManager.PERMISSION_GRANTED) {Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: adjustStreamVolume from pid="+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());return;}// If the stream is STREAM_ASSISTANT,// make sure that the calling app have the MODIFY_AUDIO_ROUTING permission.if (streamType == AudioSystem.STREAM_ASSISTANT &&mContext.checkPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING, pid, uid)!= PackageManager.PERMISSION_GRANTED) {Log.w(TAG, "MODIFY_AUDIO_ROUTING Permission Denial: adjustStreamVolume from pid="+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());return;}// use stream type alias here so that streams with same alias have the same behavior,// including with regard to silent mode control (e.g the use of STREAM_RING below and in// checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)//获得streamtype对应的别名,所谓别名就是可以让一些streamtype共享音量一套音量调节int streamTypeAlias = mStreamVolumeAlias[streamType];VolumeStreamState streamState = mStreamStates[streamTypeAlias];//获得streamtype当前正在输出的设备类型final int device = getDeviceForStream(streamTypeAlias);int aliasIndex = streamState.getIndex(device);boolean adjustVolume = true;int step;//所谓蓝牙的绝对音量就是音量算法由蓝牙设备执行。而非绝对音量则是由audiofinger的audiomixer执行。// skip a2dp absolute volume control request when the device// is neither an a2dp device nor BLE deviceif ((!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)&& !AudioSystem.DEVICE_OUT_ALL_BLE_SET.contains(device))&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) != 0) {return;}// If we are being called by the system (e.g. hardware keys) check for current user// so we handle user restrictions correctly.if (uid == android.os.Process.SYSTEM_UID) {uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));}// validate calling package and app opif (!checkNoteAppOp(STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage, attributionTag)) {return;}// reset any pending volume commandsynchronized (mSafeMediaVolumeStateLock) {mPendingVolumeCommand = null;}flags &= ~AudioManager.FLAG_FIXED_VOLUME;if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) {flags |= AudioManager.FLAG_FIXED_VOLUME;// Always toggle between max safe volume and 0 for fixed volume devices where safe// volume is enforced, and max and 0 for the others.// This is simulated by stepping by the full allowed volume rangeif (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&mSafeMediaVolumeDevices.contains(device)) {step = safeMediaVolumeIndex(device);} else {step = streamState.getMaxIndex();}if (aliasIndex != 0) {aliasIndex = step;}} else {//计算音量调节步幅(按键每次步进是1)// convert one UI step (+/-1) into a number of internal units on the stream aliasstep = rescaleStep(10, streamType, streamTypeAlias);}// If either the client forces allowing ringer modes for this adjustment,// or the stream type is one that is affected by ringer modesif (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||(isUiSoundsStreamType(streamTypeAlias))) {int ringerMode = getRingerModeInternal();// do not vibrate if already in vibrate modeif (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {flags &= ~AudioManager.FLAG_VIBRATE;}// Check if the ringer mode handles this adjustment. If it does we don't// need to adjust the volume further.final int result = checkForRingerModeChange(aliasIndex, direction, step,streamState.mIsMuted, callingPackage, flags);adjustVolume = (result & FLAG_ADJUST_VOLUME) != 0;// If suppressing a volume adjustment in silent mode, display the UI hintif ((result & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {flags |= AudioManager.FLAG_SHOW_SILENT_HINT;}// If suppressing a volume down adjustment in vibrate mode, display the UI hintif ((result & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0) {flags |= AudioManager.FLAG_SHOW_VIBRATE_HINT;}}// If the ringer mode or zen is muting the stream, do not change stream unless// it'll cause us to exit dndif (!volumeAdjustmentAllowedByDnd(streamTypeAlias, flags)) {adjustVolume = false;}int oldIndex = mStreamStates[streamType].getIndex(device);// Check if the volume adjustment should be handled by an absolute volume controller insteadif (isAbsoluteVolumeDevice(device)&& (flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0) {AbsoluteVolumeDeviceInfo info = mAbsoluteVolumeDeviceInfoMap.get(device);if (info.mHandlesVolumeAdjustment) {dispatchAbsoluteVolumeAdjusted(streamType, info, oldIndex, direction,keyEventMode);return;}}if (adjustVolume && (direction != AudioManager.ADJUST_SAME)&& (keyEventMode != AudioDeviceVolumeManager.ADJUST_MODE_END)) {mAudioHandler.removeMessages(MSG_UNMUTE_STREAM);if (isMuteAdjust && !mFullVolumeDevices.contains(device)) {boolean state;if (direction == AudioManager.ADJUST_TOGGLE_MUTE) {state = !streamState.mIsMuted;} else {state = direction == AudioManager.ADJUST_MUTE;}muteAliasStreams(streamTypeAlias, state);} else if ((direction == AudioManager.ADJUST_RAISE) &&!checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {Log.e(TAG, "adjustStreamVolume() safe volume index = " + oldIndex);mVolumeController.postDisplaySafeVolumeWarning(flags);} else if (!isFullVolumeDevice(device)//在此处修改stream对应的index&& (streamState.adjustIndex(direction * step, device, caller,hasModifyAudioSettings)|| streamState.mIsMuted)) {// Post message to set system volume (it in turn will post a// message to persist).if (streamState.mIsMuted) {// Unmute the stream if it was previously mutedif (direction == AudioManager.ADJUST_RAISE) {// unmute immediately for volume upmuteAliasStreams(streamTypeAlias, false);} else if (direction == AudioManager.ADJUST_LOWER) {if (mIsSingleVolume) {sendMsg(mAudioHandler, MSG_UNMUTE_STREAM, SENDMSG_QUEUE,streamTypeAlias, flags, null, UNMUTE_STREAM_DELAY);}}}//重点来了,发送设置设备音量的消息,这个调用会使得音量修改到达底层audioflinger并真正生效sendMsg(mAudioHandler,MSG_SET_DEVICE_VOLUME,SENDMSG_QUEUE,device,0,streamState,0);}int newIndex = mStreamStates[streamType].getIndex(device);// Check if volume update should be send to AVRCPif (streamTypeAlias == AudioSystem.STREAM_MUSIC&& AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {if (DEBUG_VOL) {Log.d(TAG, "adjustSreamVolume: postSetAvrcpAbsoluteVolumeIndex index="+ newIndex + "stream=" + streamType);}mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(newIndex / 10);} else if (isAbsoluteVolumeDevice(device)&& (flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0) {AbsoluteVolumeDeviceInfo info = mAbsoluteVolumeDeviceInfoMap.get(device);dispatchAbsoluteVolumeChanged(streamType, info, newIndex);}if (AudioSystem.isLeAudioDeviceType(device)&& streamType == getBluetoothContextualVolumeStream()&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {if (DEBUG_VOL) {Log.d(TAG, "adjustSreamVolume postSetLeAudioVolumeIndex index="+ newIndex + " stream=" + streamType);}mDeviceBroker.postSetLeAudioVolumeIndex(newIndex,mStreamStates[streamType].getMaxIndex(), streamType);}// Check if volume update should be send to Hearing Aidif (device == AudioSystem.DEVICE_OUT_HEARING_AID) {// only modify the hearing aid attenuation when the stream to modify matches// the one expected by the hearing aidif (streamType == getBluetoothContextualVolumeStream()) {if (DEBUG_VOL) {Log.d(TAG, "adjustSreamVolume postSetHearingAidVolumeIndex index="+ newIndex + " stream=" + streamType);}mDeviceBroker.postSetHearingAidVolumeIndex(newIndex, streamType);}}}final int newIndex = mStreamStates[streamType].getIndex(device);if (adjustVolume) {//针对接有HDMI设备并且适配了HDMI相关接口的产品synchronized (mHdmiClientLock) {if (mHdmiManager != null) {// At most one of mHdmiPlaybackClient and mHdmiTvClient should be non-nullHdmiClient fullVolumeHdmiClient = mHdmiPlaybackClient;if (mHdmiTvClient != null) {fullVolumeHdmiClient = mHdmiTvClient;}if (fullVolumeHdmiClient != null&& mHdmiCecVolumeControlEnabled&& streamTypeAlias == AudioSystem.STREAM_MUSIC// vol change on a full volume device&& isFullVolumeDevice(device)) {int keyCode = KeyEvent.KEYCODE_UNKNOWN;switch (direction) {case AudioManager.ADJUST_RAISE:keyCode = KeyEvent.KEYCODE_VOLUME_UP;break;case AudioManager.ADJUST_LOWER:keyCode = KeyEvent.KEYCODE_VOLUME_DOWN;break;case AudioManager.ADJUST_TOGGLE_MUTE:case AudioManager.ADJUST_MUTE:case AudioManager.ADJUST_UNMUTE:// Many CEC devices only support toggle mute. Therefore, we send the// same keycode for all three mute options.keyCode = KeyEvent.KEYCODE_VOLUME_MUTE;break;default:break;}if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {final long ident = Binder.clearCallingIdentity();try {switch (keyEventMode) {case AudioDeviceVolumeManager.ADJUST_MODE_NORMAL:fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, true);fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, false);break;case AudioDeviceVolumeManager.ADJUST_MODE_START:fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, true);break;case AudioDeviceVolumeManager.ADJUST_MODE_END:fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, false);break;default:Log.e(TAG, "Invalid keyEventMode " + keyEventMode);}} finally {Binder.restoreCallingIdentity(ident);}}}if (streamTypeAlias == AudioSystem.STREAM_MUSIC&& (oldIndex != newIndex || isMuteAdjust)) {maybeSendSystemAudioStatusCommand(isMuteAdjust);}}}}//发送音量改变的通知sendVolumeUpdate(streamType, oldIndex, newIndex, flags, device);}

adjustStreamVolume里面的代码同样很长,涉及到很多不同的场景要处理。主脉络是

1.修改streamtype的index。我们在此处加log可以判断音量值(index)是否调节符合预期。

streamState.adjustIndex(direction * step, device, caller,hasModifyAudioSettings)

2.发送消息(message)将刷新后的音量值(index)设置到底层。就是这行代码。

  sendMsg(mAudioHandler,MSG_SET_DEVICE_VOLUME,SENDMSG_QUEUE,device,0,streamState,0);

MSG_SET_DEVICE_VOLUME消息处理

Audioservice内部有个handler负责将client对server的调用全部顺序执行。

 public void handleMessage(Message msg) {
5350              switch (msg.what) {
5351  
5352                  case MSG_SET_DEVICE_VOLUME:
5353                      setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
5354                      break;
5355  
   /*package*/ void setDeviceVolume(VolumeStreamState streamState, int device) {
5033  
5034          final boolean isAvrcpAbsVolSupported = mDeviceBroker.isAvrcpAbsoluteVolumeSupported();
5035  
5036          synchronized (VolumeStreamState.class) {
5037              // Apply volume//将VolumeStreamState的index值设置到底层
5038              streamState.applyDeviceVolume_syncVSS(device, isAvrcpAbsVolSupported);
5039  
5040              // Apply change to all streams using this one as alias//将使用当前streamtype作为别名的streamtype的index也同步设置到底层
5041              int numStreamTypes = AudioSystem.getNumStreamTypes();
5042              for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
5043                  if (streamType != streamState.mStreamType &&
5044                          mStreamVolumeAlias[streamType] == streamState.mStreamType) {
5045                      // Make sure volume is also maxed out on A2DP device for aliased stream
5046                      // that may have a different device selected
5047                      int streamDevice = getDeviceForStream(streamType);
5048                      if ((device != streamDevice) && isAvrcpAbsVolSupported
5049                              && ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0)) {
5050                          mStreamStates[streamType].applyDeviceVolume_syncVSS(device,
5051                                  isAvrcpAbsVolSupported);
5052                      }
5053                      mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice,
5054                              isAvrcpAbsVolSupported);
5055                  }
5056              }
5057          }
5058          // Post a persist volume msg//将VolumeStreamState的index保存到settings进行持久化
5059          sendMsg(mAudioHandler,
5060                  MSG_PERSIST_VOLUME,
5061                  SENDMSG_QUEUE,
5062                  device,
5063                  0,
5064                  streamState,
5065                  PERSIST_DELAY);
5066  
5067      }

由setDeviceVolume函数负责处理MSG_SET_DEVICE_VOLUME消息。主要逻辑如下:

1.通过applyDeviceVolume_syncVSS函数将VolumeStreamState的index值同步设置到底层。VSS就是VolumeStreamState的缩写。

2.将使用当前streamtype作为别名的streamtype的index也同步设置到底层。

3.将VolumeStreamState的index保存到settings进行持久化

applyDeviceVolume_syncVSS

函数很简单,比较重要的一点是通过将index设置为0来实现mute。接着调用setStreamVolumeIndex函数。

void applyDeviceVolume_syncVSS(int device, boolean isAvrcpAbsVolSupported) {
4656              int index;
4657              if (mIsMuted) {
4658                  index = 0;
4659              } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && isAvrcpAbsVolSupported) {
4660                  index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
4661              } else if ((device & mFullVolumeDevices) != 0) {
4662                  index = (mIndexMax + 5)/10;
4663              } else if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
4664                  index = (mIndexMax + 5)/10;
4665              } else {
4666                  index = (getIndex(device) + 5)/10;
4667              }
4668              setStreamVolumeIndex(index, device);
4669          }

setStreamVolumeIndex

setStreamVolumeIndex函数最终将VolumeStreamState的mStreamType,index和device通过AudioSystem接口设置到底层。

  private void setStreamVolumeIndex(int index, int device) {
4645              // Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
4646              // This allows RX path muting by the audio HAL only when explicitly muted but not when
4647              // index is just set to 0 to repect BT requirements
4648              if (mStreamType == AudioSystem.STREAM_BLUETOOTH_SCO && index == 0 && !mIsMuted) {
4649                  index = 1;
4650              }
4651              AudioSystem.setStreamVolumeIndexAS(mStreamType, index, device);
4652          }
4653  

MSG_PERSIST_VOLUME消息处理

通过调用persistVolume函数处理MSG_PERSIST_VOLUME消息。

5360                  case MSG_PERSIST_VOLUME:
5361                      persistVolume((VolumeStreamState) msg.obj, msg.arg1);
5362                      break;

persistVolume持久化音量index

通过Android系统的ContentResolver机制对音量的index进行持久化。对ContentResolver机制感兴趣的同学可以搜集资料了解一下。根据AudioService持久化音量的方式,我们可以在自己的应用里面监听音量index的变化以实现某些定制需求。

        private void persistVolume(VolumeStreamState streamState, int device) {
5088              if (mUseFixedVolume) {
5089                  return;
5090              }
5091              if (mIsSingleVolume && (streamState.mStreamType != AudioSystem.STREAM_MUSIC)) {
5092                  return;
5093              }
5094              if (streamState.hasValidSettingsName()) {
5095                  System.putIntForUser(mContentResolver,
5096                          streamState.getSettingNameForDevice(device),
5097                          (streamState.getIndex(device) + 5)/ 10,
5098                          UserHandle.USER_CURRENT);
5099              }
5100          }

至此,调节音量在AudioService中的实现已经分析完了。接下来的逻辑我们在后面继续讨论,现在做一下总结。

总结

1.AudioService通过VolumeStreamState维护每一种streamtype对应的index。
2.每个index由streamtype和device共同决定。
3.index设置到底层生效最终是通过AudioSystem的接口。(后续我们将会看到底层设置音量做了哪些工作)
4.Audioservice涉及和底层交互的逻辑都会通过消息机制(handler)进行异步序列化处理。

http://www.lryc.cn/news/626727.html

相关文章:

  • 嵌入式开发学习———Linux环境下网络编程学习(四)
  • 04-认证授权服务开发指南
  • 读《精益数据分析》:规模化(Scale)—— 复制成功,进军新市场
  • Kafka如何保证消费确认与顺序消费?
  • Python爬虫实战:研究dark-fantasy,构建奇幻文学数据采集分析系统
  • GitHub宕机生存指南:从应急协作到高可用架构设计
  • BM25 vs TF-IDF:经典文本检索方法的对比
  • 《算法导论》第 34 章 - NP 完全性
  • RK Android14 新建分区恢复出厂设置分区数据不擦除及开机动画自定义(二)
  • 细说数仓中不同类型的维度
  • 哈希:字母异位词分组
  • Linux系统:C语言进程间通信信号(Signal)
  • 动态规划----6.单词拆分
  • Java 大视界 -- Java 大数据在智能医疗远程会诊数据管理与协同诊断优化中的应用(402)
  • C++---向下取整(>>)与向零取整(/)
  • WPF Alert弹框控件 - 完全使用指南
  • 【力扣 买卖股票的最佳时机 Java/Python】
  • 【Unity3D优化】平衡 Hide 与 Destroy:基于性能等级与 LRU 的 UI 管理策略与实践思考
  • 大数据毕业设计选题推荐-基于Hadoop的电信客服数据处理与分析系统-Spark-HDFS-Pandas
  • 计算机网络模型
  • 华为数通认证学习
  • CSS 定位的核心属性:position
  • SPSS数据文件的建立与管理
  • JAVA中向量数据库(Milvus)怎么配合大模型使用
  • 案例分享:BRAV-7123助力家用型人形机器人,智能生活未来已来
  • vscode连接docker
  • Linux 文本处理与 Shell 编程笔记:正则表达式、sed、awk 与变量脚本
  • React-native之组件
  • 51单片机-驱动LED点阵模块教程
  • Gitee仓库 日常操作详细步骤