InCallTonePlayer

packages/services/Telecomm/src/com/android/server/telecom/InCallTonePlayer.java,该类用于通话中提示音的播放。

   public static class Factory {private final CallAudioManager mCallAudioManager;private final TelecomSystem.SyncRoot mLock;Factory(CallAudioManager callAudioManager, TelecomSystem.SyncRoot lock) {mCallAudioManager = callAudioManager;mLock = lock;}InCallTonePlayer createPlayer(int tone) {return new InCallTonePlayer(tone, mCallAudioManager, mLock);}}

使用工厂方法获取该类实例对象。

    /** The ID of the tone to play. */private final int mToneId;

mToneId是提示音id,构造函数中初始化,值定义有:

 public static final int TONE_INVALID = 0;public static final int TONE_BUSY = 1;public static final int TONE_CALL_ENDED = 2;...public static final int TONE_VOICE_PRIVACY = 13;public static final int TONE_VIDEO_UPGRADE = 14;

由于InCallTonePlayer继承自Thread,所以它的运行在run方法中

public final class InCallTonePlayer extends Thread
 @Overridepublic void run() {ToneGenerator toneGenerator = null;try {Log.d(this, "run(toneId = %s)", mToneId);final int toneType;  // Passed to ToneGenerator.startTone.final int toneVolume;  // Passed to the ToneGenerator constructor.final int toneLengthMillis;switch (mToneId) {case TONE_BUSY:// TODO: CDMA-specific tonestoneType = ToneGenerator.TONE_SUP_BUSY;       //依据id获取tone的类型、音量和时长,可见tone类型是在framework中定义的toneVolume = RELATIVE_VOLUME_HIPRI;toneLengthMillis = 4000;break;...default:throw new IllegalStateException("Bad toneId: " + mToneId);}int stream = AudioManager.STREAM_VOICE_CALL;if (mCallAudioManager.isBluetoothAudioOn()) {stream = AudioManager.STREAM_BLUETOOTH_SCO;   //设置音频stream,有蓝牙连接就切换到蓝牙设备播放... toneGenerator = new ToneGenerator(stream, toneVolume);  //初始化ToneGenerator...synchronized (this) {if (mState != STATE_STOPPED) {mState = STATE_ON;toneGenerator.startTone(toneType);   //播放try {Log.v(this, "Starting tone %d...waiting for %d ms.", mToneId,toneLengthMillis + TIMEOUT_BUFFER_MILLIS);wait(toneLengthMillis + TIMEOUT_BUFFER_MILLIS);  //阻塞直到停止播放} catch (InterruptedException e) {Log.w(this, "wait interrupted", e);}}}mState = STATE_OFF;} finally {if (toneGenerator != null) {  //资源释放toneGenerator.release();}cleanUpTonePlayer();}}
    void stopTone() {    //停止tone播放synchronized (this) {if (mState == STATE_ON) {Log.d(this, "Stopping the tone %d.", mToneId);notify();   //由于run中最后是调用wait阻塞,所以这里notify就是通知线程可以结束运行了。}mState = STATE_STOPPED;}}

tone音的原理,见frameworks/base/media/java/android/media/ToneGenerator.java中的定义:

    /*** Proprietary tone, prompt tone: 400Hz+1200Hz, 200ms ON** @see #ToneGenerator(int, int)*/public static final int  TONE_PROP_PROMPT = 27;

每个tone音是两个不同频率的音混合,响固定时间。

AsyncRingtonePlayer

packages/services/Telecomm/src/com/android/server/telecom/AsyncRingtonePlayer.java

    private static final int EVENT_PLAY = 1;private static final int EVENT_STOP = 2;private static final int EVENT_REPEAT = 3;

定义了三个消息常量,分别代表播放、停止和重复

    private Handler getNewHandler() {Preconditions.checkState(mHandler == null);HandlerThread thread = new HandlerThread("ringtone-player");thread.start();return new Handler(thread.getLooper()) {@Overridepublic void handleMessage(Message msg) {switch(msg.what) {case EVENT_PLAY:handlePlay((Uri) msg.obj);break;case EVENT_REPEAT:handleRepeat();break;case EVENT_STOP:handleStop();break;}}};}

消息处理Handler初始化中可以看出使用了HandlerThread,这样就看出铃声的处理是在线程中运行,这也对应了Async的前缀。铃声的播放等操作就是对队列的消息操作,这个不再分析。

Ringer

packages/services/Telecomm/src/com/android/server/telecom/Ringer.java,负责来电时候铃声和振动处理

 private void startRingingOrCallWaiting(Call call) {   //响铃的入口Call foregroundCall = mCallsManager.getForegroundCall();Log.v(this, "startRingingOrCallWaiting, foregroundCall: %s.", foregroundCall);if (mRingingCalls.contains(foregroundCall)&& !mCallsManager.hasActiveOrHoldingCall()) {   //普通来电状态...AudioManager audioManager =(AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);if (audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0) {if (mState != STATE_RINGING) {Log.event(call, Log.Events.START_RINGER);mState = STATE_RINGING;}mCallAudioManager.setIsRinging(call, true);...mRingtonePlayer.play(foregroundCall.getRingtone()); //使用AsyncRingtonePlayer响铃...} else {Log.v(this, "startRingingOrCallWaiting, skipping because volume is 0");}if (shouldVibrate(mContext) && !mIsVibrating) {   //振动mVibrator.vibrate(VIBRATION_PATTERN, VIBRATION_PATTERN_REPEAT,VIBRATION_ATTRIBUTES);mIsVibrating = true;}} else if (foregroundCall != null) {     //通话中来电   ...// All incoming calls are in background so play call waiting.stopRinging(call, "Stop for call-waiting");if (mState != STATE_CALL_WAITING) {Log.event(call, Log.Events.START_CALL_WAITING_TONE);mState = STATE_CALL_WAITING;}if (mCallWaitingPlayer == null) {mCallWaitingPlayer =mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING);mCallWaitingPlayer.startTone();   //使用InCallTonePlayer响起提示音}}}

音量键和电源键停止响铃

系统按键会先在PhoneWindowManager中处理然后再发送到app窗口去处理,关于铃声的PhoneWindowManager会处理,所以不会发送到app再去处理,这个对要修改这个逻辑的同学来说不大友好,不过一般厂商也不改这些默认动作。
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {...switch (keyCode) {case KeyEvent.KEYCODE_VOLUME_DOWN:case KeyEvent.KEYCODE_VOLUME_UP:case KeyEvent.KEYCODE_VOLUME_MUTE: {...TelecomManager telecomManager = getTelecommService();if (telecomManager != null) {if (telecomManager.isRinging()) {                           telecomManager.silenceRinger();...
}

音量键在来电时候按下会调用TelecomManager方法停止响铃。

private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {...if (telecomManager != null) {if (telecomManager.isRinging()) {// Pressing Power while there's a ringing incoming// call should silence the ringer.telecomManager.silenceRinger();} else if ((mIncallPowerBehavior& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0&& telecomManager.isInCall() && interactive) {// Otherwise, if "Power button ends call" is enabled,// the Power button will hang up any current active call.hungUp = telecomManager.endCall();}}...
}

电源键处理中如果是来电会停止响铃,如果是通话中而且INCALL_POWER_BUTTON_BEHAVIOR_HANGUP值大于0的话会挂断电话。

来电铃声和通话中的提示音相关推荐

  1. 电话卡插到终端服务器通话时长,通话中添加背景音的方法、终端及系统专利_专利查询 - 天眼查...

    1.一种通话中添加背景音的方法,其特征在于,包括以下步骤: 监听到来电或去电,以及开始通话时,向服务器发送获取用于本次通话中营造环境声音的音频数据的请求: 接收所述服务器反馈的对应于所述请求的至少一段 ...

  2. Android屏蔽呼出电话提示音,Android来电拦截及拦截后的提示音

    Android电话拦截及拦截后的提示音 1. 电话拦截 这个功能大家可能都知道了,就是利用反射原理调用ITelephony的隐藏方法来实现.这个就不说了,在附件的代码里有. 2.拦截后提示忙音/空号/ ...

  3. Android怎么自定义充电铃声,安卓手机自定义充电提示音教程

    安卓手机其实也是可以设置充电提示音的,我们只需要下载一个第三方软件,然后在APP中进行一些简单的设置,就可以实现自定义更换手机充电提示音了.安卓手机怎么自定义充电提示音?下面小编就为大家带来了详细的教 ...

  4. 【Python】通过winsound的Beep模块调取电脑的喇叭声音,用来在程序中做提示音或警报声

    当我们在工作时,运行的程序出现报错,或者抢票成功等,可能就不会注意到. 此时如果有1个提示音,就会更方便一点.Python通过Beep模块来实现. # 从winsound中引入Beep模块 # 使用B ...

  5. 如何实现来电拦截及拦截后的提示音

    先说下几个要点: 1. 电话拦截 这个功能大家可能都知道了,就是利用反射原理调用ITelephony的隐藏方法来实现.这个就不说了,在附件的代码里有. 2.拦截后提示忙音/空号/已关机/已停机 这个功 ...

  6. Android来电拦截及拦截后的提示音

    1. 电话拦截 这个功能大家可能都知道了,就是利用反射原理调用ITelephony的隐藏方法来实现.这个就不说了,在附件的代码里有. 2.拦截后提示忙音/空号/已关机/已停机 这个功能其实是要用到MM ...

  7. 实例详解如何实现来电拦截及拦截后的提示音

    先说下几个要点: 1. 电话拦截 这个功能大家可能都知道了,就是利用反射原理调用ITelephony的隐藏方法来实现.这个就不说了,在附件的代码里有. 2.拦截后提示忙音/空号/已关机/已停机 这个功 ...

  8. 播放生成Android4.0.3中的提示音

    题记:写这篇博客要主是加深自己对播放生成的认识和总结实现算法时的一些验经和训教,如果有错误请指出,万分感谢. 一.ToneGenerator的应用 参照com.android.contacts.dia ...

  9. Android怎么自定义充电铃声,安卓手机充电提示音怎么改 Android设置换充电提示音教程...

    自苹果ios14充电提示音火了之后,很多安卓手机也增加了这个功能,比如vivo和OPPO,而其它厂牌的手机需要下载软件才能更改充电提示音,还是比较麻烦的,安卓手机充电提示音怎么设置?下面带来全面介绍. ...

最新文章

  1. python如何实现多进程_Python实现多进程的四种方式
  2. Spring-cloud-eureka-robbin-hystrix-feign
  3. python查看文件夹文件的所有权限,Python判断某个用户对某个文件的权限
  4. C#——《C#语言程序设计》实验报告——继承与多态——电视和电灯委托
  5. php5.3教程,Php 5.3发布
  6. 在线斯诺克html5,用HTML 5打造斯诺克桌球俱乐部
  7. inspect python模块_Python inspect模块:仅限关键字参数
  8. 望城2019年华为软件云项目_今天,华为、京东两大项目在长沙开工
  9. cassandra学习笔记一
  10. .net中模拟键盘和鼠标操作
  11. 阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_03 过滤器_1_FileFilter过滤器的原理和使用...
  12. 一步步编写avalon组件02:分页组件
  13. JAVA实现微信授权登录(详解)
  14. 创龙SOM-TL437xF 核心板简介(二)
  15. [SSL_CHX][2021-08-19]转二进制
  16. android 获取蓝牙设备id_安卓蓝牙系统中如何获取蓝牙音乐的音频跟踪会话ID
  17. 新晋云计算工程师就业的感受和经验分享
  18. 命令关闭所有cmd窗口
  19. 更新chrome驱动
  20. http代理评测,代理IP评测

热门文章

  1. css 开启硬件加速的属性
  2. 打开计算机界面怎么没桌面图标,电脑桌面图标都没了?详解如何轻松恢复桌面图标...
  3. php pdo oci安装失败,pdo_oci的安装问题
  4. C#为listview选中的项添加右键菜单
  5. C#WinForm - 右键选中DataGridView或TreeView
  6. 静态时序分析(STA) —— 时序检查
  7. 树莓派4B 连接及配置
  8. python用matplotlib画皮卡丘_matplotlib常用用法总结(持续更新)
  9. oracle数据库variable,oracle中的define,declare,variable的差别
  10. 场效应管(FET)总结: