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

来源:互联网 发布: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