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

Android Audio音量——硬按键调节音量(七)

        前面的文章已经介绍了音量调整及静音设置的相关调用,这里我们来梳理一下通过硬按键来调节音量及静音的相关调用流程。

一、硬按键调用

        这里我们从 PhoneWindowManager 开始,该类主要负责管理设备上的所有窗口,包括应用程序窗口和其他系统窗口。同时负责输入事件的分发工作,包括我们这里要分析的硬按键事件。

1、PhoneWindowManager

源码位置:/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

interceptKeyBeforeDispatching

@Override
public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event, int policyFlags) {final int keyCode = event.getKeyCode();……if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN|| keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {if (mUseTvRouting || mHandleVolumeKeysInWM) {dispatchDirectAudioEvent(event);return -1;}……}……
}

        该函数在按键事件被分发给应用程序之前进行拦截和处理,也就是来处理音量键的按下事件。这里调用了 dispatchDirectAudioEvent() 函数。

dispatchDirectAudioEvent

private void dispatchDirectAudioEvent(KeyEvent event) {// 通过 HDMI 音频系统客户端发送按键事件(这里不关注)……try {// 处理按键事件getAudioService().handleVolumeKey(event, mUseTvRouting, mContext.getOpPackageName(), TAG);} catch (Exception e) {……}
}static IAudioService getAudioService() {IAudioService audioService = IAudioService.Stub.asInterface(ServiceManager.checkService(Context.AUDIO_SERVICE));if (audioService == null) {Log.w(TAG, "Unable to find IAudioService interface.");}return audioService;
}

        这里主要负责处理直接与音频相关的按键事件,最终调用 AudioService 中的 handleVolumeKey() 函数。

2、AudioService

源码位置:/frameworks/base/services/core/java/com/android/server/audio/AudioService.java

handleVolumeKey

public void handleVolumeKey(@NonNull KeyEvent event, boolean isOnTv, @NonNull String callingPackage, @NonNull String caller) {int keyEventMode = VOL_ADJUST_NORMAL;……int flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_FROM_KEY;switch (event.getKeyCode()) {case KeyEvent.KEYCODE_VOLUME_UP: // 音量上adjustSuggestedStreamVolume(AudioManager.ADJUST_RAISE,AudioManager.USE_DEFAULT_STREAM_TYPE,  flags, callingPackage, caller, Binder.getCallingUid(), true, keyEventMode);break;case KeyEvent.KEYCODE_VOLUME_DOWN: // 音量下adjustSuggestedStreamVolume(AudioManager.ADJUST_LOWER,AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller,Binder.getCallingUid(), true, keyEventMode);break;case KeyEvent.KEYCODE_VOLUME_MUTE: // 静音if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {adjustSuggestedStreamVolume(AudioManager.ADJUST_TOGGLE_MUTE,AudioManager.USE_DEFAULT_STREAM_TYPE, flags, callingPackage, caller,Binder.getCallingUid(), true, VOL_ADJUST_NORMAL);}break;……}
}

        可以看到,这里对于音量上、音量下和静音的硬按键处理都是调用 adjustSuggestedStreamVolume() 函数继续处理。

adjustSuggestedStreamVolume

public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,String callingPackage, String caller) {// 检查调用者是否有 MODIFY_AUDIO_SETTINGS 权限boolean hasModifyAudioSettings = mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)== PackageManager.PERMISSION_GRANTED;adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,caller, Binder.getCallingUid(), hasModifyAudioSettings, VOL_ADJUST_NORMAL);
}private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,String callingPackage, String caller, int uid, boolean hasModifyAudioSettings, int keyEventMode) {……// 通知外部音量控制器boolean hasExternalVolumeController = notifyExternalVolumeController(direction);……// 调整音量adjustStreamVolume(streamType, direction, flags, callingPackage, caller, uid, hasModifyAudioSettings, keyEventMode);
}

        对于音量的调整,我们在前面的文章中已经分析过了,这里就不再重复。这里我们主要看 notifyExternalVolumeController(),该函数其实就是相当于一个回调函数,用来通知外部音量控制器的操作。

notifyExternalVolumeController

private boolean notifyExternalVolumeController(int direction) {final IAudioPolicyCallback externalVolumeController;……sendMsg(mAudioHandler, MSG_NOTIFY_VOL_EVENT, SENDMSG_QUEUE, direction, 0 /*ignored*/,externalVolumeController, 0 /*delay*/);return true;
}

        这里通过发送消息的方式告知外部控制器音量调整的方向。

onNotifyVolumeEvent

private class AudioHandler extends Handler {……private void onNotifyVolumeEvent(@NonNull IAudioPolicyCallback apc,        @AudioManager.VolumeAdjustment int direction) {try {apc.notifyVolumeAdjust(direction);} catch(Exception e) {}}……@Overridepublic void handleMessage(Message msg) {switch (msg.what) {……case MSG_NOTIFY_VOL_EVENT:onNotifyVolumeEvent((IAudioPolicyCallback) msg.obj, msg.arg1);break;……}}
}

        这里首先对 MSG_NOTIFY_VOL_EVENT 消息进行了处理,调用了 onNotifyVolumeEvent() 函数。接着又调用了 IAudioPolicyCallback 的 notifyVolumeAdjust() 函数,这里一个 AIDL 接口,其实现在 AudioPolicy 中。

3、AudioPolicy

源码位置:/frameworks/base/media/java/android/media/audiopolicy/AudioPolicy.java

