- --------
- 写在前:本人新手有理解不准确或者错误的地方请大家指正出来,我好及时修改
- -----
- ---我理解的来电流程大概分为6个部分--
- 注:1>2>3并不是只步骤,是指经过的第1>2>3个类。
① 1>2>3 RIL>GSMPhone
状态变化>发出来电通知
② 4>5>6>7>8>9 PstnIncomingCallNotifier>Call
接收到通知>准备创建连接
③ 10>11>12>13CreateConnectionProcesser> ConnectionServices
开始创建连接>创建连接完成。
④ 14>11>9>>8 ConnectionServicesAdapter>CallsManager
处理这个创建的
连接>成功来电
⑤ 8>15>16>17 CallsManager>Phone
成功来电>准备启动界面
⑥ 18>19>20>21>22 CallList>StatubarNotifier
开始启动界面 显示来电
----
framwork
① RIL>GSMPhone Call状态变化 -> 发出来电通知
- 0. Modem发出Call状态变化的通知,
1. framwork/opt/telephony/.../RIL.java
1.1.RIL接收到RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED消息
1.2.然后经由mCallStateRegistrants.notifyRegistrants发出通知(RegistrantList消息处理机制此处不做具体说明)。-
private voidprocessUnsolicited (Parcel p) {...switch(response) {...case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:if (RILJ_LOGD) unsljLog(response);mCallStateRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
BaseCommands.java registerForCallStateChanged() mCallStateRegistrants.add(r);
注册为观察者(android源码中大量用到观察者模式,或者叫RegistrantList消息处理机制)。
@Overridepublic void registerForCallStateChanged(Handler h, int what, Object obj) {Registrant r = new Registrant (h, what, obj);- //添加到观察者列表
mCallStateRegistrants.add(r);}
2. framwork/opt/telephony/...GSMCallTracker.java
2.1查找察者被调用的地方(AS中的快捷键Ctrl+Alt+H), 两处被响应处理处理,其中一处:GSMCallTracker handleMessage
...//registerForCallStateChanged调用mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);...@Overridepublic void//响应处理handleMessage (Message msg) {...case EVENT_CALL_STATE_CHANGE://调用父类CallTracker查询Call List方法- pollCallsWhenSafe();
- break;
2.1.1.
pollCallsWhenSafe()方法在CallTracker.java中实现
- protected void pollCallsWhenSafe() {
mNeedsPoll = true;if (checkNoOperationsPending()) {mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);mCi.getCurrentCalls(mLastRelevantPoll);//RIL.java中的getCurrentCalls方法}}
2.1.2 回到RIL.java
getCurrentCalls 将
RIL_REQUEST_GET_CURRENT_CALLS 消息封装成
RILRequest
类型并发送。
此处
result为
EVENT_POLL_CALLS_RESULT,所以后
面
GSMCallTracker 中
hand
lemessage响应的是这个消息。
@Overridepublic voidgetCurrentCalls (Message result) {RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_CURRENT_CALLS, result);if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));send(rr);}
2.2 RIL.java 有三处接收处理
RIL_REQUEST_GET_CURRENT_CALLS
消息,真正的逻辑处理在processSolicited方法
private RILRequest processSolicited (Parcel p) {- ...
case RIL_REQUEST_GET_CURRENT_CALLS: ret = responseCallList(p); break;- ...
if (rr.mResult != null) {AsyncResult.forMessage(rr.mResult, null, tr);rr.mResult.sendToTarget();//发出handler消息通知}
2.3 回到framworks/opt/telephony/.../telephony/gsm/GSMCallTracker.java
rr
.
mResult
.
sendToTarget
()发出handler消息通知后,
handleMessage方法中响应消息“ EVENT_POLL_CALLS_RESULT",这个消息是在
pollCallsWhenSafe中封装的。
@Overridepublic voidhandleMessage (Message msg) {...case EVENT_POLL_CALLS_RESULT:ar = (AsyncResult)msg.obj;if (msg == mLastRelevantPoll) {if (DBG_POLL) log("handle EVENT_POLL_CALL_RESULT: set needsPoll=F");mNeedsPoll = false;mLastRelevantPoll = null;handlePollCalls((AsyncResult)msg.obj);}break;
handlePollCalls方法根据RIL发出的Call List对象判断Call的状态,并发出不同的通知,
1) 新来电的通知是: phone.
notifyNewRingingConnection;
handlePollCalls(){- ...
if (newRinging != null) {mPhone.notifyNewRingingConnection(newRinging);}
另外两个是
2) 通话断开通知 onDisconnected;
3) Call状态变化通知 phone.notifiyPreciseCallStateChanged.
来电的时候发出的是phone.notifyNewRingConnection通知,进入到notifyNewRingConnection方法
3. framworks/opt/telephony/.../telephony/gsm/GSMPhone.java
public void notifyNewRingingConnection(Connection c) {super.notifyNewRingingConnectionP(c);}
调用父类
PhoneBase.java
notifyNewRingingConnectionP()
发出来电通知 mNewRingingConnectionRegistrants.notifyRegistrants(ar);
/*** Notify registrants of a new ringing Connection.* Subclasses of Phone probably want to replace this with a* version scoped to their packages*/public void notifyNewRingingConnectionP(Connection cn) {if (!mIsVoiceCapable)return;AsyncResult ar = new AsyncResult(null, cn, null);mNewRingingConnectionRegistrants.notifyRegistrants(ar);}
RegistrantList.java
public /*synchronized*/ voidnotifyRegistrants(AsyncResult ar){internalNotifyRegistrants(ar.result, ar.exception);}
private synchronized voidinternalNotifyRegistrants (Object result, Throwable exception){for (int i = 0, s = registrants.size(); i < s ; i++) {Registrant r = (Registrant) registrants.get(i);r.internalNotifyRegistrant(result, exception);}}
/*package*/ voidinternalNotifyRegistrant (Object result, Throwable exception){Handler h = getHandler();if (h == null) {clear();} else {Message msg = Message.obtain();msg.what = what;msg.obj = new AsyncResult(userObj, result, exception);h.sendMessage(msg);}}
注册为观察者的方法为:
// Inherited documentation suffices.@Overridepublic void registerForNewRingingConnection(Handler h, int what, Object obj) {checkCorrectThread(h);mNewRingingConnectionRegistrants.addUnique(h, what, obj);}
registerForNewRingingConnection这个方法在4个地方被调用,即有4个地方关心是否有新来电的变化。
1.packages/servicesTelephony/.../PstnIncomingCallNotifier.java log:
D/Telephony( 1396): PstnIncomingCallNotifier: handleNewRingingConnection
2.framworks/opt/telephony/.../PhoneProxy.java
3.framworks/opt/telephony/test/.../GSMPhoneTEST.java
4.framworks/opt/telephony/.../telephony/CallManager.java 5.0之前是这里处理,新版本由PstnIncomingCallNotifier处理
----------------
前面:RIL发出Call状态变化消息通知,GSMPhone发出来电通知
----------------
Telephony
② PstnIncomingCallNotifier>Call 接收Framework层到通知>准备创建连接
4. packages/services/Telephony/.../PstnIncomingCallNotifier.java
registerForNotifications方法调用
registerForNewRingingConnection
4.1 调用
Phonebase中
不同的方法,注册为观察者。
翻译(不通顺 =_=):
我们应当直接跟phoneProxy做交互处理。然而phoneProxy直接与CallManager交互处理,
我们要么监听callmanager,要么就像这样参与到proxy中去。
两种都是不可取的,如果这个类和callmanager能够
register generically with the phone proxy instead
,这会比较好。
或者更好的是只是直接注册通知
with phone proxy,
而不用担心技术的改变,这需要改变opt/telephony中的代码。
/**
* Register for notifications from the base phone.
* TODO: We should only need to interact with the phoneproxy directly. However,
* since the phoneproxy only interacts directly with CallManager we either listen to callmanager
* or we have to poke into the proxy like this. Neither is desirable. It would be better if
* this class and callManager could register generically with the phone proxy instead and get
* radio techonology changes directly. Or better yet, just register for the notifications
* directly with phone proxy and never worry about the technology changes. This requires a
* change in opt/telephony code.
*/
private void registerForNotifications() {
Phone newPhone = mPhoneProxy.getActivePhone();
if (newPhone != mPhoneBase) {
unregisterForNotifications();
if (newPhone != null) {
Log.i(this, "Registering: %s", newPhone);
mPhoneBase = newPhone;
//调用
registerForNewRingingConnection方法
mPhoneBase.registerForNewRingingConnection(
mHandler, EVENT_NEW_RINGING_CONNECTION, null);
mPhoneBase.registerForCallWaiting(
mHandler, EVENT_CDMA_CALL_WAITING, null);
mPhoneBase.registerForUnknownConnection(mHandler, EVENT_UNKNOWN_CONNECTION,
null);
}
}
}
4.2 handle 处理EVENT_NEW_RINGING_CONNECTION消息
private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) {
...
case
EVENT_NEW_RINGING_CONNECTION
:
handleNewRingingConnection((AsyncResult) msg.obj);
break;
4.2.1 handleNewRingingConnection方法,处理新的来电连接。
此处对应的log为:
D/Telephony( 1396): PstnIncomingCallNotifier:
handleNewRingingConnection
/**
* 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.
//在发送intent到Telecom之前最后一次验证ringing 状态
if (call != null && call.getState().isRinging()) {
sendIncomingCallIntent(connection);
}
}
}
4.2.2 sendIncomingCallIntent方法
发送incoming call intent到telecom,发送的Connection 类型,里面包括isIncoming getState isRinging等
/**
* Sends the incoming call intent to telecom.
*/
private void sendIncomingCallIntent(Connection connection) {
Bundle extras = null;
if (connection.getNumberPresentation() == TelecomManager.PRESENTATION_ALLOWED &&
!TextUtils.isEmpty(connection.getAddress())) {
extras = new Bundle();
Uri uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, connection.getAddress(), null);
extras.putParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER, uri);
}
TelecomManager.from(mPhoneProxy.getContext()).addNewIncomingCall(
TelecomAccountRegistry.makePstnPhoneAccountHandle(mPhoneProxy), extras);
}
addNewIncomingCall()定义在:
framworks/base/
telecomm
/java/android/telecom/
TelecomManager.java
TelecomManager的功能则主要是对TelecomService提供的远程接口的封装,然后提供给应用使用。
addNewIncomingCall方法
@SystemApi
来电时触发此方法
/**
* 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);
}
}
addNewIncomingCall 的具体实现
Telecomm
5. packages/services/Telecomm/src/com/android/server/telecom/TelecomServiceImpl.java
继承自ITelecomService,TelecomService的接口由TeleComManager封装,并其供给应用使用,
5.1
@Override
addNewIncomingCall
新建intent 设定intent 的ACTION 、addFalgs等
- public static final String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
- ...
- /**
* @see android.telecom.TelecomManager#addNewIncomingCall*/@Overridepublic void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null) {mAppOpsManager.checkPackage(Binder.getCallingUid(), phoneAccountHandle.getComponentName().getPackageName());Intent intent = new Intent(TelecomManager.ACTION_INCOMING_CALL);intent.setPackage(mContext.getPackageName());intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);if (extras != null) {intent.putExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras);}long token = Binder.clearCallingIdentity();//启动ActivitymContext.startActivityAsUser(intent, UserHandle.CURRENT);Binder.restoreCallingIdentity(token);}}
//启动
CallActivity
对应的log:
I/ActivityManager( 852): START u0 {act=android.telecom.action.INCOMING_CALL flg=0x10000000 pkg=com.android.server.telecom cmp=com.android.server.telecom/.IncomingCallActivity (has extras)
} from uid 1001 on display 0
183<activity-alias android:name="IncomingCallActivity"184 android:targetActivity="CallActivity"185 android:exported="true"> 186 <intent-filter> 187 <action android:name="android.telecom.action.INCOMING_CALL" /> 188 <category android:name="android.intent.category.DEFAULT" /> 189 </intent-filter> 190 </activity-alias>
6. packages/services/Telecomm/src/com/android/server/telecom/
CallActivity.java
6.1 执行完processIntent()后就finish(),对应的log
D/Telecom ( 1376): CallActivity:
onCreate: end
public class CallActivity extends Activity {
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
// TODO: Figure out if there is something to restore from bundle.
// See OutgoingCallBroadcaster in services/Telephony for more.
processIntent(getIntent());
// This activity does not have associated UI, so close.
finish();
Log.d(this, "onCreate: end");
}
6.1.1
processIntent
判断action
/**
* Processes intents sent to the activity.
*
* @param intent The intent.
*/
private void processIntent(Intent intent) {
// Ensure call intents are not processed on devices that are not capable of calling.
if (!isVoiceCapable()) {
return;
}
verifyCallAction(intent);
String action = intent.getAction();
if (Intent.ACTION_CALL.equals(action) ||
Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
Intent.ACTION_CALL_EMERGENCY.equals(action)) {
processOutgoingCallIntent(intent);
} else if (TelecomManager.ACTION_INCOMING_CALL.equals(action)) {
processIncomingCallIntent(intent);
}
}
6.1.2
processIncomingCallIntent
private void processIncomingCallIntent(Intent intent) {
if (UserHandle.myUserId() == UserHandle.USER_OWNER) {
CallReceiver
.
processIncomingCallIntent
(
intent
);
} else {
sendBroadcastToReceiver(intent, true /* isIncoming */);
}
}
7. packages/services/Telecomm/src/com/android/server/telecom/CallReceiver.java
7.1 对应log
D/Telecom ( 1376): com.android.server.telecom.CallReceiver: Processing incoming call from connection service [ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}]
static void processIncomingCallIntent(Intent intent) {
PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
if (phoneAccountHandle == null) {
Log.w(TAG, "Rejecting incoming call due to null phone account");
return;
}
if (phoneAccountHandle.getComponentName() == null) {
Log.w(TAG, "Rejecting incoming call due to null component name");
return;
}
Bundle clientExtras = null;
if (intent.hasExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS)) {
clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS);
}
if (clientExtras == null) {
clientExtras = Bundle.EMPTY;
}
Log.d(TAG, "Processing incoming call from connection service [%s]",
phoneAccountHandle.getComponentName());
getCallsManager().processIncomingCallIntent(phoneAccountHandle, clientExtras);
}
8. packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java
8.1
processIncomingCallIntent
D/Telecom ( 1376): CallsManager: processIncomingCallIntent
new一个Call 对象 把前面的参数传进来,然后调用call中建立连接的方法
startCreateConnection
/**
* 开始把call attach到connection services
*
* @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.
*/
/*** 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);}
④ 14>11>9>>8 ConnectionServicesAdapter>CallsManager
处理这个创建的连接>成功来电
⑤ 8>15>16>17 CallsManager>Phone
成功来电>准备启动界面
⑥ 18>19>20>21>22 CallList>StatubarNotifier
开始启动界面 显示来电
9. packages/services/Telecomm/src/com/android/server/telecom/Call.java
9.1 startCreateConnection()
开始建立连接队列,一旦完成创建,就应当有一个活动active的连接了存在service里。
/*** 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();}
③CreateConnectionProcesser>ConnectionServices 开始创建连接>创建连接完成。
10. packages/services/Telecomm/src/com/android/server/telecom/CreateConnectionProcessor.java
10.1 process
void process() {Log.v(this, "process");mAttemptRecords = new ArrayList<>();if (mCall.getTargetPhoneAccount() != null) {mAttemptRecords.add(new CallAttemptRecord(mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));}adjustAttemptsForConnectionManager();adjustAttemptsForEmergency();mAttemptRecordIterator = mAttemptRecords.iterator();attemptNextPhoneAccount();}
10.2 attemptNextPhoneAccount()
service试图建立连接
private void attemptNextPhoneAccount() {...if (mResponse != null && attempt != null) {Log.i(this, "Trying attempt %s", attempt);ConnectionServiceWrapper service =mRepository.getService(attempt.connectionManagerPhoneAccount.getComponentName());if (service == null) {Log.i(this, "Found no connection service for attempt %s", attempt);attemptNextPhoneAccount();} else {mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);mCall.setConnectionService(service);Log.i(this, "Attempting to call from %s", service.getComponentName());service.createConnection(mCall, new Response(service));}}
11.
packages/services/Telecomm/src/com/android/server/telecom/
ConnectionServiceWrapper.java
11.1 createConnection()
BindCallback 是interface
/**
* 为播出的电话建立连接,或者attach一个已经存在的来电。
*/
/*** 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() {@Overridepublic 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()));}}@Overridepublic void onFailure() {Log.e(this, new Exception(), "Failure to call %s", getComponentName());response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR));}};mBinder.bind(callback);}
12. packages/services/Telecomm/src/com/android/server/telecom/Servicesbinder.java
12.1
bind
绑定连接
/**
* 执行绑定到服务的操作(如果还没有绑定)然后执行指定的回调方法
*
* @param callback The 回调方法通知绑定是成功或失败
*/
/*** Helper class to perform on-demand binding.*/final class Binder {/*** Performs an bind to the service (only if not already bound) and executes the* specified callback.** @param callback The callback to notify of the binding's success or failure.*/void bind(BindCallback callback) {ThreadUtil.checkOnMainThread();Log.d(ServiceBinder.this, "bind()");// Reset any abort request if we're asked to bind again.clearAbort();if (!mCallbacks.isEmpty()) {// Binding already in progress, append to the list of callbacks and bail out.mCallbacks.add(callback);return;}mCallbacks.add(callback);if (mServiceConnection == null) {Intent serviceIntent = new Intent(mServiceAction).setComponent(mComponentName);ServiceConnection connection = new ServiceBinderConnection();Log.d(ServiceBinder.this, "Binding to service with intent: %s", serviceIntent);- if (!mContext.bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE)) {
handleFailedConnection();return;}} else {Log.d(ServiceBinder.this, "Service is already bound.");Preconditions.checkNotNull(mBinder);handleSuccessfulConnection();}}}- //
上面的执行完之后,顺序执行到
onServiceConnected
12.2
onServiceConnected
private final class ServiceBinderConnection implements ServiceConnection {@Overridepublic void onServiceConnected(ComponentName componentName, IBinder binder) {ThreadUtil.checkOnMainThread();Log.i(this, "Service bound %s", componentName);//这句log被打印出来了// Unbind request was queued so unbind immediately.if (mIsBindingAborted) {clearAbort();logServiceDisconnected("onServiceConnected");mContext.unbindService(this);handleFailedConnection();return;}mServiceConnection = this;setBinder(binder);handleSuccessfulConnection();}
handleSuccessfulConnection
private void handleSuccessfulConnection() {for (BindCallback callback : mCallbacks) {callback.onSuccess();}mCallbacks.clear();}
回调上面的onSuccess() 执行
mServiceInterface
.
createConnection
createConnection的具体实现在
13. framworks/base/telecomm/java/android/telecom/ConnectionServices.java
@Overridepublic void createConnection(PhoneAccountHandle connectionManagerPhoneAccount,String id,ConnectionRequest request,boolean isIncoming,boolean isUnknown) {//chengzhiSomeArgs 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();}
13.1 handleMessage()
MSG_CREATE_CONNECTION
case MSG_CREATE_CONNECTION: {SomeArgs args = (SomeArgs) msg.obj;try {//chengzhifinal PhoneAccountHandle connectionManagerPhoneAccount =(PhoneAccountHandle) args.arg1;final String id = (String) args.arg2;final ConnectionRequest request = (ConnectionRequest) args.arg3;final boolean isIncoming = args.argi1 == 1;final boolean isUnknown = args.argi2 == 1;if (!mAreAccountsInitialized) {Log.d(this, "Enqueueing pre-init request %s", id);mPreInitializationConnectionRequests.add(new Runnable() {@Overridepublic void run() {createConnection(connectionManagerPhoneAccount,id,request,isIncoming,isUnknown);}});} else {//chengzhi debugcreateConnection(connectionManagerPhoneAccount,id,request,isIncoming,isUnknown);}} finally {args.recycle();}break;}
13.1.1 createConnection方法
创建
Connection
connection
/**
* 这个方法可以被telecom用来创建呼出电话或者一个已存在的来电。任何一种情况,telecom都会循环经过一系列的服务和 调用
createConnection util a connection service取消或者成功完成创建。
*/
/*** 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);//chengzhi 01Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request): isIncoming ? onCreateIncomingConnection(callManagerAccount, request): onCreateOutgoingConnection(callManagerAccount, request);.....mAdapter.handleCreateConnectionComplete
前面建立连接成功了,后面处理成功的连接
后面执行
mAdapter.handleCreateConnectionComplete
④ 14>11>9>>8 ConnectionServicesAdapter>CallsManager 处理这个创建的连接>成功来电
14.framework/base/telecomm/java/android/telecom/ConnectionServicesAdapter.java
void handleCreateConnectionComplete(String id,ConnectionRequest request,ParcelableConnection connection) {for (IConnectionServiceAdapter adapter : mAdapters) {try {//chengzhi 03adapter.handleCreateConnectionComplete(id, request, connection);} catch (RemoteException e) {}}}
⑤ CallsManager>Phone 成功来电>准备启动界面
15. //packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java
handleCreateConnectionComplete
private final class Adapter extends IConnectionServiceAdapter.Stub {@Overridepublic void handleCreateConnectionComplete(String callId,ConnectionRequest request,ParcelableConnection connection) {logIncoming("handleCreateConnectionComplete %s", request);if (mCallIdMapper.isValidCallId(callId)) {SomeArgs args = SomeArgs.obtain();args.arg1 = callId;args.arg2 = request;args.arg3 = connection;mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_COMPLETE, args).sendToTarget();}}
15.2 handleMessage处理消息
MSG_HANDLE_CREATE_CONNECTION_COMPLETE
private final Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {Call call;switch (msg.what) {case MSG_HANDLE_CREATE_CONNECTION_COMPLETE: {SomeArgs args = (SomeArgs) msg.obj;try {String callId = (String) args.arg1;ConnectionRequest request = (ConnectionRequest) args.arg2;ParcelableConnection connection = (ParcelableConnection) args.arg3;handleCreateConnectionComplete(callId, request, connection);} finally {args.recycle();}break;}
15.2.1 handleCreateConnectionComplete
()
如果成功连接
private void handleCreateConnectionComplete(String callId,ConnectionRequest request,ParcelableConnection connection) {// TODO: Note we are not using parameter "request", which is a side effect of our tacit// assumption that we have at most one outgoing connection attempt per ConnectionService.// This may not continue to be the case.if (connection.getState() == Connection.STATE_DISCONNECTED) {// A connection that begins in the DISCONNECTED state is an indication of// failure to connect; we handle all failures uniformlyremoveCall(callId, connection.getDisconnectCause());} else {// Successful connectionif (mPendingResponses.containsKey(callId)) {mPendingResponses.remove(callId).handleCreateConnectionSuccess(mCallIdMapper, connection);}}}
重写
handleCreateConnectionSuccess方法
16. //packages/services/Telecomm/src/com/android/server/telecom/
Call.java
16.1 handleCreateConnetionSucess()
@Override
public void handleCreateConnectionSuccess(
CallIdMapper idMapper,
ParcelableConnection connection) {
Log.v(this, "handleCreateConnectionSuccessful %s", connection);
mCreateConnectionProcessor = null;
setTargetPhoneAccount(connection.getPhoneAccount());
setHandle(connection.getHandle(), connection.getHandlePresentation());
setCallerDisplayName(
connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation());
setCallCapabilities(connection.getCapabilities());
setVideoProvider(connection.getVideoProvider());
setVideoState(connection.getVideoState());
setRingbackRequested(connection.isRingbackRequested());
setIsVoipAudioMode(connection.getIsVoipAudioMode());
setStatusHints(connection.getStatusHints());
mConferenceableCalls.clear();
for (String id : connection.getConferenceableConnectionIds()) {
mConferenceableCalls.add(idMapper.getCall(id));
}
if (mIsUnknown) {
for (Listener l : mListeners) {
l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection.getState()));
}
} else if (mIsIncoming) {
// We do not handle incoming calls immediately when they are verified by the connection
// service. We allow the caller-info-query code to execute first so that we can read the
// direct-to-voicemail property before deciding if we want to show the incoming call to
// the user or if we want to reject the call.
mDirectToVoicemailQueryPending = true;
// Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
// showing the user the incoming call screen.
mHandler.postDelayed(mDirectToVoicemailRunnable, Timeouts.getDirectToVoicemailMillis(
mContext.getContentResolver()));
} else {
for (Listener l : mListeners) {
l.onSuccessfulOutgoingCall(this,
getStateFromConnectionState(connection.getState()));
}
}
}
16.2 Runnable mDirectToVoicemailRunnable
private final Runnable mDirectToVoicemailRunnable = new Runnable() {@Overridepublic void run() {processDirectToVoicemail();}
16.2.1
processDirectToVoicemail
final class Call implements CreateConnectionResponse {/*** Listener for events on the call.*/interface Listener {void onSuccessfulIncomingCall(Call call);...private void processDirectToVoicemail() {if (mDirectToVoicemailQueryPending) {if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {Log.i(this, "Directing call to voicemail: %s.", this);// TODO: Once we move State handling from CallsManager to Call, we// will not need to set STATE_RINGING state prior to calling reject.setState(CallState.RINGING);reject(false, null);} else {// TODO: Make this class (not CallsManager) responsible for changing// the call state to STATE_RINGING.// TODO: Replace this with state transition to STATE_RINGING.for (Listener l : mListeners) {l.onSuccessfulIncomingCall(this);}}mDirectToVoicemailQueryPending = false;}}
17. //package/services/Telecomm/src/com/android/server/telecom/CallsManager.java
17.1
@Override
onSuccessfulIncomingCall
if 判断后 addCall()
public final class CallsManager extends Call.ListenerBase {...@Overridepublic void onSuccessfulIncomingCall(Call incomingCall) {Log.d(this, "onSuccessfulIncomingCall");setCallState(incomingCall, CallState.RINGING);if (hasMaximumRingingCalls(incomingCall.getTargetPhoneAccount().getId())) {incomingCall.reject(false, null);// since the call was not added to the list of calls, we have to call the missed// call notifier and the call logger manually.mMissedCallNotifier.showMissedCallNotification(incomingCall);mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE);} else {incomingCall.mIsActiveSub = true;addCall(incomingCall);setActiveSubscription(incomingCall.getTargetPhoneAccount().getId());}}
17.1.1 addCall()
/*** Adds the specified call to the main list of live calls.** @param call The call to add.*/private void addCall(Call call) {Log.v(this, "addCall(%s)", call);call.addListener(this);mCalls.add(call);// TODO: Update mForegroundCall prior to invoking// onCallAdded for calls which immediately take the foreground (like the first call).for (CallsManagerListener listener : mListeners) {listener.onCallAdded(call);}updateForegroundCall();}
InCallUI
⑥ CallList>StatubarNotifier 开始启动界面 显示来电
18. //package/services/Telecomm/src/com/android/server/telecom/
InCallController.java
18.1 重写onCallAdded
@Override onCallAdded()
@Overridepublic void onCallAdded(Call call) {if (mInCallServices.isEmpty()) {bind();//执行这里} else {Log.i(this, "onCallAdded: %s", call);//输出log// Track the call if we don't already know about it.addCall(call);for (Map.Entry<ComponentName, IInCallService> entry : mInCallServices.entrySet()) {ComponentName componentName = entry.getKey();IInCallService inCallService = entry.getValue();ParcelableCall parcelableCall = toParcelableCall(call,componentName.equals(mInCallComponentName) /* includeVideoProvider */);try {inCallService.addCall(parcelableCall);} catch (RemoteException ignored) {}}}}
18.1.1 bind()
InCallServiceConnection inCallServiceConnection = new InCallServiceConnection();
/*** Binds to the in-call app if not already connected by binding directly to the saved* component name of the {@link IInCallService} implementation.*/private void bind() {ThreadUtil.checkOnMainThread();if (mInCallServices.isEmpty()) {PackageManager packageManager = mContext.getPackageManager();Intent serviceIntent = new Intent(InCallService.SERVICE_INTERFACE);for (ResolveInfo entry : packageManager.queryIntentServices(serviceIntent, 0)) {ServiceInfo serviceInfo = entry.serviceInfo;if (serviceInfo != null) {boolean hasServiceBindPermission = serviceInfo.permission != null &&serviceInfo.permission.equals(Manifest.permission.BIND_INCALL_SERVICE);boolean hasControlInCallPermission = packageManager.checkPermission(Manifest.permission.CONTROL_INCALL_EXPERIENCE,serviceInfo.packageName) == PackageManager.PERMISSION_GRANTED;if (!hasServiceBindPermission) {Log.w(this, "InCallService does not have BIND_INCALL_SERVICE permission: " +serviceInfo.packageName);continue;}if (!hasControlInCallPermission) {Log.w(this,"InCall UI does not have CONTROL_INCALL_EXPERIENCE permission: " +serviceInfo.packageName);continue;}InCallServiceConnection inCallServiceConnection = new InCallServiceConnection();ComponentName componentName = new ComponentName(serviceInfo.packageName,serviceInfo.name);Log.i(this, "Attempting to bind to InCall %s, is dupe? %b ", //log输出serviceInfo.packageName,mServiceConnections.containsKey(componentName));if (!mServiceConnections.containsKey(componentName)) {Intent intent = new Intent(InCallService.SERVICE_INTERFACE);intent.setComponent(componentName);if (mContext.bindServiceAsUser(intent, inCallServiceConnection,Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {mServiceConnections.put(componentName, inCallServiceConnection);}}}}}}
18.1.2 InCallServiceConnection
inCallServiceConnection
=
new
InCallServiceConnection
();
InCallServiceConnection
/*** Used to bind to the in-call app and triggers the start of communication between* this class and in-call app.*/private class InCallServiceConnection implements ServiceConnection {/** {@inheritDoc} */@Override public void onServiceConnected(ComponentName name, IBinder service) {Log.d(this, "onServiceConnected: %s", name);onConnected(name, service);}/** {@inheritDoc} */@Override public void onServiceDisconnected(ComponentName name) {Log.d(this, "onDisconnected: %s", name);onDisconnected(name);}}
18.1.3 onConnected
/*** Persists the {@link IInCallService} instance and starts the communication between* this class and in-call app by sending the first update to in-call app. This method is* called after a successful binding connection is established.** @param componentName The service {@link ComponentName}.* @param service The {@link IInCallService} implementation.*/private void onConnected(ComponentName componentName, IBinder service) {ThreadUtil.checkOnMainThread();Log.i(this, "onConnected to %s", componentName);IInCallService inCallService = IInCallService.Stub.asInterface(service);try {inCallService.setInCallAdapter(new InCallAdapter(CallsManager.getInstance(),mCallIdMapper));mInCallServices.put(componentName, inCallService);} catch (RemoteException e) {Log.e(this, e, "Failed to set the in-call adapter.");return;}// Upon successful connection, send the state of the world to the service.ImmutableCollection<Call> calls = CallsManager.getInstance().getCalls();if (!calls.isEmpty()) {Log.i(this, "Adding %s calls to InCallService after onConnected: %s", calls.size(),componentName);for (Call call : calls) {try {// Track the call if we don't already know about it.Log.i(this, "addCall after binding: %s", call);addCall(call);inCallService.addCall(toParcelableCall(call,componentName.equals(mInCallComponentName) /* includeVideoProvider */));} catch (RemoteException ignored) {}}onAudioStateChanged(null, CallsManager.getInstance().getAudioState());} else {unbind();}}
addCall(call)
19. //framworks/base/telecomm/java/android/telecom/InCallService.java
@Override
addCall()
/** Manages the binder calls so that the implementor does not need to deal with it. */private final class InCallServiceBinder extends IInCallService.Stub {@Overridepublic void setInCallAdapter(IInCallAdapter inCallAdapter) {mHandler.obtainMessage(MSG_SET_IN_CALL_ADAPTER, inCallAdapter).sendToTarget();}@Overridepublic void addCall(ParcelableCall call) {mHandler.obtainMessage(MSG_ADD_CALL, call).sendToTarget();}
19.1 handleMessage 处理消息 MSG_ADD_CALL
/** Default Handler used to consolidate binder method calls onto a single thread. */private final Handler mHandler = new Handler(Looper.getMainLooper()) {@Overridepublic void handleMessage(Message msg) {if (mPhone == null && msg.what != MSG_SET_IN_CALL_ADAPTER) {return;}switch (msg.what) {case MSG_SET_IN_CALL_ADAPTER:mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj));onPhoneCreated(mPhone);break;case MSG_ADD_CALL:mPhone.internalAddCall((ParcelableCall) msg.obj);break;
21. //framworks/base/telecomm/java/android/telecom/Phone.java
internalAddCall()
final void internalAddCall(ParcelableCall parcelableCall) {Call call = new Call(this, parcelableCall.getId(), mInCallAdapter,parcelableCall.mIsActiveSub);mCallByTelecomCallId.put(parcelableCall.getId(), call);mCalls.add(call);checkCallTree(parcelableCall);call.internalUpdate(parcelableCall, mCallByTelecomCallId);fireCallAdded(call);}
20.1.1 fireCallAdded()
private void fireCallAdded(Call call) {for (Listener listener : mListeners) {listener.onCallAdded(this, call);}}
onCallAdded()
@SystemApi系统Api 其他使用的地方会 @Override
@SystemApipublic final class Phone { public abstract static class Listener {...public void onCallAdded(Phone phone, Call call) { }
接22_2.
22_1. //pacakge/apps/InCallUI/src/com/android/incallui/
CallList.java
@Override
onCallAdded
/*** Static singleton accessor method.*/public static CallList getInstance() {return sInstance;}private Phone.Listener mPhoneListener = new Phone.Listener() {@Overridepublic void onCallAdded(Phone phone, android.telecom.Call telecommCall) {Call call = new Call(telecommCall);if (call.getState() == Call.State.INCOMING) {onIncoming(call, call.getCannedSmsResponses());} else {onUpdate(call);}}
执行了下面的方法,但再往后的步骤不是从这里走的。
onIncoming()
/*** Called when a single call has changed.*/public void onIncoming(Call call, List<String> textMessages) {Log.d(this, "onIncoming - " + call);// Update active subscription from call object. it will be set by// Telecomm service for incoming call and whenever active sub changes.if (call.mIsActiveSub) {long sub = call.getSubId();Log.d(this, "onIncoming - sub:" + sub + " mSubId:" + mSubId);if (sub != mSubId) {setActiveSubscription(sub);}}if (updateCallInMap(call)) {Log.i(this, "onIncoming - " + call);}updateCallTextMap(call, textMessages);for (Listener listener : mListeners) {listener.onIncomingCall(call);}}
22_2. //pacakge/apps/InCallUI/src/com/android/incallui/InCallPresenter.java
@Override
onCallAdded
public class InCallPresenter implements CallList.Listener, InCallPhoneListener {...private final Phone.Listener mPhoneListener = new Phone.Listener() {...@Overridepublic void onCallAdded(Phone phone, android.telecom.Call call) {call.addListener(mCallListener);}
23. //pacakge/apps/InCallUI/src/com/android/incallui/InCallPresenter.java @override onCallAdded
onIncomingCall
/*** Called when there is a new incoming call.** @param call*/@Overridepublic void onIncomingCall(Call call) {InCallState newState = startOrFinishUi(InCallState.INCOMING);InCallState oldState = mInCallState;Log.i(this, "Phone switching state: " + oldState + " -> " + newState);mInCallState = newState;for (IncomingCallListener listener : mIncomingCallListeners) {listener.onIncomingCall(oldState, mInCallState, call);}if (CallList.getInstance().isDsdaEnabled() && (mInCallActivity != null)) {mInCallActivity.updateDsdaTab();}}
startOrFinishUi() 开始或结束UI
/*** When the state of in-call changes, this is the first method to get called. It determines if* the UI needs to be started or finished depending on the new state and does it.*/private InCallState startOrFinishUi(InCallState newState) {Log.d(this, "startOrFinishUi: " + mInCallState + " -> " + newState);// TODO: Consider a proper state machine implementation// If the state isn't changing or if we're transitioning from pending outgoing to actual// outgoing, we have already done any starting/stopping of activities in a previous pass// ...so lets cut out earlyboolean alreadyOutgoing = mInCallState == InCallState.PENDING_OUTGOING &&newState == InCallState.OUTGOING;boolean isAnyOtherSubActive = InCallState.INCOMING == newState &&mCallList.isAnyOtherSubActive(mCallList.getActiveSubscription());if ((newState == mInCallState && !(mInCallActivity == null && isAnyOtherSubActive))|| alreadyOutgoing) {return newState;}// A new Incoming call means that the user needs to be notified of the the call (since// it wasn't them who initiated it). We do this through full screen notifications and// happens indirectly through {@link StatusBarNotifier}.//// The process for incoming calls is as follows://// 1) CallList - Announces existence of new INCOMING call// 2) InCallPresenter - Gets announcement and calculates that the new InCallState// - should be set to INCOMING.// 3) InCallPresenter - This method is called to see if we need to start or finish// the app given the new state.// 4) StatusBarNotifier - Listens to InCallState changes. InCallPresenter calls// StatusBarNotifier explicitly to issue a FullScreen Notification// that will either start the InCallActivity or show the user a// top-level notification dialog if the user is in an immersive app.// That notification can also start the InCallActivity.// 5) InCallActivity - Main activity starts up and at the end of its onCreate will// call InCallPresenter::setActivity() to let the presenter// know that start-up is complete.//// [ AND NOW YOU'RE IN THE CALL. voila! ]//// Our app is started using a fullScreen notification. We need to do this whenever// we get an incoming call.final boolean startStartupSequence = (InCallState.INCOMING == newState);// A dialog to show on top of the InCallUI to select a PhoneAccountfinal boolean showAccountPicker = (InCallState.WAITING_FOR_ACCOUNT == newState);// A new outgoing call indicates that the user just now dialed a number and when that// happens we need to display the screen immediately or show an account picker dialog if// no default is set. However, if the main InCallUI is already visible, we do not want to// re-initiate the start-up animation, so we do not need to do anything here.//// It is also possible to go into an intermediate state where the call has been initiated// but Telecomm has not yet returned with the details of the call (handle, gateway, etc.).// This pending outgoing state can also launch the call screen.//// This is different from the incoming call sequence because we do not need to shock the// user with a top-level notification. Just show the call UI normally.final boolean mainUiNotVisible = !isShowingInCallUi() || !getCallCardFragmentVisible();final boolean showCallUi = ((InCallState.PENDING_OUTGOING == newState ||InCallState.OUTGOING == newState) && mainUiNotVisible);// TODO: Can we be suddenly in a call without it having been in the outgoing or incoming// state? I havent seen that but if it can happen, the code below should be enabled.// showCallUi |= (InCallState.INCALL && !isActivityStarted());// The only time that we have an instance of mInCallActivity and it isn't started is// when it is being destroyed. In that case, lets avoid bringing up another instance of// the activity. When it is finally destroyed, we double check if we should bring it back// up so we aren't going to lose anything by avoiding a second startup here.boolean activityIsFinishing = mInCallActivity != null && !isActivityStarted();if (activityIsFinishing) {Log.i(this, "Undo the state change: " + newState + " -> " + mInCallState);return mInCallState;}if (showCallUi || showAccountPicker) {Log.i(this, "Start in call UI");showInCall(false /* showDialpad */, !showAccountPicker /* newOutgoingCall */);}//如果是来电的话- else if (startStartupSequence) {
Log.i(this, "Start Full Screen in call UI");// We're about the bring up the in-call UI for an incoming call. If we still have// dialogs up, we need to clear them out before showing incoming screen.if (isActivityStarted()) {mInCallActivity.dismissPendingDialogs();}if (!startUi(newState)) {// startUI refused to start the UI. This indicates that it needed to restart the// activity. When it finally restarts, it will call us back, so we do not actually// change the state yet (we return mInCallState instead of newState).return mInCallState;}} else if (newState == InCallState.NO_CALLS) {// The new state is the no calls state. Tear everything down.attemptFinishActivity();attemptCleanup();}return newState;}
startUi 创建UI
private boolean startUi(InCallState inCallState) {final Call incomingCall = mCallList.getIncomingCall();boolean isCallWaiting = mCallList.getActiveCall() != null &&mCallList.getIncomingCall() != null;// If the screen is off, we need to make sure it gets turned on for incoming calls.// This normally works just fine thanks to FLAG_TURN_SCREEN_ON but that only works// when the activity is first created. Therefore, to ensure the screen is turned on// for the call waiting case, we finish() the current activity and start a new one.// There should be no jank from this since the screen is already off and will remain so// until our new activity is up.// In addition to call waiting scenario, we need to force finish() in case of DSDA when// we get an incoming call on one sub and there is a live call in other sub and screen// is off.boolean anyOtherSubActive = (incomingCall != null &&mCallList.isAnyOtherSubActive(mCallList.getActiveSubscription()));Log.i(this, "Start UI " + " anyOtherSubActive:" + anyOtherSubActive);if (isCallWaiting || anyOtherSubActive) {if (mProximitySensor.isScreenReallyOff() && isActivityStarted()) {mInCallActivity.finish();// When the activity actually finishes, we will start it again if there are// any active calls, so we do not need to start it explicitly here. Note, we// actually get called back on this function to restart it.// We return false to indicate that we did not actually start the UI.return false;} else {showInCall(false, false);}} else {mStatusBarNotifier.updateNotification(inCallState, mCallList);}return true;}
23. //pakages/apps/InCallUI/src/com/android/incallui/StatuBarNotifier.java
updateNotification()
/*** Updates the phone app's status bar notification *and* launches the* incoming call UI in response to a new incoming call.** If an incoming call is ringing (or call-waiting), the notification* will also include a "fullScreenIntent" that will cause the* InCallScreen to be launched, unless the current foreground activity* is marked as "immersive".** (This is the mechanism that actually brings up the incoming call UI* when we receive a "new ringing connection" event from the telephony* layer.)** Also note that this method is safe to call even if the phone isn't* actually ringing (or, more likely, if an incoming call *was** ringing briefly but then disconnected). In that case, we'll simply* update or cancel the in-call notification based on the current* phone state.** @see #updateInCallNotification(InCallState,CallList)*/public void updateNotification(InCallState state, CallList callList) {updateInCallNotification(state, callList);}
updateInCallNotification()
/*** Helper method for updateInCallNotification() and* updateNotification(): Update the phone app's* status bar notification based on the current telephony state, or* cancels the notification if the phone is totally idle.*/private void updateInCallNotification(final InCallState state, CallList callList) {Log.d(this, "updateInCallNotification...");Call call = getCallToShow(callList);// Whether we have an outgoing call but the incall UI has yet to show up.// Since we don't normally show a notification while the incall screen is// in the foreground, if we show the outgoing notification before the activity// comes up the user will see it flash on and off on an outgoing call. We therefore// do not show the notification for outgoing calls before the activity has started.boolean isOutgoingWithoutIncallUi =state == InCallState.OUTGOING &&!InCallPresenter.getInstance().isActivityPreviouslyStarted();// Whether to show a notification immediately.boolean showNotificationNow =// We can still be in the INCALL state when a call is disconnected (in order to show// the "Call ended" screen. So check that we have an active connection too.(call != null) &&// We show a notification iff there is an active call.state.isConnectingOrConnected() &&// If the UI is already showing, then for most cases we do not want to show// a notification since that would be redundant, unless it is an incoming call,// in which case the notification is actually an important alert.(!InCallPresenter.getInstance().isShowingInCallUi() || state.isIncoming()) &&// If we have an outgoing call with no UI but the timer has fired, we show// a notification anyway.(!isOutgoingWithoutIncallUi ||mNotificationTimer.getState() == NotificationTimer.State.FIRED);if (showNotificationNow) {showNotification(call);} else {cancelInCall();if (isOutgoingWithoutIncallUi &&mNotificationTimer.getState() == NotificationTimer.State.CLEAR) {mNotificationTimer.schedule();}}// If we see a UI, or we are done with calls for now, reset to ground state.if (InCallPresenter.getInstance().isShowingInCallUi() || call == null) {mNotificationTimer.clear();}}
private void showNotification(final Call call) {final boolean isIncoming = (call.getState() == Call.State.INCOMING ||call.getState() == Call.State.CALL_WAITING);// we make a call to the contact info cache to query for supplemental data to what the// call provides. This includes the contact name and photo.// This callback will always get called immediately and synchronously with whatever data// it has available, and may make a subsequent call later (same thread) if it had to// call into the contacts provider for more data.mContactInfoCache.findInfo(call, isIncoming, new ContactInfoCacheCallback() {@Overridepublic void onContactInfoComplete(String callId, ContactCacheEntry entry) {Call call = CallList.getInstance().getCallById(callId);if (call != null) {buildAndSendNotification(call, entry);}}@Overridepublic void onImageLoadComplete(String callId, ContactCacheEntry entry) {Call call = CallList.getInstance().getCallById(callId);if (call != null) {buildAndSendNotification(call, entry);}}});}
构建heads-ip notification通知
/*** Sets up the main Ui for the notification*/private void buildAndSendNotification(Call originalCall, ContactCacheEntry contactInfo) {// This can get called to update an existing notification after contact information has come// back. However, it can happen much later. Before we continue, we need to make sure that// the call being passed in is still the one we want to show in the notification.final Call call = getCallToShow(CallList.getInstance());if (call == null || !call.getId().equals(originalCall.getId())) {return;}final int state = call.getState();final boolean isConference = call.isConferenceCall();final boolean isVideoUpgradeRequest = call.getSessionModificationState()== Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;// Check if data has changed; if nothing is different, don't issue another notification.final int iconResId = getIconToDisplay(call);final Bitmap largeIcon = getLargeIconToDisplay(contactInfo, isConference);final int contentResId = getContentString(call);final String contentTitle = getContentTitle(contactInfo, isConference);if (!checkForChangeAndSaveData(iconResId, contentResId, largeIcon, contentTitle, state)) {return;}/** Nothing more to check...build and send it.*/final Notification.Builder builder = getNotificationBuilder();// Set up the main intent to send the user to the in-call screenfinal PendingIntent inCallPendingIntent = createLaunchPendingIntent();builder.setContentIntent(inCallPendingIntent);// Set the intent as a full screen intent as well if a call is incomingif ((state == Call.State.INCOMING || state == Call.State.CALL_WAITING) &&!InCallPresenter.getInstance().isShowingInCallUi()) {configureFullScreenIntent(builder, inCallPendingIntent, call);}// Set the contentbuilder.setContentText(mContext.getString(contentResId));builder.setSmallIcon(iconResId);builder.setContentTitle(contentTitle);builder.setLargeIcon(largeIcon);builder.setColor(mContext.getResources().getColor(R.color.dialer_theme_color));if (isVideoUpgradeRequest) {builder.setUsesChronometer(false);addDismissUpgradeRequestAction(builder);addAcceptUpgradeRequestAction(builder);} else {createIncomingCallNotification(call, state, builder);}addPersonReference(builder, contactInfo, call);/** Fire off the notification // 发射通知!!*/Notification notification = builder.build();Log.d(this, "Notifying IN_CALL_NOTIFICATION: " + notification);mNotificationManager.notify(IN_CALL_NOTIFICATION, notification);mIsShowingNotification = true;}
上面两个方法都要执行,一个是创建通知 一个是启动通知
1.创建通知
createIncomingCallNotification
private void createIncomingCallNotification(Call call, int state, Notification.Builder builder) {if (state == Call.State.ACTIVE) {builder.setUsesChronometer(true);builder.setWhen(call.getConnectTimeMillis());} else {builder.setUsesChronometer(false);}// Add hang up option for any active calls (active | onhold), outgoing calls (dialing).if (state == Call.State.ACTIVE ||state == Call.State.ONHOLD ||Call.State.isDialing(state)) {addHangupAction(builder);} else if (state == Call.State.INCOMING || state == Call.State.CALL_WAITING) {addDismissAction(builder);if (call.isVideoCall(mContext)) {addVoiceAction(builder);addVideoCallAction(builder);} else {addAnswerAction(builder);}}}
上面添加“接听”和“忽略”两个操作,
private void addAnswerAction(Notification.Builder builder) {Log.i(this, "Will show \"answer\" action in the incoming call Notification");PendingIntent answerVoicePendingIntent = createNotificationPendingIntent(mContext, InCallApp.ACTION_ANSWER_VOICE_INCOMING_CALL);builder.addAction(R.drawable.ic_call_white_24dp,mContext.getText(R.string.description_target_answer),answerVoicePendingIntent);}private void addDismissAction(Notification.Builder builder) {Log.i(this, "Will show \"dismiss\" action in the incoming call Notification");PendingIntent declinePendingIntent =createNotificationPendingIntent(mContext, InCallApp.ACTION_DECLINE_INCOMING_CALL);builder.addAction(R.drawable.ic_close_dk,mContext.getText(R.string.notification_action_dismiss),declinePendingIntent);}
启动通知了:
NotificationManager.java
/*** Post a notification to be shown in the status bar. If a notification with* the same id has already been posted by your application and has not yet been canceled, it* will be replaced by the updated information.** @param id An identifier for this notification unique within your* application.* @param notification A {@link Notification} object describing what to show the user. Must not* be null.*/public void notify(int id, Notification notification){notify(null, id, notification);}
根据下面的说明我们知道,在状态栏显示处通知
我们跟的是来电的代码,来电信息呈献给用户有两种情况:
1. 屏幕唤醒状态下 是在通知栏显示通知
2. 屏幕熄灭状态下 点亮屏幕全屏显示
如下图所示:
/*** Post a notification to be shown in the status bar. If a notification with* the same tag and id has already been posted by your application and has not yet been* canceled, it will be replaced by the updated information.** @param tag A string identifier for this notification. May be {@code null}.* @param id An identifier for this notification. The pair (tag, id) must be unique* within your application.* @param notification A {@link Notification} object describing what to* show the user. Must not be null.*/public void notify(String tag, int id, Notification notification){int[] idOut = new int[1];INotificationManager service = getService();String pkg = mContext.getPackageName();if (notification.sound != null) {notification.sound = notification.sound.getCanonicalUri();if (StrictMode.vmFileUriExposureEnabled()) {notification.sound.checkFileUriExposed("Notification.sound");}}if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");Notification stripped = notification.clone();Builder.stripForDelivery(stripped);try {service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,stripped, idOut, UserHandle.myUserId());if (id != idOut[0]) {Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);}} catch (RemoteException e) {}}

4485

被折叠的 条评论
为什么被折叠?



