編輯:關於Android編程
一. 列表內容
Markdown和擴展Markdown簡潔的語法 代碼塊高亮 圖片鏈接和圖片上傳 LaTex數學公式 UML序列圖和流程圖 離線寫博客這段負責通話模塊的開發,研究了一下telephony模塊,網上參考了些資料加上自己的理解,總結了一下android6.0 MT 流程:。
gdsfdsfs 豐富的快捷鍵
先放出6.0的MT時序圖大家有個直觀感受,下面代碼一步步進行分析
第一部分:RIL–>GSMPhone Call狀態變化 -> 發出來電通知(frameworks\opt\telephony)
1. framwork/opt/telephony/…/RIL.java
作用:RIL-Java在本質上就是一個RIL代理,起到一個轉發的作用,是Android Java概念空間中的電話系統的起點。
RIL接收到RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED消息
private void processUnsolicited (Parcel p) {
...
switch(response) {
...
case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
if (RILJ_LOGD) unsljLog(response);
2、然後經由mCallStateRegistrants.notifyRegistrants發出通知
mCallStateRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
BaseCommands.java registerForCallStateChanged() mCallStateRegistrants.add(r);
@Override
public void registerForCallStateChanged(Handler h, int what, Object obj) {
Registrant r = new Registrant (h, what, obj);
//添加到觀察者列表
mCallStateRegistrants.add(r);
}
重點1:這其實是觀察者模式的一種實現形式
1.RefistrantList 通知者 2.Registrant 觀察者,這是一個一對多的關系,在有事件更新時,凡是在名單上登記過的對象,都會收到通知。
RegistrantList通知者支持對通知者的增加(add/addUnique)刪除(remove),並且能夠發出通知(notifyRegitrants);而Registrant作為觀察者,響應通知者發出的notifyRegistrant通知。
整體上這個消息注冊機制分為兩部分,消息注冊和消息通知。當調用regist方法時將Message存放進去,當其調用notify方法時將所有Message取出並發送到MessageQueue中等待處理。
3. framwork/opt/telephony/…GSMCallTracker.java
作用:GSMCallTracker在本質上是一個Handler。GSMCallTracker是Android的通話管理層。GSMCallTracker建立了ConnectionList來管理現行的通話連接,並向上層提供電話調用接口。
查找察者被調用的地方, 兩處被響應處理處理,其中一處:GSMCallTracker handleMessage
...//registerForCallStateChanged調用
mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
...
@Override
public void
//響應處理
handleMessage (Message msg) {
...
case EVENT_CALL_STATE_CHANGE: //MT第一次
//調用父類CallTracker查詢Call List方法
pollCallsWhenSafe();
break;
4、pollCallsWhenSafe()方法在CallTracker.java中實現
protected void pollCallsWhenSafe() {
mNeedsPoll = true;
if (checkNoOperationsPending()) {
mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
5、 //RIL.java中的getCurrentCalls方法
mCi.getCurrentCalls(mLastRelevantPoll);
}
}
回到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);
send(rr);
}
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, ret, null);
rr.mResult.sendToTarget();//發出handler消息通知
}
6、回到framworks/opt/telephony/…/telephony/gsm/GSMCallTracker.java
rr.mResult.sendToTarget()發出handler消息通知後,會在GSMCallTracker中的handleMessage方法中響應。並且它的消息類型是“EVENT_POLL_CALLS_RESULT”
@Override
public void handleMessage (Message msg) {
...
case EVENT_POLL_CALLS_RESULT:
ar = (AsyncResult)msg.obj;
if (msg == mLastRelevantPoll) {
mNeedsPoll = false;
mLastRelevantPoll = null;
7、 handlePollCalls((AsyncResult)msg.obj);
}
break;
8、handlePollCalls方法根據RIL發出的Call List對象判斷Call的狀態,並發出不同的通知,
1) 新來電的通知是: phone.notifyNewRingingConnection;
另外兩個是
2) 通話斷開通知 onDisconnected;
3) Call狀態變化通知 phone.notifiyPreciseCallStateChanged.
(當狀態改變之後便會通過GsmPhone的notifyPreciseCallStateChanged()方法發起響應)
來電的時候發出的是phone.notifyNewRingConnection通知,進入到notifyNewRingConnection方法
handlePollCalls(){
...
if (newRinging != null) {
mPhone.notifyNewRingingConnection(newRinging);
}
9、framworks/opt/telephony/…/telephony/gsm/GSMPhone.java
public void notifyNewRingingConnection(Connection c) {
super.notifyNewRingingConnectionP(c);
}
調用父類 PhoneBase.java(為com.android.internal.telephony.phone接口實現。)
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);
}
重點2: RegistrantList.java \frameworks\base\core\java\android\os
notifyRegistrants方法實現
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);
}
通過log發現PstnIncomingCallNotifier.java調用registerForNewRingingConnection()
01-05 07:10:05.517962 1596 1596 D Telephony: PstnIncomingCallNotifier: handleNewRingingConnection
第二部分:PstnIncomingCallNotifier–>Call 接收Framework層到通知–>准備創建連接
10、packages/services/Telephony/…/PstnIncomingCallNotifier.java(packages\services\telephony)
作用:監聽來之相關電話對象的來電事件和通知Telecom在每次發生的時候,這一實例的存在為了每個電話的通話服務
registerForNotifications方法調用registerForNewRingingConnection
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);
}
}
}
11、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;
12、從之前的log看: 由handleNewRingingConnection方法,處理新的來電連接。
private void handleNewRingingConnection(AsyncResult asyncResult) {
Log.d(this, "handleNewRingingConnection");
Connection connection = (Connection) asyncResult.result;
if (connection != null) {
Call call = connection.getCall();
//在發送intent到Telecom之前最後一次驗證ringing 狀態
if (call != null && call.getState().isRinging()) {
sendIncomingCallIntent(connection);
}
}
}
13、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);
}
14、addNewIncomingCall()定義在: framworks/base/telecomm/java/android/telecom/TelecomManager.java
作用:TelecomManager的功能則主要是對TelecomService提供的遠程接口的封裝,然後提供給應用使用。
來電時觸發 addNewIncomingCall方法
@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);
}
}
15、 packages/services/Telecomm/src/com/android/server/telecom/TelecomServiceImpl.java
繼承自ITelecomService,TelecomService的接口由TeleComManager封裝,並其供給應用使用,
重點2:telecom進程講解
addNewIncomingCall
新建intent 設定intent 的ACTION 、addFalgs等
/**
* @see android.telecom.TelecomManager#addNewIncomingCall
*/
@Override
public void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
synchronized (mLock) {
long token = Binder.clearCallingIdentity();
try {
Intent intent = new Intent(TelecomManager.ACTION_INCOMING_CALL);
intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
phoneAccountHandle);
intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, true);
if (extras != null) {
intent.putExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras);
}
CallIntentProcessor.processIncomingCallIntent(mCallsManager, intent);
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
CallIntentProcessor.java (packages\services\telecomm\src\com\android\server\telecom)
static void processIncomingCallIntent(CallsManager callsManager, Intent intent) {
callsManager.processIncomingCallIntent(phoneAccountHandle, clientExtras);
}
19、packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java
作用:
CallManager類提供了一個抽象層,以供phoneApp訪問和控制call等操作。它實現了Phone接口。CallManager提供呼叫和連接控制以及Channel能力。CallManager提供三種類型的API:
1,呼叫控制和操作,如dial()和hangup();
2,Channel的能力,如canconference();
3,注冊通知。接著將Phone注冊進mCM,Phone狀態改變之後InCallUI就能夠收到變化消息了。
void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
....
Call call = new Call(
mContext,
this,
mLock,
mConnectionServiceRepository,
mContactsAsyncHelper,
mCallerInfoAsyncQueryFactory,
handle,
null /* gatewayInfo */,
null /* connectionManagerPhoneAccount */,
phoneAccountHandle,
true /* isIncoming */,
false /* isConference */);
call.setIntentExtras(extras);
call.addListener(this);
20、new一個Call 對象 把前面的參數傳進來,然後調用call中建立連接的方法startCreateConnection
call.startCreateConnection(mPhoneAccountRegistrar);
}
21、packages/services/Telecomm/src/com/android/server/telecom/Call.java
作用:封裝的一個給定的電話在其整個生命周期的各個方面,從電話意圖被telecom接收開始
開始建立連接隊列,一旦完成創建,就應當有一個活動active的連接了存在service裡。
void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
Preconditions.checkState(mCreateConnectionProcessor == null);
mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
phoneAccountRegistrar, mContext);
mCreateConnectionProcessor.process();
}
第三部分:ConnectionServicesAdapter–>CallsManager 處理這個創建的連接–>成功來電 CallsManager–>Phone 成功來電–>准備啟動界面
22、packages/services/Telecomm/src/com/android/server/telecom/CreateConnectionProcessor.java
void process() {
Log.v(this, "process");
clearTimeout();
mAttemptRecords = new ArrayList<>();
if (mCall.getTargetPhoneAccount() != null) {
mAttemptRecords.add(new CallAttemptRecord(
mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));
}
adjustAttemptsForConnectionManager();
adjustAttemptsForEmergency();
mAttemptRecordIterator = mAttemptRecords.iterator();
attemptNextPhoneAccount();
}
23、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));
}
}
24、packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java 為撥出的電話建立連接,或者attach一個已經存在的來電。
void createConnection(final Call call, final CreateConnectionResponse response) {
mBinder.bind(callback);
}
25、ServiceBinder.java (packages\services\telecomm\src\com\android\server\telecom)
作用:抽象類用來進行綁定和解除綁定到指定的服務接口的工作。子類提供服務的意圖和組件名稱和這個類調用受保護方法在類綁定,未綁定或者失敗的時候
執行綁定到服務的操作(如果還沒有綁定)然後執行指定的回調方法
void bind(BindCallback callback, Call call) {
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(call);
Log.event(call, Log.Events.BIND_CS, mComponentName);
final int bindingFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
final boolean isBound;
if (mUserHandle != null) {
isBound = mContext.bindServiceAsUser(serviceIntent, connection, bindingFlags,
mUserHandle);
} else {
isBound = mContext.bindService(serviceIntent, connection, bindingFlags);
}
if (!isBound) {
handleFailedConnection();
return;
}
} else {
Log.d(ServiceBinder.this, "Service is already bound.");
Preconditions.checkNotNull(mBinder);
handleSuccessfulConnection();
}
}
26、上面的執行完之後,順序執行到onServiceConnected
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();
}
27、
private void handleSuccessfulConnection() {
for (BindCallback callback : mCallbacks) {
callback.onSuccess();
}
mCallbacks.clear();
}
28、回調上面的onSuccess() 執行mServiceInterface.createConnectioncreateConnection的具體實現在ConnectionService.java (frameworks\base\telecomm\java\android\telecom)
作用:一個提供電話連接到Android設備上運行的進程。
@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;
29、 mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget();
}
30、
case MSG_CREATE_CONNECTION: {
createConnection(
connectionManagerPhoneAccount,
id,
request,
isIncoming,
isUnknown);
}
31、這個方法可以被telecom用來創建呼出電話或者一個已存在的來電。任何一種情況,telecom都會循環經過一系列的服務和 調用createConnection util a connection service取消或者成功完成創建。
private void createConnection(
final PhoneAccountHandle callManagerAccount,
final String callId,
final ConnectionRequest request,
boolean isIncoming,
boolean isUnknown) {
Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
: isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
: onCreateOutgoingConnection(callManagerAccount, request);
.....
mAdapter.handleCreateConnectionComplete
32、前面建立連接成功了,後面處理成功的連接,後面執行mAdapter.handleCreateConnectionComplete
ConnectionServiceAdapter>CallsManager 處理這個創建的連接>成功來電
33~34.ConnectionServiceAdapter.java (frameworks\base\telecomm\java\android\telecom)
作用:提供iconnectionservice實現與系統的手機應用程序的交互方法。
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 成功來電>准備啟動界面
34. packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java
作用:Telecomm 層的連接管理者
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)) {
ConnectionServiceWrapper.this
.handleCreateConnectionComplete(callId, request, connection);
}
}
ConnectionServiceAdapterServant.java (frameworks\base\telecomm\java\android\telecom)
private final IConnectionServiceAdapter mStub = new IConnectionServiceAdapter.Stub() {
@Override
public void handleCreateConnectionComplete(
String id,
ConnectionRequest request,
ParcelableConnection connection) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = id;
args.arg2 = request;
args.arg3 = connection;
mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_COMPLETE, args).sendToTarget();
}
34、 handleMessage處理消息 MSG_HANDLE_CREATE_CONNECTION_COMPLETE
// Internal method defined to centralize handling of RemoteException
private void internalHandleMessage(Message msg) throws RemoteException {
switch (msg.what) {
case MSG_HANDLE_CREATE_CONNECTION_COMPLETE: {
SomeArgs args = (SomeArgs) msg.obj;
try {
mDelegate.handleCreateConnectionComplete(
(String) args.arg1,
(ConnectionRequest) args.arg2,
(ParcelableConnection) args.arg3);
} finally {
args.recycle();
}
break;
}
35、 如果成功連接 ConnectionServiceWrapper.java (packages\services\telecomm\src\com\android\server\telecom)
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方法
36. packages/services/Telecomm/src/com/android/server/telecom/Call.java 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()));
}
}
}
37、 Runnable mDirectToVoicemailRunnable
private final Runnable mDirectToVoicemailRunnable = new Runnable() {
@Override
public void run() {
processDirectToVoicemail();
}
38、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;
}
}
39. package/services/Telecomm/src/com/android/server/telecom/CallsManager.java
39.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());
}
}
39.2 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();
}
第四部分:CallList–>InCallActivity 開始啟動界面 –>顯示來電
40、package/services/Telecomm/src/com/android/server/telecom/InCallController.java
作用:結合並提供服務,通過它可以將更新發送到呼叫程序。這類被創建和擁有的callsmanager保持綁定到(被調用的應用程序中實現)。
重寫onCallAdded –>
@Override
public void onCallAdded(Call call) {
if (!isBoundToServices()) {
bindToServices(call);
} else {
adjustServiceBindingsForEmergency();
Log.i(this, "onCallAdded: %s", call);
// Track the call if we don't already know about it.
addCall(call);
for (Map.Entry entry : mInCallServices.entrySet()) {
ComponentName componentName = entry.getKey();
IInCallService inCallService = entry.getValue();
ParcelableCall parcelableCall = toParcelableCall(call,
true /* includeVideoProvider */);
try {
inCallService.addCall(parcelableCall);
} catch (RemoteException ignored) {
}
}
}
}
41、bindToServices – bindToInCallService
InCallServiceConnection inCallServiceConnection = new InCallServiceConnection();
private class InCallServiceConnection implements ServiceConnection {
/** {@inheritDoc} */
@Override public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(this, "onServiceConnected: %s", name);
onConnected(name, service);
}
42 、
private void onConnected(ComponentName componentName, IBinder service) {
addCall(call);
}
43. framworks/base/telecomm/java/android/telecom/InCallService.java
作用:這個服務可以被任何希望提供管理電話的用戶界面的應用實現,
當那個服務存在一個電話telecom就去綁定這個服務並用它去通知任何活動狀態和最近斷開的呼叫的被調用的應用
@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();
}
44. 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;
45. 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);
}
45.1 fireCallAdded()
private void fireCallAdded(Call call) {
for (Listener listener : mListeners) {
listener.onCallAdded(this, call);
}
}
46、onCallAdded()@SystemApi系統Api,一個空的實現方法 其他使用的地方會 @Override
@SystemApi
public final class Phone {
public abstract static class Listener {
...
public void onCallAdded(Phone phone, Call call) { }
47. pacakge/apps/InCallUI/src/com/android/incallui/CallList.java
作用:保持主動呼叫的列表和通知感興趣的類關於這個列表的變化,因為他們是從堆棧收到電話,
對這個類變化的主要聽眾是InCallPresenter
@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);
}
}
48、執行了下面的方法,但再往後的步驟不是從這裡走的。
onIncoming()
/**
* Called when a single call has changed.
*/
public void onIncoming(Call call, List textMessages) {
Log.d(this, "onIncoming - " + call);
// Update active subscription from call object. it will be set by
// Telecomm service for incoming call and whenever active sub changes.
if (call.mIsActiveSub) {
long sub = call.getSubId();
Log.d(this, "onIncoming - sub:" + sub + " mSubId:" + mSubId);
if (sub != mSubId) {
setActiveSubscription(sub);
}
}
if (updateCallInMap(call)) {
Log.i(this, "onIncoming - " + call);
}
updateCallTextMap(call, textMessages);
for (Listener listener : mListeners) {
listener.onIncomingCall(call);
}
}
49. //pacakge/apps/InCallUI/src/com/android/incallui/InCallPresenter.java
作用:接受來至CallList的更新並通知InCallActivity(UI)的變化。負責為一個新的呼叫啟動活動和當通話斷開時結束activity
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();
}
}
50、startUi
showInCall(false /* showDialpad */, !showAccountPicker /* newOutgoingCall */);
51、showInCall
public void showInCall(final boolean showDialpad, final boolean newOutgoingCall) {
Log.i(this, "Showing InCallActivity");
mContext.startActivity(getInCallIntent(showDialpad, newOutgoingCall));
}
總結
以下是MT流程的類圖,我們就根據這個圖做下最後總結
加粗 Ctrl + B 斜體 Ctrl + I 引用 Ctrl + Q 插入鏈接 Ctrl + L 插入代碼 Ctrl + K 插入圖片 Ctrl + G 提升標題 Ctrl + H 有序列表 Ctrl + O 無序列表 Ctrl + U 橫線 Ctrl + R 撤銷 Ctrl + Z 重做 Ctrl + Y
Markdown及擴展
Markdown 是一種輕量級標記語言,它允許人們使用易讀易寫的純文本格式編寫文檔,然後轉換成格式豐富的HTML頁面。 —— [ 維基百科 ]
使用簡單的符號標識不同的標題,將某些文字標記為粗體或者斜體,創建一個鏈接等,詳細語法參考幫助?。
本編輯器支持 Markdown Extra , 擴展了很多好用的功能。具體請參考Github.
表格
Markdown Extra 表格語法:
項目
價格
Computer
$1600
Phone
$12
Pipe
$1
可以使用冒號來定義對齊方式:
項目
價格
數量
Computer
1600 元
5
Phone
12 元
12
Pipe
1 元
234
定義列表
Markdown Extra 定義列表語法: 項目1 項目2 定義 A 定義 B 項目3 定義 C
定義 D
定義D內容
代碼塊
代碼塊語法遵循標准markdown代碼,例如:
@requires_authorization
def somefunc(param1='', param2=0):
'''A docstring'''
if param1 > param2: # interesting
print 'Greater'
return (param2 - param1 + 1) or None
class SomeClass:
pass
>>> message = '''interpreter
... prompt'''
腳注
生成一個腳注。
數學公式
使用MathJax渲染LaTex 數學公式.
行內公式,數學公式為:Γ(n)=(n?1)!?n∈N 。 塊級公式:
x=?b±b2?4ac???????√2a
UML 圖:
可以渲染序列圖:
或者流程圖:
離線寫博客
即使用戶在沒有網絡的情況下,也可以通過本編輯器離線寫博客(直接在曾經使用過的浏覽器中輸入write.blog.csdn.net/mdeditor即可。Markdown編輯器使用浏覽器離線存儲將內容保存在本地。
用戶寫博客的過程中,內容實時保存在浏覽器緩存中,在用戶關閉浏覽器或者其它異常情況下,內容不會丟失。用戶再次打開浏覽器時,會顯示上次用戶正在編輯的沒有發表的內容。
博客發表後,本地緩存將被刪除。
用戶可以選擇 把正在寫的博客保存到服務器草稿箱,即使換浏覽器或者清除緩存,內容也不會丟失。
注意:雖然浏覽器存儲大部分時候都比較可靠,但為了您的數據安全,在聯網後,請務必及時發表或者保存到服務器草稿箱。
浏覽器兼容
目前,本編輯器對Chrome浏覽器支持最為完整。建議大家使用較新版本的Chrome。 IE9以下不支持 IE9,10,11存在以下問題
不支持離線功能 IE9不支持文件導入導出 IE10不支持拖拽文件導入
這裡是 腳注 的 內容. ?
1、為什麼要有AIDL?為什麼要有AIDL呢,官方文檔介紹AIDL中有這麼一句話:Note: Using AIDL is necessary only ifyou all
以前看別人的程序的drawable文件夾裡有xml資源,說實話第一次見到這樣的xml圖像資源時,我真心不知道是干什麼的。抽空學習了一下圖像資源,才了解了這類圖像資源的妙用
最近軟文的東西寫的比較少,家裡的小寶寶(麥麥)咬毛了,在細心照料。外加最近玩WOW比較上頭,所以沒花大時間在寫文章上,今天看到個實現APP內提示跳轉應用市場的庫覺得還不錯
從Android的官方開發者博客找了一份幻燈片,介紹了一些Android UI設計,我們把這個教程整理出來,希望大家喜歡。為開發者,為啥我們要關心UI,前面