来电铃声和通话中的提示音

来源:互联网 发布:linux 解压包指定目录 编辑:程序博客网 时间:2024/04/30 00:57

InCallTonePlayer

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

[java] view plain copy
  1. public static class Factory {  
  2.      private final CallAudioManager mCallAudioManager;  
  3.      private final TelecomSystem.SyncRoot mLock;  
  4.   
  5.      Factory(CallAudioManager callAudioManager, TelecomSystem.SyncRoot lock) {  
  6.          mCallAudioManager = callAudioManager;  
  7.          mLock = lock;  
  8.      }  
  9.   
  10.      InCallTonePlayer createPlayer(int tone) {  
  11.          return new InCallTonePlayer(tone, mCallAudioManager, mLock);  
  12.      }  
  13.  }  
使用工厂方法获取该类实例对象。
[java] view plain copy
  1. /** The ID of the tone to play. */  
  2. private final int mToneId;  
mToneId是提示音id,构造函数中初始化,值定义有:
[java] view plain copy
  1. public static final int TONE_INVALID = 0;  
  2.    public static final int TONE_BUSY = 1;  
  3.    public static final int TONE_CALL_ENDED = 2;  
  4.    ...  
  5.    public static final int TONE_VOICE_PRIVACY = 13;  
  6.    public static final int TONE_VIDEO_UPGRADE = 14;  
由于InCallTonePlayer继承自Thread,所以它的运行在run方法中
[java] view plain copy
  1. public final class InCallTonePlayer extends Thread  
[java] view plain copy
  1. @Override  
  2.    public void run() {  
  3.        ToneGenerator toneGenerator = null;  
  4.        try {  
  5.            Log.d(this"run(toneId = %s)", mToneId);  
  6.   
  7.            final int toneType;  // Passed to ToneGenerator.startTone.  
  8.            final int toneVolume;  // Passed to the ToneGenerator constructor.  
  9.            final int toneLengthMillis;  
  10.   
  11.            switch (mToneId) {  
  12.                case TONE_BUSY:  
  13.                    // TODO: CDMA-specific tones  
  14.                    toneType = ToneGenerator.TONE_SUP_BUSY;       //依据id获取tone的类型、音量和时长,可见tone类型是在framework中定义的  
  15.                    toneVolume = RELATIVE_VOLUME_HIPRI;  
  16.                    toneLengthMillis = 4000;  
  17.                    break;  
  18.                ...  
  19.                default:  
  20.                    throw new IllegalStateException("Bad toneId: " + mToneId);  
  21.            }  
  22.   
  23.            int stream = AudioManager.STREAM_VOICE_CALL;  
  24.            if (mCallAudioManager.isBluetoothAudioOn()) {  
  25.                stream = AudioManager.STREAM_BLUETOOTH_SCO;   //设置音频stream,有蓝牙连接就切换到蓝牙设备播放  
  26.              
  27.            ...   
  28.            toneGenerator = new ToneGenerator(stream, toneVolume);  //初始化ToneGenerator  
  29.            ...  
  30.   
  31.   
  32.            synchronized (this) {  
  33.                if (mState != STATE_STOPPED) {  
  34.                    mState = STATE_ON;  
  35.                    toneGenerator.startTone(toneType);   //播放  
  36.                    try {  
  37.                        Log.v(this"Starting tone %d...waiting for %d ms.", mToneId,  
  38.                                toneLengthMillis + TIMEOUT_BUFFER_MILLIS);  
  39.                        wait(toneLengthMillis + TIMEOUT_BUFFER_MILLIS);  //阻塞直到停止播放  
  40.                    } catch (InterruptedException e) {  
  41.                        Log.w(this"wait interrupted", e);  
  42.                    }  
  43.                }  
  44.            }  
  45.            mState = STATE_OFF;  
  46.        } finally {  
  47.            if (toneGenerator != null) {  //资源释放  
  48.                toneGenerator.release();  
  49.            }  
  50.            cleanUpTonePlayer();  
  51.        }  
  52.    }  
[java] view plain copy
  1. void stopTone() {    //停止tone播放  
  2.     synchronized (this) {  
  3.         if (mState == STATE_ON) {  
  4.             Log.d(this"Stopping the tone %d.", mToneId);  
  5.             notify();   //由于run中最后是调用wait阻塞,所以这里notify就是通知线程可以结束运行了。  
  6.         }  
  7.         mState = STATE_STOPPED;  
  8.     }  
  9. }  
tone音的原理,见frameworks/base/media/java/android/media/ToneGenerator.java中的定义:
[java] view plain copy
  1. /** 
  2.  * Proprietary tone, prompt tone: 400Hz+1200Hz, 200ms ON 
  3.  * 
  4.  * @see #ToneGenerator(int, int) 
  5.  */  
  6. public static final int  TONE_PROP_PROMPT = 27;  
每个tone音是两个不同频率的音混合,响固定时间。

AsyncRingtonePlayer

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

[java] view plain copy
  1. private static final int EVENT_PLAY = 1;  
  2. private static final int EVENT_STOP = 2;  
  3. private static final int EVENT_REPEAT = 3;  
定义了三个消息常量,分别代表播放、停止和重复
[java] view plain copy
  1. private Handler getNewHandler() {  
  2.     Preconditions.checkState(mHandler == null);  
  3.   
  4.     HandlerThread thread = new HandlerThread("ringtone-player");  
  5.     thread.start();  
  6.   
  7.     return new Handler(thread.getLooper()) {  
  8.         @Override  
  9.         public void handleMessage(Message msg) {  
  10.             switch(msg.what) {  
  11.                 case EVENT_PLAY:  
  12.                     handlePlay((Uri) msg.obj);  
  13.                     break;  
  14.                 case EVENT_REPEAT:  
  15.                     handleRepeat();  
  16.                     break;  
  17.                 case EVENT_STOP:  
  18.                     handleStop();  
  19.                     break;  
  20.             }  
  21.         }  
  22.     };  
  23. }  
消息处理Handler初始化中可以看出使用了HandlerThread,这样就看出铃声的处理是在线程中运行,这也对应了Async的前缀。铃声的播放等操作就是对队列的消息操作,这个不再分析。

Ringer

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

[java] view plain copy
  1. private void startRingingOrCallWaiting(Call call) {   //响铃的入口  
  2.        Call foregroundCall = mCallsManager.getForegroundCall();  
  3.        Log.v(this"startRingingOrCallWaiting, foregroundCall: %s.", foregroundCall);  
  4.        
  5.        if (mRingingCalls.contains(foregroundCall)  
  6.            && !mCallsManager.hasActiveOrHoldingCall()) {   //普通来电状态  
  7.               
  8.            ...  
  9.   
  10.            AudioManager audioManager =  
  11.                    (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);  
  12.            if (audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0) {  
  13.                if (mState != STATE_RINGING) {  
  14.                    Log.event(call, Log.Events.START_RINGER);  
  15.                    mState = STATE_RINGING;  
  16.                }  
  17.                mCallAudioManager.setIsRinging(call, true);  
  18.                ...  
  19.                mRingtonePlayer.play(foregroundCall.getRingtone()); //使用AsyncRingtonePlayer响铃  
  20.                ...  
  21.            } else {  
  22.                Log.v(this"startRingingOrCallWaiting, skipping because volume is 0");  
  23.            }  
  24.   
  25.            if (shouldVibrate(mContext) && !mIsVibrating) {   //振动  
  26.                mVibrator.vibrate(VIBRATION_PATTERN, VIBRATION_PATTERN_REPEAT,  
  27.                        VIBRATION_ATTRIBUTES);  
  28.                mIsVibrating = true;  
  29.            }  
  30.        } else if (foregroundCall != null) {     //通话中来电     
  31.            ...  
  32.   
  33.            // All incoming calls are in background so play call waiting.  
  34.            stopRinging(call, "Stop for call-waiting");  
  35.   
  36.   
  37.            if (mState != STATE_CALL_WAITING) {  
  38.                Log.event(call, Log.Events.START_CALL_WAITING_TONE);  
  39.                mState = STATE_CALL_WAITING;  
  40.            }  
  41.   
  42.            if (mCallWaitingPlayer == null) {  
  43.                mCallWaitingPlayer =  
  44.                        mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING);  
  45.                mCallWaitingPlayer.startTone();   //使用InCallTonePlayer响起提示音  
  46.            }  
  47.        }  
  48.    }  