notifyVolumeAdjust

private final IAudioPolicyCallback mPolicyCb = new IAudioPolicyCallback.Stub() {public void notifyVolumeAdjust(int adjustment) {sendMsg(MSG_VOL_ADJUST, null /* ignored */, adjustment);……}
};

        这里发送一个 MSG_VOL_ADJUST 用来处理音量相关的回调。

private final static int MSG_VOL_ADJUST = 6;private class EventHandler extends Handler {……    @Overridepublic void handleMessage(Message msg) {switch(msg.what) {// 焦点状态……case MSG_VOL_ADJUST:if (mVolCb != null) {mVolCb.onVolumeAdjustment(msg.arg1);}break;……}}
}

        这里除了处理硬按键调整音量外,还有焦点状态相关的处理,这里我们不去关注。我们先来看一下 mVolCb 参数。

setAudioPolicyVolumeCallback

public static class Builder {private AudioPolicyVolumeCallback mVolCb;@NonNullpublic Builder setAudioPolicyVolumeCallback(@NonNull AudioPolicyVolumeCallback vc) {……mVolCb = vc;return this;}……
}public static abstract class AudioPolicyVolumeCallback {public AudioPolicyVolumeCallback() {}/*** 当按下按键事件触发音量键相关更改时调用* @param adjustment 键的音量调节类型*/public void onVolumeAdjustment(@AudioManager.VolumeAdjustment int adjustment) {}
}

        可以看到 mVolCb 其实就是通过 Builder 中的 setAudioPolicyVolumeCallback() 函数设置进来的一个 AudioPolicyVolumeCallback 回调,下面就来看一下该回调的监听。

4、CarAudioService

        不难发现,在 CarAudioService 中的 init() 中调用了 setupDynamicRoutingLocked() 函数,该函数中设置了 AudioPolicyVolumeCallback 回调监听。

源码位置:/packages/services/Car/service/src/com/android/car/audio/CarAudioService.java

setupDynamicRoutingLocked

private void setupDynamicRoutingLocked() {final AudioPolicy.Builder builder = new AudioPolicy.Builder(mContext);……// Attach the {@link AudioPolicyVolumeCallback}builder.setAudioPolicyVolumeCallback(mAudioPolicyVolumeCallback);……
}

        下面来看一下监听函数的相关处理。

private final AudioPolicy.AudioPolicyVolumeCallback mAudioPolicyVolumeCallback =new AudioPolicy.AudioPolicyVolumeCallback() {@Overridepublic void onVolumeAdjustment(int adjustment) {int zoneId = CarAudioManager.PRIMARY_AUDIO_ZONE;// 获取音频上下文@AudioContext int suggestedContext = getSuggestedAudioContext();// 获取音量组 IDint groupId;synchronized (mImplLock) {groupId = getVolumeGroupIdForAudioContextLocked(zoneId, suggestedContext);}……// 获取当前音量final int currentVolume = getGroupVolume(zoneId, groupId);final int flags = AudioManager.FLAG_FROM_KEY | AudioManager.FLAG_SHOW_UI;// 处理不同的音量调整情况switch (adjustment) {case AudioManager.ADJUST_LOWER: // 降低音量int minValue = Math.max(currentVolume - 1, getGroupMinVolume(zoneId, groupId));setGroupVolume(zoneId, groupId, minValue , flags);break;case AudioManager.ADJUST_RAISE: // 提高音量int maxValue =  Math.min(currentVolume + 1, getGroupMaxVolume(zoneId, groupId));setGroupVolume(zoneId, groupId, maxValue, flags);break;case AudioManager.ADJUST_MUTE: // 静音setMasterMute(true, flags);callbackMasterMuteChange(zoneId, flags);break;case AudioManager.ADJUST_UNMUTE: // 取消静音setMasterMute(false, flags);callbackMasterMuteChange(zoneId, flags);break;case AudioManager.ADJUST_TOGGLE_MUTE: // 切换静音状态setMasterMute(!mAudioManager.isMasterMute(), flags);callbackMasterMuteChange(zoneId, flags);break;case AudioManager.ADJUST_SAME:default:break;}}
};

        这里可以看到在静音状态改变后,调用 callbackMasterMuteChange 方法通知静音状态的变化,这与前面文章中静音设置中的回调函数相对应。


http://www.mrgr.cn/news/13154.html

相关文章:

  • 开学季 小学学科资源免费取 让你节省2W元报班费
  • 【计算机系统架构】从0开始构建一台现代计算机|时序逻辑、主存储器|第3章
  • Spring-2- AOP 切面编程
  • MySql中常用的sql语句大全(工作常用篇)
  • unicode编码存在转义字符,导致乱码问题的解决方案
  • 【XML详解】
  • 获取文件属性/库Lib
  • 力扣: 删除链表的倒数第N个元素
  • MySQL编译安装-麒麟V10 x86
  • TCP+UDP通信
  • P10916 椰子
  • Java基础(包装类)
  • python 使用宝塔面板在云服务器上搭建 flask
  • 【mysql】mysql之索引学习
  • 离散数学中的逻辑应用(2)
  • FFmpeg的入门实践系列四(AVS)
  • leetcode322:零钱兑换
  • vue项目中,修改elementui一些复杂控件样式
  • Debezium系列之:记录一次命令行可以访问mysql数据库,但是debezium connector无法访问数据库原因排查
  • PyTorch深度学习模型训练流程:(一、分类)