来电铃声和通话中的提示音
来源:互联网 发布:apache tomcat 6 编辑:程序博客网 时间:2024/04/30 01:04
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
@Override public 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 tones toneType = 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()) { @Override public 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的话会挂断电话。
0 0
- 来电铃声和通话中的提示音
- 来电铃声和通话中的提示音
- mt6735 “提示音和通知”中试听手机铃声时来电,来电铃声和预览铃声声音重叠
- FAQ08356][Audio Volume]来电\闹钟\短信提示\铃声预览\拍照音等,在播放过程中插入或者拔出耳机,外放中的铃声音异常
- Android设置来电铃声和分享操作
- Android 系统自动获取来电/短信/提示铃声
- SoundPool提示音铃声
- android 播放来电铃声
- android 设置来电铃声
- Android来电铃声设置
- android5.0联系人铃声设置和来电读取分析
- 来电手机铃声捕获方法
- 设置默认来电铃声 android
- Android 代码设置来电铃声
- Android 修改系统来电铃声
- Android4.22来电铃声流程
- Android 代码设置来电铃声
- Android 代码设置来电铃声
- Visual Studio 2017 Intro
- 《java语言程序设计》第七章学习总结
- git服务器搭建
- 使用 intellij idea IDE 搭建 Spring boot 项目
- 创建文件判断
- 来电铃声和通话中的提示音
- 作用域是什么
- Linux下PPTPD搭建VPN服务器连接后无法上外网及619错误的解决办法
- Android Studio 使用小技巧和快捷键
- Android 分享文件
- 深度优先搜索与广度优先搜索
- XMind中关于“大图”的操作技巧(二)
- HDU 1004(map) Let the Balloon Rise
- Android客户端和Java服务器端Socket代码连的坑(接空指针异常问题)