Android 5.0 Lollipop MT流程 代码

2024-02-27 13:10

本文主要是介绍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状态变化的通知,



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


② 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.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}]
<div class="linenums" style="color: rgb(30, 52, 123); margin-top: 0px; margin-bottom: 0px; padding-left: 0px; "><div class="L0" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">    </span><span class="kwd" style="color: rgb(30, 52, 123); ">static</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="kwd" style="color: rgb(30, 52, 123); ">void</span><span class="pln" style="color: rgb(72, 72, 76); "> processIncomingCallIntent</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="typ" style="color: teal; ">Intent</span><span class="pln" style="color: rgb(72, 72, 76); "> intent</span><span class="pun" style="color: rgb(147, 161, 161); ">)</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">{</span></code></div><div class="L1" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="typ" style="color: teal; ">PhoneAccountHandle</span><span class="pln" style="color: rgb(72, 72, 76); "> phoneAccountHandle </span><span class="pun" style="color: rgb(147, 161, 161); ">=</span><span class="pln" style="color: rgb(72, 72, 76); "> intent</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">getParcelableExtra</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span></code></div><div class="L2" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">                </span><span class="typ" style="color: teal; ">TelecomManager</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">EXTRA_PHONE_ACCOUNT_HANDLE</span><span class="pun" style="color: rgb(147, 161, 161); ">);</span></code></div><div class="L3" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "> </code></div><div class="L4" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="kwd" style="color: rgb(30, 52, 123); ">if</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">phoneAccountHandle </span><span class="pun" style="color: rgb(147, 161, 161); ">==</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="kwd" style="color: rgb(30, 52, 123); ">null</span><span class="pun" style="color: rgb(147, 161, 161); ">)</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">{</span></code></div><div class="L5" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">            </span><span class="typ" style="color: teal; ">Log</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">w</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">TAG</span><span class="pun" style="color: rgb(147, 161, 161); ">,</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="str" style="color: rgb(221, 17, 68); ">"Rejecting incoming call due to null phone account"</span><span class="pun" style="color: rgb(147, 161, 161); ">);</span></code></div><div class="L6" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">            </span><span class="kwd" style="color: rgb(30, 52, 123); ">return</span><span class="pun" style="color: rgb(147, 161, 161); ">;</span></code></div><div class="L7" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="pun" style="color: rgb(147, 161, 161); ">}</span></code></div><div class="L8" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="kwd" style="color: rgb(30, 52, 123); ">if</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">phoneAccountHandle</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">getComponentName</span><span class="pun" style="color: rgb(147, 161, 161); ">()</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">==</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="kwd" style="color: rgb(30, 52, 123); ">null</span><span class="pun" style="color: rgb(147, 161, 161); ">)</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">{</span></code></div><div class="L9" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">            </span><span class="typ" style="color: teal; ">Log</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">w</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">TAG</span><span class="pun" style="color: rgb(147, 161, 161); ">,</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="str" style="color: rgb(221, 17, 68); ">"Rejecting incoming call due to null component name"</span><span class="pun" style="color: rgb(147, 161, 161); ">);</span></code></div><div class="L0" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">            </span><span class="kwd" style="color: rgb(30, 52, 123); ">return</span><span class="pun" style="color: rgb(147, 161, 161); ">;</span></code></div><div class="L1" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="pun" style="color: rgb(147, 161, 161); ">}</span></code></div><div class="L2" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "> </code></div><div class="L3" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="typ" style="color: teal; ">Bundle</span><span class="pln" style="color: rgb(72, 72, 76); "> clientExtras </span><span class="pun" style="color: rgb(147, 161, 161); ">=</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="kwd" style="color: rgb(30, 52, 123); ">null</span><span class="pun" style="color: rgb(147, 161, 161); ">;</span></code></div><div class="L4" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="kwd" style="color: rgb(30, 52, 123); ">if</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">intent</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">hasExtra</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="typ" style="color: teal; ">TelecomManager</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">EXTRA_INCOMING_CALL_EXTRAS</span><span class="pun" style="color: rgb(147, 161, 161); ">))</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">{</span></code></div><div class="L5" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">            clientExtras </span><span class="pun" style="color: rgb(147, 161, 161); ">=</span><span class="pln" style="color: rgb(72, 72, 76); "> intent</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">getBundleExtra</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="typ" style="color: teal; ">TelecomManager</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">EXTRA_INCOMING_CALL_EXTRAS</span><span class="pun" style="color: rgb(147, 161, 161); ">);</span></code></div><div class="L6" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="pun" style="color: rgb(147, 161, 161); ">}</span></code></div><div class="L7" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="kwd" style="color: rgb(30, 52, 123); ">if</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">clientExtras </span><span class="pun" style="color: rgb(147, 161, 161); ">==</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="kwd" style="color: rgb(30, 52, 123); ">null</span><span class="pun" style="color: rgb(147, 161, 161); ">)</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">{</span></code></div><div class="L8" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">            clientExtras </span><span class="pun" style="color: rgb(147, 161, 161); ">=</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="typ" style="color: teal; ">Bundle</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">EMPTY</span><span class="pun" style="color: rgb(147, 161, 161); ">;</span></code></div><div class="L9" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="pun" style="color: rgb(147, 161, 161); ">}</span></code></div><div class="L0" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "> </code></div><div class="L1" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="typ" style="color: teal; ">Log</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">d</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">TAG</span><span class="pun" style="color: rgb(147, 161, 161); ">,</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="str" style="color: rgb(221, 17, 68); ">"Processing incoming call from connection service [%s]"</span><span class="pun" style="color: rgb(147, 161, 161); ">,</span></code></div><div class="L2" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">                phoneAccountHandle</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">getComponentName</span><span class="pun" style="color: rgb(147, 161, 161); ">());</span></code></div><div class="L3" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        getCallsManager</span><span class="pun" style="color: rgb(147, 161, 161); ">().</span><span class="pln" style="color: rgb(72, 72, 76); background-color: rgb(192, 192, 192); ">processIncomingCallIntent</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">phoneAccountHandle</span><span class="pun" style="color: rgb(147, 161, 161); ">,</span><span class="pln" style="color: rgb(72, 72, 76); "> clientExtras</span><span class="pun" style="color: rgb(147, 161, 161); ">);</span></code></div><div class="L4" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">    </span><span class="pun" style="color: rgb(147, 161, 161); ">}</span></code></div></div>



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() {
@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();
}





