Android 5.0 Lollipop MT流程 代码

--------
写在前:本人新手有理解不准确或者错误的地方请大家指正出来,我好及时修改
-----
---我理解的来电流程大概分为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状态变化的通知,
Android 5.0 Lollipop MT流程 代码_第1张图片


1. framwork/opt/telephony/.../RIL.java
1.1.RIL接收到RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED消息
1.2.然后经由mCallStateRegistrants.notifyRegistrants发出通知(RegistrantList消息处理机制此处不做具体说明)。
        
        
        
        
private void processUnsolicited ( 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消息处理机制)。
        
        
        
        
 @Override
    public 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);
...
@Override
public 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 类型并发送。
         
         
         
         
@Override
public void
getCurrentCalls (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消息通知后,会在CallTracker中的handleMessage方法中响应。并且它的消息类型是“ EVENT_POLL_CALLS_RESULT"
         
         
         
         
@Override
public void
handleMessage (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*/ void
notifyRegistrants(AsyncResult ar)
{
internalNotifyRegistrants(ar.result, ar.exception);
}
          
          
          
          
private synchronized void
internalNotifyRegistrants (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*/ void
internalNotifyRegistrant (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.
@Override
public 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

Android 5.0 Lollipop MT流程 代码_第2张图片

② 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
*/
@Override
public 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();
//启动Activity
mContext.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

// packages / services / Telecomm / AndroidManifest.xml

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));
            }
        } 
 
 


Android 5.0 Lollipop MT流程 代码_第3张图片
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() {
@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);
}
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 {
@Override
public 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

         
         
         
         
@Override
public void createConnection(
PhoneAccountHandle connectionManagerPhoneAccount,
String id,
ConnectionRequest request,
boolean isIncoming,
boolean isUnknown) {
//chengzhi
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();
}

13.1 handleMessage() MSG_CREATE_CONNECTION
         
         
         
         
case MSG_CREATE_CONNECTION: {
SomeArgs args = (SomeArgs) msg.obj;
try {
//chengzhi
final 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() {
@Override
public void run() {
createConnection(
connectionManagerPhoneAccount,
id,
request,
isIncoming,
isUnknown);
}
});
} else {
//chengzhi debug
createConnection(
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 01
Connection 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 03
adapter.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 {
 
@Override
public 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() {
@Override
public 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 uniformly
removeCall(callId, connection.getDisconnectCause());
} else {
// Successful connection
if (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() {
@Override
public 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 {
...
@Override
public 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();
}

Android 5.0 Lollipop MT流程 代码_第4张图片

上图名字应为Telecomm -> InCallUI

InCallUI

⑥ CallList>StatubarNotifier    开始启动界面 显示来电

18. //package/services/Telecomm/src/com/android/server/telecom/ InCallController.java  
18.1 重写onCallAdded
@OverrideonCallAdded()
             
             
             
             
@Override
public 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 =newInCallServiceConnection();
           
           
           
           
/**
* 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 {
@Override
public void setInCallAdapter(IInCallAdapter inCallAdapter) {
mHandler.obtainMessage(MSG_SET_IN_CALL_ADAPTER, inCallAdapter).sendToTarget();
}
 
@Override
public 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()) {
@Override
public 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
             
             
             
             
@SystemApi public 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() {
@Override
public 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() {
...
 
        @Override
public 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
*/
@Override
public 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
注意注释部分的 immersive app

              
              
              
              
/**
* 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 early
boolean 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 PhoneAccount
final 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
    关于是全屏显示还是Heads-Up Notification 显示, 具体的判断条件我还没理清楚,如果有人知道的话,请在评论区写一下或者贴出一个链接,本人不胜感激。
                  
                  
                  
                  
    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() {
    @Override
    public void onContactInfoComplete(String callId, ContactCacheEntry entry) {
    Call call = CallList.getInstance().getCallById(callId);
    if (call != null) {
    buildAndSendNotification(call, entry);
    }
    }
     
    @Override
    public void onImageLoadComplete(String callId, ContactCacheEntry entry) {
    Call call = CallList.getInstance().getCallById(callId);
    if (call != null) {
    buildAndSendNotification(call, entry);
    }
    }
    });
    }

                   
                   
                   
                   
    /**
    * 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 screen
    final PendingIntent inCallPendingIntent = createLaunchPendingIntent();
    builder.setContentIntent(inCallPendingIntent);
     
    // Set the intent as a full screen intent as well if a call is incoming
    if ((state == Call.State.INCOMING || state == Call.State.CALL_WAITING) &&
    !InCallPresenter.getInstance().isShowingInCallUi()) {
    configureFullScreenIntent(builder, inCallPendingIntent, call);
    }
     
    // Set the content
    builder.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. 屏幕熄灭状态下 点亮屏幕全屏显示

    ==2015-05-06修改==
    上面的描述不准确,评论区也有同学指出,在非熄屏但锁屏的情况下,来电也是全屏的。
    这一部分的东西确实我没有理清楚,后面有时间我会搞清楚再更新这一部分的内容。

    如下图所示:
    Android 5.0 Lollipop MT流程 代码_第5张图片

                   
                   
                   
                   
    /**
    * 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) {
    }
    }

    你可能感兴趣的:(Android 5.0 Lollipop MT流程 代码)