音量键和电源键停止响铃

系统按键会先在PhoneWindowManager中处理然后再发送到app窗口去处理,关于铃声的PhoneWindowManager会处理,所以不会发送到app再去处理,这个对要修改这个逻辑的同学来说不大友好,不过一般厂商也不改这些默认动作。
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
[java] view plain copy
  1.     public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {  
  2.         ...  
  3.         switch (keyCode) {  
  4.             case KeyEvent.KEYCODE_VOLUME_DOWN:  
  5.             case KeyEvent.KEYCODE_VOLUME_UP:  
  6.             case KeyEvent.KEYCODE_VOLUME_MUTE: {  
  7.                         ...  
  8.             TelecomManager telecomManager = getTelecommService();  
  9.                                 if (telecomManager != null) {  
  10.                                     if (telecomManager.isRinging()) {                             
  11.                                       telecomManager.silenceRinger();  
  12.   
  13.         ...  
  14. }  
音量键在来电时候按下会调用TelecomManager方法停止响铃。
[java] view plain copy
  1. private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {  
  2.     ...  
  3.     if (telecomManager != null) {  
  4.             if (telecomManager.isRinging()) {  
  5.                 // Pressing Power while there's a ringing incoming  
  6.                 // call should silence the ringer.  
  7.                 telecomManager.silenceRinger();  
  8.             } else if ((mIncallPowerBehavior  
  9.                     & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0  
  10.                     && telecomManager.isInCall() && interactive) {  
  11.                 // Otherwise, if "Power button ends call" is enabled,  
  12.                 // the Power button will hang up any current active call.  
  13.                 hungUp = telecomManager.endCall();  
  14.             }  
  15.         }  
  16.     ...  
  17. }  
电源键处理中如果是来电会停止响铃,如果是通话中而且INCALL_POWER_BUTTON_BEHAVIOR_HANGUP值大于0的话会挂断电话。
原创粉丝点击