上图名字应为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
@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() {
@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()
<div class="linenums" style="margin-top: 0px; margin-bottom: 0px; padding-left: 0px; "><div class="L0" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="com" style="color: rgb(147, 161, 161); ">/**</span></code></div><div class="L1" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="com" style="color: rgb(147, 161, 161); ">     * Called when a single call has changed.</span></code></div><div class="L2" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="com" style="color: rgb(147, 161, 161); ">     */</span></code></div><div class="L3" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">    </span><span class="kwd" style="color: rgb(30, 52, 123); ">public</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="kwd" style="color: rgb(30, 52, 123); ">void</span><span class="pln" style="color: rgb(72, 72, 76); "> onIncoming</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="typ" style="color: teal; ">Call</span><span class="pln" style="color: rgb(72, 72, 76); "> call</span><span class="pun" style="color: rgb(147, 161, 161); ">,</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="typ" style="color: teal; ">List</span><span class="pun" style="color: rgb(147, 161, 161); "><</span><span class="typ" style="color: teal; ">String</span><span class="pun" style="color: rgb(147, 161, 161); ">></span><span class="pln" style="color: rgb(72, 72, 76); "> textMessages</span><span class="pun" style="color: rgb(147, 161, 161); ">)</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">{</span></code></div><div class="L4" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="typ" style="color: teal; ">Log</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">d</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="kwd" style="color: rgb(30, 52, 123); ">this</span><span class="pun" style="color: rgb(147, 161, 161); ">,</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="str" style="color: rgb(221, 17, 68); ">"onIncoming - "</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">+</span><span class="pln" style="color: rgb(72, 72, 76); "> call</span><span class="pun" style="color: rgb(147, 161, 161); ">);</span></code></div><div class="L5" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "> </code></div><div class="L6" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="com" style="color: rgb(147, 161, 161); ">// Update active subscription from call object. it will be set by</span></code></div><div class="L7" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="com" style="color: rgb(147, 161, 161); ">// Telecomm service for incoming call and whenever active sub changes.</span></code></div><div class="L8" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="kwd" style="color: rgb(30, 52, 123); ">if</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">call</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">mIsActiveSub</span><span class="pun" style="color: rgb(147, 161, 161); ">)</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">{</span></code></div><div class="L9" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">            </span><span class="kwd" style="color: rgb(30, 52, 123); ">long</span><span class="pln" style="color: rgb(72, 72, 76); "> sub </span><span class="pun" style="color: rgb(147, 161, 161); ">=</span><span class="pln" style="color: rgb(72, 72, 76); "> call</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">getSubId</span><span class="pun" style="color: rgb(147, 161, 161); ">();</span></code></div><div class="L0" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">            </span><span class="typ" style="color: teal; ">Log</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">d</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="kwd" style="color: rgb(30, 52, 123); ">this</span><span class="pun" style="color: rgb(147, 161, 161); ">,</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="str" style="color: rgb(221, 17, 68); ">"onIncoming - sub:"</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">+</span><span class="pln" style="color: rgb(72, 72, 76); "> sub </span><span class="pun" style="color: rgb(147, 161, 161); ">+</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="str" style="color: rgb(221, 17, 68); ">" mSubId:"</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">+</span><span class="pln" style="color: rgb(72, 72, 76); "> mSubId</span><span class="pun" style="color: rgb(147, 161, 161); ">);</span></code></div><div class="L1" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">            </span><span class="kwd" style="color: rgb(30, 52, 123); ">if</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">sub </span><span class="pun" style="color: rgb(147, 161, 161); ">!=</span><span class="pln" style="color: rgb(72, 72, 76); "> mSubId</span><span class="pun" style="color: rgb(147, 161, 161); ">)</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">{</span></code></div><div class="L2" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">                setActiveSubscription</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">sub</span><span class="pun" style="color: rgb(147, 161, 161); ">);</span></code></div><div class="L3" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">            </span><span class="pun" style="color: rgb(147, 161, 161); ">}</span></code></div><div class="L4" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="pun" style="color: rgb(147, 161, 161); ">}</span></code></div><div class="L5" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "> </code></div><div class="L6" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="kwd" style="color: rgb(30, 52, 123); ">if</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">updateCallInMap</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">call</span><span class="pun" style="color: rgb(147, 161, 161); ">))</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">{</span></code></div><div class="L7" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">            </span><span class="typ" style="color: teal; ">Log</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">i</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="kwd" style="color: rgb(30, 52, 123); ">this</span><span class="pun" style="color: rgb(147, 161, 161); ">,</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="str" style="color: rgb(221, 17, 68); ">"onIncoming - "</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">+</span><span class="pln" style="color: rgb(72, 72, 76); "> call</span><span class="pun" style="color: rgb(147, 161, 161); ">);</span></code></div><div class="L8" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="pun" style="color: rgb(147, 161, 161); ">}</span></code></div><div class="L9" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        updateCallTextMap</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">call</span><span class="pun" style="color: rgb(147, 161, 161); ">,</span><span class="pln" style="color: rgb(72, 72, 76); "> textMessages</span><span class="pun" style="color: rgb(147, 161, 161); ">);</span></code></div><div class="L0" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "> </code></div><div class="L1" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="kwd" style="color: rgb(30, 52, 123); ">for</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="typ" style="color: teal; ">Listener</span><span class="pln" style="color: rgb(72, 72, 76); "> listener </span><span class="pun" style="color: rgb(147, 161, 161); ">:</span><span class="pln" style="color: rgb(72, 72, 76); "> mListeners</span><span class="pun" style="color: rgb(147, 161, 161); ">)</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">{</span></code></div><div class="L2" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">            listener</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); background-color: rgb(192, 192, 192); ">onIncomingCall</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">call</span><span class="pun" style="color: rgb(147, 161, 161); ">);</span></code></div><div class="L3" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="pun" style="color: rgb(147, 161, 161); ">}</span></code></div><div class="L4" style="color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">    </span><span class="pun" style="color: rgb(147, 161, 161); ">}</span></code></div><div><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pun" style="color: rgb(147, 161, 161); ">
</span></code></div></div>



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

如下图所示:


<div class="L0" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="com" style="color: rgb(147, 161, 161); ">/**</span></code></div><div class="L1" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="com" style="color: rgb(147, 161, 161); ">     * Post a notification to be shown in the <span style="background-color: rgb(192, 192, 192); ">status bar</span>. If a notification with</span></code></div><div class="L2" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="com" style="color: rgb(147, 161, 161); ">     * the same tag and id has already been posted by your application and has not yet been</span></code></div><div class="L3" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="com" style="color: rgb(147, 161, 161); ">     * canceled, it will be replaced by the updated information.</span></code></div><div class="L4" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="com" style="color: rgb(147, 161, 161); ">     *</span></code></div><div class="L5" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="com" style="color: rgb(147, 161, 161); ">     * @param tag A string identifier for this notification.  May be {@code null}.</span></code></div><div class="L6" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="com" style="color: rgb(147, 161, 161); ">     * @param id An identifier for this notification.  The pair (tag, id) must be unique</span></code></div><div class="L7" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="com" style="color: rgb(147, 161, 161); ">     *        within your application.</span></code></div><div class="L8" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="com" style="color: rgb(147, 161, 161); ">     * @param notification A {@link Notification} object describing what to</span></code></div><div class="L9" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="com" style="color: rgb(147, 161, 161); ">     *        show the user. Must not be null.</span></code></div><div class="L0" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="com" style="color: rgb(147, 161, 161); ">     */</span></code></div><div class="L1" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">    </span><span class="kwd" style="color: rgb(30, 52, 123); ">public</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="kwd" style="color: rgb(30, 52, 123); ">void</span><span class="pln" style="color: rgb(72, 72, 76); "> notify</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="typ" style="color: teal; ">String</span><span class="pln" style="color: rgb(72, 72, 76); "> tag</span><span class="pun" style="color: rgb(147, 161, 161); ">,</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="kwd" style="color: rgb(30, 52, 123); ">int</span><span class="pln" style="color: rgb(72, 72, 76); "> id</span><span class="pun" style="color: rgb(147, 161, 161); ">,</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="typ" style="color: teal; ">Notification</span><span class="pln" style="color: rgb(72, 72, 76); "> notification</span><span class="pun" style="color: rgb(147, 161, 161); ">)</span></code></div><div class="L2" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">    </span><span class="pun" style="color: rgb(147, 161, 161); ">{</span></code></div><div class="L3" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="kwd" style="color: rgb(30, 52, 123); ">int</span><span class="pun" style="color: rgb(147, 161, 161); ">[]</span><span class="pln" style="color: rgb(72, 72, 76); "> idOut </span><span class="pun" style="color: rgb(147, 161, 161); ">=</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="kwd" style="color: rgb(30, 52, 123); ">new</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="kwd" style="color: rgb(30, 52, 123); ">int</span><span class="pun" style="color: rgb(147, 161, 161); ">[</span><span class="lit" style="color: rgb(25, 95, 145); ">1</span><span class="pun" style="color: rgb(147, 161, 161); ">];</span></code></div><div class="L4" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="typ" style="color: teal; ">INotificationManager</span><span class="pln" style="color: rgb(72, 72, 76); "> service </span><span class="pun" style="color: rgb(147, 161, 161); ">=</span><span class="pln" style="color: rgb(72, 72, 76); "> getService</span><span class="pun" style="color: rgb(147, 161, 161); ">();</span></code></div><div class="L5" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="typ" style="color: teal; ">String</span><span class="pln" style="color: rgb(72, 72, 76); "> pkg </span><span class="pun" style="color: rgb(147, 161, 161); ">=</span><span class="pln" style="color: rgb(72, 72, 76); "> mContext</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">getPackageName</span><span class="pun" style="color: rgb(147, 161, 161); ">();</span></code></div><div class="L6" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="kwd" style="color: rgb(30, 52, 123); ">if</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">notification</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">sound </span><span class="pun" style="color: rgb(147, 161, 161); ">!=</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="kwd" style="color: rgb(30, 52, 123); ">null</span><span class="pun" style="color: rgb(147, 161, 161); ">)</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">{</span></code></div><div class="L7" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">            notification</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">sound </span><span class="pun" style="color: rgb(147, 161, 161); ">=</span><span class="pln" style="color: rgb(72, 72, 76); "> notification</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">sound</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">getCanonicalUri</span><span class="pun" style="color: rgb(147, 161, 161); ">();</span></code></div><div class="L8" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">            </span><span class="kwd" style="color: rgb(30, 52, 123); ">if</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="typ" style="color: teal; ">StrictMode</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">vmFileUriExposureEnabled</span><span class="pun" style="color: rgb(147, 161, 161); ">())</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">{</span></code></div><div class="L9" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">                notification</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">sound</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">checkFileUriExposed</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="str" style="color: rgb(221, 17, 68); ">"Notification.sound"</span><span class="pun" style="color: rgb(147, 161, 161); ">);</span></code></div><div class="L0" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">            </span><span class="pun" style="color: rgb(147, 161, 161); ">}</span></code></div><div class="L1" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="pun" style="color: rgb(147, 161, 161); ">}</span></code></div><div class="L2" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="kwd" style="color: rgb(30, 52, 123); ">if</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">localLOGV</span><span class="pun" style="color: rgb(147, 161, 161); ">)</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="typ" style="color: teal; ">Log</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">v</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">TAG</span><span class="pun" style="color: rgb(147, 161, 161); ">,</span><span class="pln" style="color: rgb(72, 72, 76); "> pkg </span><span class="pun" style="color: rgb(147, 161, 161); ">+</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="str" style="color: rgb(221, 17, 68); ">": notify("</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">+</span><span class="pln" style="color: rgb(72, 72, 76); "> id </span><span class="pun" style="color: rgb(147, 161, 161); ">+</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="str" style="color: rgb(221, 17, 68); ">", "</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">+</span><span class="pln" style="color: rgb(72, 72, 76); "> notification </span><span class="pun" style="color: rgb(147, 161, 161); ">+</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="str" style="color: rgb(221, 17, 68); ">")"</span><span class="pun" style="color: rgb(147, 161, 161); ">);</span></code></div><div class="L3" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="typ" style="color: teal; ">Notification</span><span class="pln" style="color: rgb(72, 72, 76); "> stripped </span><span class="pun" style="color: rgb(147, 161, 161); ">=</span><span class="pln" style="color: rgb(72, 72, 76); "> notification</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">clone</span><span class="pun" style="color: rgb(147, 161, 161); ">();</span></code></div><div class="L4" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="typ" style="color: teal; ">Builder</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">stripForDelivery</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">stripped</span><span class="pun" style="color: rgb(147, 161, 161); ">);</span></code></div><div class="L5" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="kwd" style="color: rgb(30, 52, 123); ">try</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">{</span></code></div><div class="L6" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">            service</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">enqueueNotificationWithTag</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">pkg</span><span class="pun" style="color: rgb(147, 161, 161); ">,</span><span class="pln" style="color: rgb(72, 72, 76); "> mContext</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">getOpPackageName</span><span class="pun" style="color: rgb(147, 161, 161); ">(),</span><span class="pln" style="color: rgb(72, 72, 76); "> tag</span><span class="pun" style="color: rgb(147, 161, 161); ">,</span><span class="pln" style="color: rgb(72, 72, 76); "> id</span><span class="pun" style="color: rgb(147, 161, 161); ">,</span></code></div><div class="L7" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">                    stripped</span><span class="pun" style="color: rgb(147, 161, 161); ">,</span><span class="pln" style="color: rgb(72, 72, 76); "> idOut</span><span class="pun" style="color: rgb(147, 161, 161); ">,</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="typ" style="color: teal; ">UserHandle</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">myUserId</span><span class="pun" style="color: rgb(147, 161, 161); ">());</span></code></div><div class="L8" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">            </span><span class="kwd" style="color: rgb(30, 52, 123); ">if</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">id </span><span class="pun" style="color: rgb(147, 161, 161); ">!=</span><span class="pln" style="color: rgb(72, 72, 76); "> idOut</span><span class="pun" style="color: rgb(147, 161, 161); ">[</span><span class="lit" style="color: rgb(25, 95, 145); ">0</span><span class="pun" style="color: rgb(147, 161, 161); ">])</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">{</span></code></div><div class="L9" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">                </span><span class="typ" style="color: teal; ">Log</span><span class="pun" style="color: rgb(147, 161, 161); ">.</span><span class="pln" style="color: rgb(72, 72, 76); ">w</span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="pln" style="color: rgb(72, 72, 76); ">TAG</span><span class="pun" style="color: rgb(147, 161, 161); ">,</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="str" style="color: rgb(221, 17, 68); ">"notify: id corrupted: sent "</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">+</span><span class="pln" style="color: rgb(72, 72, 76); "> id </span><span class="pun" style="color: rgb(147, 161, 161); ">+</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="str" style="color: rgb(221, 17, 68); ">", got back "</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">+</span><span class="pln" style="color: rgb(72, 72, 76); "> idOut</span><span class="pun" style="color: rgb(147, 161, 161); ">[</span><span class="lit" style="color: rgb(25, 95, 145); ">0</span><span class="pun" style="color: rgb(147, 161, 161); ">]);</span></code></div><div class="L0" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">            </span><span class="pun" style="color: rgb(147, 161, 161); ">}</span></code></div><div class="L1" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="pun" style="color: rgb(147, 161, 161); ">}</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="kwd" style="color: rgb(30, 52, 123); ">catch</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">(</span><span class="typ" style="color: teal; ">RemoteException</span><span class="pln" style="color: rgb(72, 72, 76); "> e</span><span class="pun" style="color: rgb(147, 161, 161); ">)</span><span class="pln" style="color: rgb(72, 72, 76); "> </span><span class="pun" style="color: rgb(147, 161, 161); ">{</span></code></div><div class="L2" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">        </span><span class="pun" style="color: rgb(147, 161, 161); ">}</span></code></div><div class="L3" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(247, 247, 249); color: rgb(190, 190, 197); line-height: 18px; padding-left: 0px; list-style-type: none; "><code class="language-java" style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; word-wrap: break-word; "><span class="pln" style="color: rgb(72, 72, 76); ">    </span><span class="pun" style="color: rgb(147, 161, 161); ">}</span></code></div>

这篇关于Android 5.0 Lollipop MT流程 代码的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/752495

相关文章

uniapp接入微信小程序原生代码配置方案(优化版)

uniapp项目需要把微信小程序原生语法的功能代码嵌套过来,无需把原生代码转换为uniapp,可以配置拷贝的方式集成过来 1、拷贝代码包到src目录 2、vue.config.js中配置原生代码包直接拷贝到编译目录中 3、pages.json中配置分包目录,原生入口组件的路径 4、manifest.json中配置分包,使用原生组件 5、需要把原生代码包里的页面修改成组件的方

公共筛选组件(二次封装antd)支持代码提示

如果项目是基于antd组件库为基础搭建,可使用此公共筛选组件 使用到的库 npm i antdnpm i lodash-esnpm i @types/lodash-es -D /components/CommonSearch index.tsx import React from 'react';import { Button, Card, Form } from 'antd'

17.用300行代码手写初体验Spring V1.0版本

1.1.课程目标 1、了解看源码最有效的方式,先猜测后验证,不要一开始就去调试代码。 2、浓缩就是精华,用 300行最简洁的代码 提炼Spring的基本设计思想。 3、掌握Spring框架的基本脉络。 1.2.内容定位 1、 具有1年以上的SpringMVC使用经验。 2、 希望深入了解Spring源码的人群,对 Spring有一个整体的宏观感受。 3、 全程手写实现SpringM

工作流Activiti初体验—流程撤回【二】

已经玩工作流了,打算还是研究一下撤回的功能。但是流程图里面并不带撤回的组件,所以需要自己动态改造一下,还是延续上一个流程继续试验撤回功能。《工作流Activiti初体验【一】》 完整流程图 我们研究一下分发任务撤回到发起任务,其他环节的撤回类似 撤回的原理大概如下: 将分发任务后面的方向清空,把发起任务拼接到原来的判断网关,然后结束分发任务,这样流程就到发起任务了 此时的流程如上图,

ROS话题通信流程自定义数据格式

ROS话题通信流程自定义数据格式 需求流程实现步骤定义msg文件编辑配置文件编译 在 ROS 通信协议中,数据载体是一个较为重要组成部分,ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:String、Int32、Int64、Char、Bool、Empty… 但是,这些数据一般只包含一个 data 字段,结构的单一意味着功能上的局限性,当传输一些复杂的数据,比如:

代码随想录算法训练营:12/60

非科班学习算法day12 | LeetCode150:逆波兰表达式 ,Leetcode239: 滑动窗口最大值  目录 介绍 一、基础概念补充: 1.c++字符串转为数字 1. std::stoi, std::stol, std::stoll, std::stoul, std::stoull(最常用) 2. std::stringstream 3. std::atoi, std

Eclipse+ADT与Android Studio开发的区别

下文的EA指Eclipse+ADT,AS就是指Android Studio。 就编写界面布局来说AS可以边开发边预览(所见即所得,以及多个屏幕预览),这个优势比较大。AS运行时占的内存比EA的要小。AS创建项目时要创建gradle项目框架,so,创建项目时AS比较慢。android studio基于gradle构建项目,你无法同时集中管理和维护多个项目的源码,而eclipse ADT可以同时打开

android 免费短信验证功能

没有太复杂的使用的话,功能实现比较简单粗暴。 在www.mob.com网站中可以申请使用免费短信验证功能。 步骤: 1.注册登录。 2.选择“短信验证码SDK” 3.下载对应的sdk包,我这是选studio的。 4.从头像那进入后台并创建短信验证应用,获取到key跟secret 5.根据技术文档操作(initSDK方法写在setContentView上面) 6.关键:在有用到的Mo

android一键分享功能部分实现

为什么叫做部分实现呢,其实是我只实现一部分的分享。如新浪微博,那还有没去实现的是微信分享。还有一部分奇怪的问题:我QQ分享跟QQ空间的分享功能,我都没配置key那些都是原本集成就有的key也可以实现分享,谁清楚的麻烦详解下。 实现分享功能我们可以去www.mob.com这个网站集成。免费的,而且还有短信验证功能。等这分享研究完后就研究下短信验证功能。 开始实现步骤(新浪分享,以下是本人自己实现

Android我的二维码扫描功能发展史(完整)

最近在研究下二维码扫描功能,跟据从网上查阅的资料到自己勉强已实现扫描功能来一一介绍我的二维码扫描功能实现的发展历程: 首页通过网络搜索发现做android二维码扫描功能看去都是基于google的ZXing项目开发。 2、搜索怎么使用ZXing实现自己的二维码扫描:从网上下载ZXing-2.2.zip以及core-2.2-source.jar文件,分别解压两个文件。然后把.jar解压出来的整个c