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音是两个不同频率的音混合,响固定时间。
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的前缀。铃声的播放等操作就是对队列的消息操作,这个不再分析。
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响起提示音
}
}
}
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的话会挂断电话。