写在前面的话
本文主要分析Android 接电话的流程,研究的代码是Android 5.1的,现在我们只关注应用层,以CDMA为例,GSM同理。
一、显示来电的界面
(如果图片看不清的话,可以右键选择在新标签中打开图片,或者把图片另存到自己电脑再查看。)
本文来自 ,转载请务必注明出处。
步骤1,2:在Framework层的最后,是由PhoneBase.java将来电通知传递到应用层的,如果想了解这段流程,请看《Android 5.1 Phone MT(来电)流程分析(Framework层) 》的步骤1~13。
步骤3,4,5: 好了,回到正题,本文主要讲的是应用层的流程。PstnIncomingCallNotifier.java在registerForNotifications()方法里注册监听了EVENT_NEW_RINGING_CONNECTION
事件,因此它会接收到Framework层传递过来的来电通知。在PstnIncomingCallNotifier.java里mHandler的handleMessage()方法有EVENT_NEW_RINGING_CONNECTION
相应的处理。
/**
* Used to listen to events from {@link #mPhoneBase}.
*/
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch(msg.what) {
case EVENT_NEW_RINGING_CONNECTION:
handleNewRingingConnection((AsyncResult) msg.obj);
break;
...
}
}
};
/**
* Verifies the incoming call and triggers sending the incoming-call intent to Telecom.
*
* @param asyncResult The result object from the new ringing event.
*/
private void handleNewRingingConnection(AsyncResult asyncResult) {
Log.d(this, "handleNewRingingConnection");
Connection connection = (Connection) asyncResult.result;
if (connection != null) {
Call call = connection.getCall();
// Final verification of the ringing state before sending the intent to Telecom.
if (call != null && call.getState().isRinging()) {
Phone phone = call.getPhone();
if (phone != null&& isBlockedByFirewall(connection.getAddress(),
phone.getPhoneId())) {
PhoneUtils.hangupRingingCall(call);
sendBlockRecordBroadcast(phone.getPhoneId(), connection.getAddress());
return;
}
sendIncomingCallIntent(connection);
}
}
}
/**
* Sends the incoming call intent to telecom.
*/
private void sendIncomingCallIntent(Connection connection) {
...
TelecomManager.from(mPhoneProxy.getContext()).addNewIncomingCall(
TelecomAccountRegistry.makePstnPhoneAccountHandle(mPhoneProxy), extras);
}
}
步骤6: TelecomManager.java的addNewIncomingCall()方法
/**
* Registers a new incoming call. A {@link ConnectionService} should invoke this method when it
* has an incoming call. The specified {@link PhoneAccountHandle} must have been registered
* with {@link #registerPhoneAccount}. Once invoked, this method will cause the system to bind
* to the {@link ConnectionService} associated with the {@link PhoneAccountHandle} and request
* additional information about the call (See
* {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming call UI.
*
* @param phoneAccount A {@link PhoneAccountHandle} registered with
* {@link #registerPhoneAccount}.
* @param extras A bundle that will be passed through to
* {@link ConnectionService#onCreateIncomingConnection}.
* @hide
*/
@SystemApi
public void addNewIncomingCall(PhoneAccountHandle phoneAccount, Bundle extras) {
try {
if (isServiceConnected()) {
getTelecomService().addNewIncomingCall(
phoneAccount, extras == null ? new Bundle() : extras);
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException adding a new incoming call: " + phoneAccount, e);
}
}
步骤7~10: TelecomService.java的addNewIncomingCall()方法
/**
* @see android.telecom.TelecomManager#addNewIncomingCall
*/
@Override
public void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
Log.i(this, "Adding new incoming call with phoneAccountHandle %s", phoneAccountHandle);
if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null) {
mAppOpsManager.checkPackage(
Binder.getCallingUid(), phoneAccountHandle.getComponentName().getPackageName());
// Make sure it doesn't cross the UserHandle boundary
enforceUserHandleMatchesCaller(phoneAccountHandle);
Intent intent = new Intent(TelecomManager.ACTION_INCOMING_CALL);
intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
intent.putExtra(CallReceiver.KEY_IS_INCOMING_CALL, true);
if (extras != null) {
intent.putExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras);
}
sendRequestAsync(MSG_NEW_INCOMING_CALL, 0, intent);
} else {
Log.w(this, "Null phoneAccountHandle. Ignoring request to add new incoming call");
}
}
private MainThreadRequest sendRequestAsync(int command, int arg1, Object arg) {
MainThreadRequest request = new MainThreadRequest();
request.arg = arg;
mMainThreadHandler.obtainMessage(command, arg1, 0, request).sendToTarget();
return request;
}
(在这里跟Android5.0是不一样的)在sendRequestAsync()方法里调用obtainMessage()方法创建了一个消息类型为MSG_NEW_INCOMING_CALL
的Message,并且通过sendToTarget()发送出去。
步骤11,12: TelecomService.java里MainThreadHandler的handleMessage()方法有对MSG_NEW_INCOMING_CALL
的处理。
case MSG_NEW_INCOMING_CALL:
if (request.arg == null || !(request.arg instanceof Intent)) {
Log.w(this, "Invalid new incoming call request");
break;
}
CallReceiver.processIncomingCallIntent((Intent) request.arg);
break;
调用了CallReceiver.java的processIncomingCallIntent(),进而又调用CallsManager.java的processIncomingCallIntent()方法。
步骤13: CallsManager.java的processIncomingCallIntent()方法
/**
* Starts the process to attach the call to a connection service.
*
* @param phoneAccountHandle The phone account which contains the component name of the
* connection service to use for this call.
* @param extras The optional extras Bundle passed with the intent used for the incoming call.
*/
void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
Log.d(this, "processIncomingCallIntent");
Uri handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER);
Call call = new Call(
mContext,
mConnectionServiceRepository,
handle,
null /* gatewayInfo */,
null /* connectionManagerPhoneAccount */,
phoneAccountHandle,
true /* isIncoming */,
false /* isConference */);
call.setExtras(extras);
// TODO: Move this to be a part of addCall()
call.addListener(this);
call.startCreateConnection(mPhoneAccountRegistrar);
}
在这里创建了一个Call对象,并且把需要的参数传递进来,并且调用Call的startCreateConnection方法。
步骤14,15,16: Call.java的startCreateConnection()方法,这个Call.java是在packages\services\telecomm\src\com\android\server\telecom目录下的。
/**
* Starts the create connection sequence. Upon completion, there should exist an active
* connection through a connection service (or the call will have failed).
*
* @param phoneAccountRegistrar The phone account registrar.
*/
void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
Preconditions.checkState(mCreateConnectionProcessor == null);
mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
phoneAccountRegistrar, mContext);
mCreateConnectionProcessor.process();
}
创建了一个CreateConnectionProcessor对象,先调用它的process()方法,再调用attemptNextPhoneAccount方法,最后调用了ConnectionServiceWrapper.java的createConnection()方法,步骤13中创建的Call对象也就被传递到了这里。
步骤17: ConnectionServiceWrapper.java的createConnection()方法。
/**
* Creates a new connection for a new outgoing call or to attach to an existing incoming call.
*/
void createConnection(final Call call, final CreateConnectionResponse response) {
Log.d(this, "createConnection(%s) via %s.", call, getComponentName());
BindCallback callback = new BindCallback() {
@Override
public void onSuccess() {
String callId = mCallIdMapper.getCallId(call);
mPendingResponses.put(callId, response);
GatewayInfo gatewayInfo = call.getGatewayInfo();
Bundle extras = call.getExtras();
if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null &&
gatewayInfo.getOriginalAddress() != null) {
extras = (Bundle) extras.clone();
extras.putString(
TelecomManager.GATEWAY_PROVIDER_PACKAGE,
gatewayInfo.getGatewayProviderPackageName());
extras.putParcelable(
TelecomManager.GATEWAY_ORIGINAL_ADDRESS,
gatewayInfo.getOriginalAddress());
}
try {
mServiceInterface.createConnection(
call.getConnectionManagerPhoneAccount(),
callId,
new ConnectionRequest(
call.getTargetPhoneAccount(),
call.getHandle(),
extras,
call.getVideoState()),
call.isIncoming(),
call.isUnknown());
} catch (RemoteException e) {
Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
mPendingResponses.remove(callId).handleCreateConnectionFailure(
new DisconnectCause(DisconnectCause.ERROR, e.toString()));
}
}
@Override
public void onFailure() {
Log.e(this, new Exception(), "Failure to call %s", getComponentName());
response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR));
}
};
mBinder.bind(callback);
}
步骤18,19:这里调用了ConnectionServiceWrapper的父类ServiceBinder的bind()方法,先new一个ServiceConnection对象,然后绑定一个远程服务端服务。如果绑定成功的话,在ServiceBinder的内部类ServiceBinderConnection的onServiceConnected()方法就被调用。
在这里做了两件事:
1、步骤20,21:通过setBinder()方法,回调ConnectionServiceWrapper的setServiceInterface()方法,通过mServiceInterface = IConnectionService.Stub.asInterface(binder);
这行代码获取一个远程服务端的对象mServiceInterface 。
2、步骤22,23:再通过调用handleSuccessfulConnection()方法回调callback 的onSuccess()方法,也就又回到ConnectionServiceWrapper的createConnection()方法里。
步骤24,25:最后通过这一行mServiceInterface.createConnection();
,调用ConnectionService.java里mBinder的createConnection()方法。
private final IBinder mBinder = new IConnectionService.Stub() {
...
@Override
public void createConnection(PhoneAccountHandle connectionManagerPhoneAccount,
String id,ConnectionRequest request,
boolean isIncoming,boolean isUnknown) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionManagerPhoneAccount;
args.arg2 = id;
args.arg3 = request;
args.argi1 = isIncoming ? 1 : 0;
args.argi2 = isUnknown ? 1 : 0;
mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget();
}
...
}
步骤26,27:在这里通过obtainMessage()方法创建了一个消息类型为MSG_CREATE_CONNECTION
的Message,再把传进来的参数封装到Message里再发送出去,然后在ConnectionService.java里mHandler的handleMessage()方法里处理这个Message,最后就调用了ConnectionService.java的createConnection()方法。
步骤28: ConnectionService.java的createConnection()方法。
/**
* This can be used by telecom to either create a new outgoing call or attach to an existing
* incoming call. In either case, telecom will cycle through a set of services and call
* createConnection util a connection service cancels the process or completes it successfully.
*/
private void createConnection(
final PhoneAccountHandle callManagerAccount,
final String callId,
final ConnectionRequest request,
boolean isIncoming,
boolean isUnknown) {
Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
"isIncoming: %b, isUnknown: %b", callManagerAccount, callId, request, isIncoming,
isUnknown);
Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
: isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
: onCreateOutgoingConnection(callManagerAccount, request);
Log.d(this, "createConnection, connection: %s", connection);
if (connection == null) {
connection = Connection.createFailedConnection(
new DisconnectCause(DisconnectCause.ERROR));
}
if (connection.getState() != Connection.STATE_DISCONNECTED) {
addConnection(callId, connection);
}
Uri address = connection.getAddress();
String number = address == null ? "null" : address.getSchemeSpecificPart();
Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s, properties: 0x%x",
Connection.toLogSafePhoneNumber(number),
Connection.stateToString(connection.getState()),
Connection.capabilitiesToString(connection.getConnectionCapabilities()),
connection.getCallProperties());
Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);
mAdapter.handleCreateConnectionComplete(
callId,
request,
new ParcelableConnection(
getAccountHandle(request, connection),
connection.getState(),
connection.getConnectionCapabilities(),
connection.getCallProperties(),
connection.getAddress(),
connection.getAddressPresentation(),
connection.getCallerDisplayName(),
connection.getCallerDisplayNamePresentation(),
connection.getVideoProvider() == null ?
null : connection.getVideoProvider().getInterface(),
connection.getVideoState(),
connection.isRingbackRequested(),
connection.getAudioModeIsVoip(),
connection.getStatusHints(),
connection.getDisconnectCause(),
createIdList(connection.getConferenceables()),
connection.getCallSubstate()));
if (isUnknown) {
triggerConferenceRecalculate();
}
}
步骤29: onCreateIncomingConnection()方法会被调用到,这个方法被TelephonyConnectionService重写,TelephonyConnectionService是ConnectionService的实例,所以会调用TelephonyConnectionService.java的onCreateIncomingConnection()方法来创建一个CDMAConnection对象。
步骤30:创建CDMAConnection对象之后,就调用ConnectionServiceAdapter.java的handleCreateConnectionComplete()来处理之后的事情,比如启动UI界面之类。
步骤31~39:流程一直走,这一段也没什么好说的了。
步骤40: CallsManager.java的onSuccessfulIncomingCall()方法。
在这里做了两件事:
1、把Call的状态从NEW
改成RINGING
。
2、把Call对象添加到Call的集合里。(步骤41)
步骤42~63: 启动UI界面,有两种方式,弹出一个小窗口显示来电,或者全屏显示来电。
二、接听电话
也没什么好说的,时序图已经写得比较明白,在步骤18之后,就紧接着《Android 5.1 Phone MT(来电)流程分析(Framework层) 》的步骤21。等真正接通电话之后,就把Call的状态从RINGING改成ACTIVE,Call的状态改变之后,就会停止响铃,对应《Android 5.1 Phone MT(来电)流程分析(Framework层) 》的步骤20。
如果想继续了解Framework层的流程,请看《Android 5.1 Phone MT(来电)流程分析(Framework层) 》。