Android7.0 Phone應用源碼分析(二) phone來電流程分析

来源:http://www.cnblogs.com/lance2016/archive/2016/11/06/6035351.html
-Advertisement-
Play Games

Android7.0 Phone來電流程分析 --- 本文為原創文章,轉載請註明出處,http://www.cnblogs.com/lance2016/p/6035351.html ...


接上篇博文:Android7.0 Phone應用源碼分析(一) phone撥號流程分析

今天我們再來分析下Android7.0 的phone的來電流程

incoming

1.1TelephonyFramework

當有來電通知時,首先接收到消息的是Modem層,然後Medoem再上傳給RIL層,RIL進程通過sokcet將消息發送給RILJ(framework層的RIL),同樣進入RILJ的processResponse方法,根據上一章節去電流程的分析得知,來電屬於UnSolicited消息,事件ID是

RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED,看看RILJ里的處理

com.android.internal.telephony.RIL
processUnsolicited (Parcel p, int type) {
        ………………………………
   switch(response) {
        case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: ret =  responseVoid(p); 
        break;
        ………………………………
   }
        ………………………………
  switch(response) {
       case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
         if (RILJ_LOGD) unsljLog(response);
             mCallStateRegistrants .notifyRegistrants(new AsyncResult(null, null, null));
         break;
         ………………………………
    }
         ………………………………
}

mCallStateRegistrants是RegistrantList實例,這裡用到了觀察者模式,mCallStateRegistrants將call狀態的變化通知給了所有感興趣的註冊者,BaseCommands提供了相關註冊介面

com.android.internal.telephony. registerForCallStateChanged
 @Override
    public void registerForCallStateChanged(Handler h, int what, Object obj) {
        Registrant r = new Registrant (h, what, obj);

        mCallStateRegistrants.add(r);
    }
}

最後找到GsmCdmaCallTracker在創建的時候註冊了該事件

com.android.internal.telephony. GsmCdmaCallTracker
public GsmCdmaCallTracker (GsmCdmaPhone phone) {
        this.mPhone = phone;
        mCi = phone.mCi;
        mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
        mCi.registerForOn(this, EVENT_RADIO_AVAILABLE, null);
        mCi.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null);
        ...... ......

}

收到EVENT_CALL_STATE_CHANGE消息後進入pollCallsWhenSafe方法

protected void pollCallsWhenSafe() {
        mNeedsPoll = true;

        if (checkNoOperationsPending()) {
            mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
            mCi.getCurrentCalls(mLastRelevantPoll);
        }}
}

這裡的處理流程跟之前撥號類似,同樣是通過RILJ獲取當前call狀態,收到回應後進入handlePollCalls方法

protected synchronized void handlePollCalls(AsyncResult ar) {
      ………………………………
     if (newRinging != null) {          // 新來電通知
        mPhone.notifyNewRingingConnection(newRinging);
     }
       ………………………………
    updatePhoneState();                // 更新phone狀態
       ………………………………
  if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected)           {
      mPhone.notifyPreciseCallStateChanged();  // 發出call狀態變化通知

   }
}

新來電進入phone的notifyNewRingingConnection的方法

com.android.internal.telephony.Phone 
public void notifyNewRingingConnectionP(Connection cn) {
   if (!mIsVoiceCapable)
         return;
    AsyncResult ar = new AsyncResult(null, cn, null);
   mNewRingingConnectionRegistrants.notifyRegistrants(ar);}
}

又是一個觀察者模式,最後找到是註冊了該事件的監聽對象PstnIncomingCallNotifier

1.2TelephonyService

com.android.services.telephony. PstnIncomingCallNotifier
private void registerForNotifications() {
  if (mPhone != null) {
         Log.i(this, "Registering: %s", mPhone);
         mPhone.registerForNewRingingConnection(mHandler, EVENT_NEW_RINGING_CONNECTION, null);
         mPhone.registerForCallWaiting(mHandler, EVENT_CDMA_CALL_WAITING, null);
         mPhone.registerForUnknownConnection(mHandler, EVENT_UNKNOWN_CONNECTION, null);
        }
}

Handler處理消息進入handleNewRingingConnection方法

private void handleNewRingingConnection(AsyncResult asyncResult) {
        Log.d(this, "handleNewRingingConnection");
        Connection connection = (Connection) asyncResult.result;
        if (connection != null) {
            Call call = connection.getCall();

            // Final verification of the ringing state before sending the intent to Telecom.
            if (call != null && call.getState().isRinging()) {
                sendIncomingCallIntent(connection);
            }
        }
 }

獲取到call對象以後,最後進入sendIncomingCallIntent

private void sendIncomingCallIntent(Connection connection) {    
       ………………………………
        PhoneAccountHandle handle = findCorrectPhoneAccountHandle();
        if (handle == null) {
            try {
                connection.hangup();
            } catch (CallStateException e) {
                // connection already disconnected. Do nothing
            }
        } else {
           TelecomManager.from(mPhone.getContext()).addNewIncomingCall(handle, extras);
        }
  }

通過aidl介面調用telecomservice的的addNewIncomingCall方法

1.3 TelecomService

TelecomServiceImpl的成員變數mBinderImpl是具體實現類

com.android.server.telecom.TelecomServiceImpl
private final ITelecomService.Stub mBinderImpl = new ITelecomService.Stub(){
public void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
   ………………………………
   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) { 
           extras.setDefusable(true);
           intent.putExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras);
    }
    mCallIntentProcessorAdapter.processIncomingCallIntent(mCallsManager, intent);

}
    ………………………………

}

這裡調用到的是CallIntentProcessor 的processIncomingCallIntent方法

com.android.server.telecom. CallIntentProcessor
static void processIncomingCallIntent(CallsManager callsManager, Intent intent) {
       ………………………………
        Log.d(CallIntentProcessor.class,
                "Processing incoming call from connection service [%s]",
                phoneAccountHandle.getComponentName());
        callsManager.processIncomingCallIntent(phoneAccountHandle, clientExtras);
}

進入callsmanager的processIncomingCallIntent方法

void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
       ………………………………
        Call call = new Call(
                getNextCallId(),
                mContext,
                this,
                mLock,
                mConnectionServiceRepository,
                mContactsAsyncHelper,
                mCallerInfoAsyncQueryFactory,
                handle,
                null /* gatewayInfo */,
                null /* connectionManagerPhoneAccount */,
                phoneAccountHandle,
                Call.CALL_DIRECTION_INCOMING /* callDirection */,
                false /* forceAttachToExistingConnection */,
                false /* isConference */
        );
        ………………………………
        call.addListener(this);
        call.startCreateConnection(mPhoneAccountRegistrar);
    }

走到這一步,跟之前分析的撥號流程一樣,創建了一個call對象,然後調用

startCreateConnection創建連接,根據之前撥號的流程分析最後會進入 ConnectionService的createConnection方法

1.4 TelecommFramework

再把實現代碼貼一遍:

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(
                callId,
                request,
                new ParcelableConnection(
                        request.getAccountHandle(),
                        connection.getState(),
                        connection.getConnectionCapabilities(),
                        connection.getConnectionProperties(),
                        connection.getAddress(),
                        connection.getAddressPresentation(),
                        connection.getCallerDisplayName(),
                        connection.getCallerDisplayNamePresentation(),
                        connection.getVideoProvider() == null ?
                                null : connection.getVideoProvider().getInterface(),
                        connection.getVideoState(),
                        connection.isRingbackRequested(),
                        connection.getAudioModeIsVoip(),
                        connection.getConnectTimeMillis(),
                        connection.getStatusHints(),
                        connection.getDisconnectCause(),
                        createIdList(connection.getConferenceables()),
                        connection.getExtras(),
                        connection.getUserData()));//MOTO Calling Code - IKPIM-1774 (ftr 33860)
        if (isUnknown) {
            triggerConferenceRecalculate();
        }
}

這裡由於是來電,所以調用onCreateIncomingConnection方法,該方法同樣返回null,所以具體是由其子類實現的,也就是TelephonyConnectionService

public Connection onCreateIncomingConnection(
            PhoneAccountHandle connectionManagerPhoneAccount,
            ConnectionRequest request) {
       
    
        Connection connection =  createConnectionFor(phone, originalConnection, false /* isOutgoing */,
                        request.getAccountHandle(), request.getTelecomCallId(),
                        request.getAddress());
        if (connection == null) {
            return Connection.createCanceledConnection();
        } else {
            return connection;
        }
  }

最後根據GMS或是CDMA返回對應Connection對象,最後進入ConnectionServiceAdapter處理

android.telecom. ConnectionServiceAdapter
  void handleCreateConnectionComplete(
            String id,
            ConnectionRequest request,
            ParcelableConnection connection) {
        for (IConnectionServiceAdapter adapter : mAdapters) {
            try {
                adapter.handleCreateConnectionComplete(id, request, connection);
            } catch (RemoteException e) {
            }
        }
}

這裡的adapter實際上就是ConnectionServiceWrapper的內部類Adapter,需要註意的是之前撥號的時候創建完connection並呼出之後,後續也會走到這個流程里

public void handleCreateConnectionComplete(String callId, ConnectionRequest request,
                ParcelableConnection connection) {
            Log.startSession("CSW.hCCC");
            long token = Binder.clearCallingIdentity();
            try {
                synchronized (mLock) {
                    logIncoming("handleCreateConnectionComplete %s", callId);
                    ConnectionServiceWrapper.this
                       handleCreateConnectionComplete(callId, request, connection);
                }
            } finally {
                Binder.restoreCallingIdentity(token);
                Log.endSession();
            }
}

最後進入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);
            }
        }
}

這裡的mPendingResponses是map容器

private final Map<String, CreateConnectionResponse> mPendingResponses = new HashMap<>();

ConnectionServiceWrapper在調用createConnection的時候會往該容器里添加對象,也就是CreateConnectionProcessor對象

public void handleCreateConnectionSuccess(
            CallIdMapper idMapper,
            ParcelableConnection connection) {
        if (mCallResponse == null) {
            // Nobody is listening for this connection attempt any longer; ask the responsible
            // ConnectionService to tear down any resources associated with the call
            mService.abort(mCall);
        } else {
            // Success -- share the good news and remember that we are no longer interested
            // in hearing about any more attempts
            mCallResponse.handleCreateConnectionSuccess(idMapper, connection);
            mCallResponse = null;
            // If there's a timeout running then don't clear it. The timeout can be triggered
            // after the call has successfully been created but before it has become active.
        }
}

這個mCallResponse是CreateConnectionProcessor創建的時候引入的,也就是call對象

com.android.server.telecom.Call
   public void handleCreateConnectionSuccess(
            CallIdMapper idMapper,
            ParcelableConnection connection) {
     
        switch (mCallDirection) {
            case CALL_DIRECTION_INCOMING:
      
                for (Listener l : mListeners) {
                    l.onSuccessfulIncomingCall(this);
                }
                break;
            case CALL_DIRECTION_OUTGOING:
                for (Listener l : mListeners) {
                    l.onSuccessfulOutgoingCall(this,
                            getStateFromConnectionState(connection.getState()));
                }
                break;
            case CALL_DIRECTION_UNKNOWN:
                for (Listener l : mListeners) {
                      l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection .getState()));
                }
                break;
        }
    }

這裡根據是來電還是去電類型,執行相應回調,監聽者會收到通知,來電事件則觸發onSuccessfulIncomingCall的回調

1.5 TelecommService

前面提到CallsManager在執行processIncomingCallIntent方法時候會創建call,之後就會給call添加監聽,所以最後會回調到CallsManager類

public void onSuccessfulIncomingCall(Call incomingCall) {
        Log.d(this, "onSuccessfulIncomingCall");
        List<IncomingCallFilter.CallFilter> filters = new ArrayList<>();
        filters.add(new DirectToVoicemailCallFilter(mCallerInfoLookupHelper));
        filters.add(new 
AsyncBlockCheckFilter
(mContext, new BlockCheckerAdapter()));
        filters.add(new CallScreeningServiceFilter(mContext, this, mPhoneAccountRegistrar,
                mDefaultDialerManagerAdapter,
                new ParcelableCallUtils.Converter(), mLock));
        new 
IncomingCallFilter
(mContext, this, incomingCall, mLock,
                mTimeoutsAdapter, filters).performFiltering();
}

這裡用到了一個迭代器模式,一個來電操作觸發這三個對象的處理:

DirectToVoicemailCallFilter,AsyncBlockCheckFilter,CallScreeningServiceFilter

生成一個IncomingCallFilter對象,調用performFiltering方法

com.android.server.telecom.callfiltering. IncomingCallFilter
 public void performFiltering() {
        Log.event(mCall, Log.Events.FILTERING_INITIATED);
        for (CallFilter filter : mFilters) {
            filter.startFilterLookup(mCall, this);
        }
        mHandler.postDelayed(new Runnable("ICF.pFTO") { // performFiltering time-out
            @Override
            public void loggedRun() {
                // synchronized to prevent a race on mResult and to enter into Telecom.
                synchronized (mTelecomLock) {
                    if (mIsPending) {
                        Log.i(IncomingCallFilter.this, "Call filtering has timed out.");
                        Log.event(mCall, Log.Events.FILTERING_TIMED_OUT);
                        mListener.onCallFilteringComplete(mCall, mResult);
                        mIsPending = false;
                    }
                }
            }
   }.prepare(), mTimeoutsAdapter.getCallScreeningTimeoutMillis(mContext.getContentResolver()));
}

他們依次執行startFilterLookup非同步查詢方法,通過回調方法並將結果CallFilteringResult傳回onCallFilteringComplete將CallFilteringResult對象傳遞迴來

public class CallFilteringResult {

    public boolean shouldAllowCall;          // 是否允許通話
    public boolean shouldReject;            // 是否拒接
    public boolean shouldAddToCallLog;     // 是否添加至通話記
    public boolean shouldShowNotification; // 是否顯示通知欄消息
   ………………………………
   ………………………………
}

public void onCallFilteringComplete(Call call, CallFilteringResult result) {
        synchronized (mTelecomLock) { // synchronizing to prevent race on mResult
            mNumPendingFilters--;
            mResult = result.combine(mResult);
            if (mNumPendingFilters == 0) {
                mHandler.post(new Runnable("ICF.oCFC") {
                    @Override
                    public void loggedRun() {
                        // synchronized to enter into Telecom.
                        synchronized (mTelecomLock) {
                            if (mIsPending) {
                                mListener.onCallFilteringComplete(mCall, mResult);
                                mIsPending = false;
                            }
                        }
                    }
                }.prepare());
            }
        }
    }

先看看DirectToVoicemailCallFilter對象,它處理的是voicemail相關信息,

實際上是由CallerInfoLookupHelper完成查詢的,內部調用CallerInfoAsyncQueryFactory的startQuery方法,而CallerInfoAsyncQueryFactory是個介面類,在CallsManager創建的時候由外部傳參進來,最後找到是TelecomService的initializeTelecomSystem里創建的

com.android.server.telecom.components. TelecomService
static void initializeTelecomSystem(Context context) {
   new CallerInfoAsyncQueryFactory() {
            @Override
           public CallerInfoAsyncQuery startQuery(
                 int token, Context context, String number, 
                 CallerInfoAsyncQuery.OnQueryCompleteListener listener,
                 Object cookie) {
                     Log.i(TelecomSystem.getInstance(),
                     "CallerInfoAsyncQuery.startQuery number=%s cookie=%s",
                     Log.pii(number), cookie);
                     return CallerInfoAsyncQuery.startQuery(
                     token, context, number, listener, cookie);
           }
 }

進入CallerInfoAsyncQuery的startQuery方法

com.android.internal.telephony.CallerInfoAsyncQuery
public static CallerInfoAsyncQuery startQuery(int token, Context context, String number,
            OnQueryCompleteListener listener, Object cookie, int subId) {

 
 final Uri contactRef = PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI.buildUpon()
          .appendPath(number)
          .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS,
          String.valueOf(PhoneNumberUtils.isUriNumber(number)))
          .build();
}

查詢的uri是PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI

返回cursor結果集後轉化成CallerInfo,其中shouldSendToVoicemail變數查詢的

是PhoneLookup.SEND_TO_VOICEMAIL欄位

public static CallerInfo getCallerInfo(Context context, Uri contactRef, Cursor cursor) {
        ...... ......
        ...... ......
        columnIndex = cursor.getColumnIndex(PhoneLookup.SEND_TO_VOICEMAIL);
                info.shouldSendToVoicemail = (columnIndex != -1) &&
                        ((cursor.getInt(columnIndex)) == 1);
                info.contactExists = true;
        ...... ......
}

最後回到DirectToVoicemailCallFilter的查詢回調,shouldSendToVoicemail為true時表示允許通話,否則是拒接

if (info.shouldSendToVoicemail) {
    result = new CallFilteringResult(
                       false, // shouldAllowCall
                        true, // shouldReject
                        true, // shouldAddToCallLog
                        true // shouldShowNotification
                         );
    } else {
            result = new CallFilteringResult(
                         true, // shouldAllowCall
                           false, // shouldReject
                          true, // shouldAddToCallLog
                          true // shouldShowNotification
                          );
     }
}

再看看AsyncBlockCheckFilter,它處理的是黑名單事件

判斷一個電話號碼是否在黑名單里調用到了BlockChecker的isBlocked方法

com.android.internal.telephony. BlockChecker
public static boolean isBlocked(Context context, String phoneNumber) {
        boolean isBlocked = false;
        long startTimeNano = System.nanoTime();

        try {
            if (BlockedNumberContract.SystemContract.shouldSystemBlockNumber(
                    context, phoneNumber)) {
                Rlog.d(TAG, phoneNumber + " is blocked.");
                isBlocked = true;
            }
        } catch (Exception e) {
            Rlog.e(TAG, "Exception checking for blocked number: " + e);
        }

        int durationMillis = (int) ((System.nanoTime() - startTimeNano) / 1000000);
        if (durationMillis > 500 || VDBG) {
            Rlog.d(TAG, "Blocked number lookup took: " + durationMillis + " ms.");
        }
        return isBlocked;
    }

BlockedNumberContract.SystemContract是framework里的一個黑名單協議類

public static final String AUTHORITY = "com.android.blockednumber";
public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = 
"should_system_block_number";

public static boolean shouldSystemBlockNumber(Context context, String phoneNumber) {
            final Bundle res = context.getContentResolver().call(
                  AUTHORITY_URI, METHOD_SHOULD_SYSTEM_BLOCK_NUMBER, phoneNumber, null);
            return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
}

黑名單是BlockedNumberProvider資料庫, 調用call方法

com.android.providers.blockednumber. BlockedNumberProvider
@Override
public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
        final Bundle res = new Bundle();
        switch (method) {
                case  SystemContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER:
                    enforceSystemReadPermissionAndPrimaryUser();
                    res.putBoolean(
                    BlockedNumberContract.RES_NUMBER_IS_BLOCKED, shouldSystemBlockNumber(arg));
                break;
                ............
                ............
        }    
        
}

private boolean shouldSystemBlockNumber(String phoneNumber) {
    if (getBlockSuppressionStatus().isSuppressed) {
         return false;
    }
    if (isEmergencyNumber(phoneNumber)) {
        return false;
    }
    
    return isBlocked(phoneNumber);
}

最後調用isBlocked方法查詢blocked表中是否存在該number

查詢得到結果後返回

CallFilteringResult result;
        if (isBlocked) {
            result = new CallFilteringResult(
                    false, // shouldAllowCall
                    true, //shouldReject
                    false, //shouldAddToCallLog
                    false // shouldShowNotification
            );
        } else {
            result = new CallFilteringResult(
                    true, // shouldAllowCall
                    false, // shouldReject
                    true, // shouldAddToCallLog
                    true // shouldShowNotification
            );
        }

如果號碼在黑名單里則攔截

最後是CallScreeningServiceFilter不知道是處理什麼,內部綁定一個抽象服務

CallScreeningService但是卻找不到哪個子類繼承了它,這裡先忽略它

回到前面IncomingCallFilter的onCallFilteringCompletev方法,結果集會做個邏輯運算

mResult = result.combine(mResult);看看它的實現

com.android.server.telecom.callfilteringCallFilteringResult
public CallFilteringResult combine(CallFilteringResult other) {
        if (other == null) {
            return this;
        }

        return new CallFilteringResult(
                shouldAllowCall && other.shouldAllowCall,
                shouldReject || other.shouldReject,
                shouldAddToCallLog && other.shouldAddToCallLog,
                shouldShowNotification && other.shouldShowNotification);
    }

只有三個過濾結果都是允許通話才允許通話,添加至通話記錄以及是否顯示到通知欄同理

當然這裡的查詢操作也有超時限制,時間是5秒,超過5秒後忽略還未查詢到的過濾器則被忽略mTimeoutsAdapter.getCallScreeningTimeoutMillis(mContext.getContentResolver()));

最後過濾結果被回調到CallsManager的onCallFilteringComplete

public void onCallFilteringComplete(Call incomingCall, CallFilteringResult result) {
   ………………………………
   if (incomingCall.getState() != CallState.DISCONNECTED &&
     incomingCall.getState() != CallState.DISCONNECTING) {
            setCallState(incomingCall, CallState.RINGING,
              result.shouldAllowCall 
? "successful incoming call" : "blocking call"
);
    } else {
       Log.i(this, "onCallFilteringCompleted: call already disconnected.");
    }

        if (result.shouldAllowCall) {
            if (hasMaximumRingingCalls()) {
                rejectCallAndLog(incomingCall);
            } else if (hasMaximumDialingCalls()) {
                rejectCallAndLog(incomingCall);
            } else {
                addCall(incomingCall);
            }
        } else {
            if (result.shouldReject) {
                incomingCall.reject(false, null);
            }
            if (result.shouldAddToCallLog) {
     
                mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE,
                        result.shouldShowNotification);
            } else if (result.shouldShowNotification) {

                mMissedCallNotifier.showMissedCallNotification(incomingCall);
            }
        }
    }

調用setCallState設置通話狀態為CallState.RINGING,接著判斷是否拒接,是否寫入通話記錄等, 正常情況下調用addCall方法

private void addCall(Call call) {
       ………………………………
        updateCallsManagerState();
        // onCallAdded for calls which immediately take the foreground (like the first call).
        for (CallsManagerListener listener : mListeners) {
            if (Log.SYSTRACE_DEBUG) {
                Trace.beginSection(listener.getClass().toString() + " addCall");
            }
            listener.onCallAdded(call);
            if (Log.SYSTRACE_DEBUG) {
                Trace.endSection();
            }
        }
        Trace.endSection();
    }

遍歷回調監聽者的onCallAdded方法,InCallController是其中一個觀察者,看看它的實現

com.android.server.telecom. InCallController
@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<ComponentName, IInCallService> entry : mInCallServices.entrySet()) {
                ComponentName componentName = entry.getKey();
                IInCallService inCallService = entry.getValue();
                ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(call,
                  true,mCallsManager.getPhoneAccountRegistrar());
                try {
                    inCallService.addCall(parcelableCall);
                } catch (RemoteException ignored) {
                }
            }
        }
    }

這裡的inCallService是個aidl介面,抽象服務InCallService的嵌套類InCallServiceBinder 實現了該介面

1.6 TelecommFramework

android.telecom. InCallService
private final class InCallServiceBinder extends IInCallService.Stub {
    @Override
        public void addCall(ParcelableCall call) {
            mHandler.obtainMessage(MSG_ADD_CALL, call).sendToTarget();
        }
    ...... ......
    ...... ......
}

handle對象處理消息MSG_ADD_CALL

private final Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
                case MSG_ADD_CALL:
                    mPhone.internalAddCall((ParcelableCall) msg.obj);
                    break;
     }
}

candroid.telecom.Phone
final void internalAddCall(ParcelableCall parcelableCall) {
        Call call 
= new Call(this
, parcelableCall.getId(), mInCallAdapter,
                parcelableCall.getState());
        mCallByTelecomCallId.put(parcelableCall.getId(), call);
        mCalls.add(call);
        checkCallTree(parcelableCall);
        call.internalUpdate(parcelableCall, mCallByTelecomCallId);
        fireCallAdded(call);
 }

 private void fireCallAdded(Call call) {
        for (Listener listener : mListeners) {
            listener.onCallAdded(this, call);
        }
  }

mPhone對象內部新建了一個call對象,獲取並轉化ParcelableCall的相關信息,並將call對象加入列表,最後回調Phone的監聽者的onCallAdded方法,這裡就是InCallService的

mPhoneListener成員變數

private Phone.Listener mPhoneListener = new Phone.Listener() {
        /** ${inheritDoc} */
          ...... ......


        /** ${inheritDoc} */
        @Override
        public void onCallAdded(Phone phone, Call call) {
            InCallService.this.onCallAdded(call);
        }
        ...... ......
    };

這裡InCallService的onCallAdded方法是一個抽象方法,具體實現在它的子類

1.7 Dialer

InCallServiceImpl繼承了InCallService,該服務在Dialer工程的manifest文件里有聲明

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Intent是 Android中重要的橋梁之一,它分為顯式意圖和隱式意圖。接下來分別針對這兩種意圖進行講解。 顯式意圖:通過指定一組數據或動作,激活應用內部的 activity;(相比隱式意圖,此做法更安全) 隱式意圖:通過指定具體的包名和類名,打開另一個程式的 Activity。 Android數 ...
  • http://www.jxedt.com/wen/quzheng/3174962940997926927.html http://www.jxedt.com/wen/quzheng/3174962947299344407.html http://www.jxedt.com/wen/quzheng/3 ...
  • 英語學習/詞典App行業Top5的分析: a:樂詞新東方背單詞(以下簡稱樂詞) 樂詞是一款很不錯的背單詞軟體。它含有全面的富有權威的單詞內容,又有新東方提供的將此視頻,能夠為用戶講授生動有趣的單詞,方便 記憶和理解。下麵就舉出一些功能來證明這些吧: 1、樂詞提供不同的用戶註冊和登陸功能,還開放了游客 ...
  • 團隊項目:井字棋游戲 我們對本次項目的一些看法: a:我們這次的項目,在現有井字棋游戲的基礎上進行了一次創新,讓用戶體驗到一種全新的游戲方式,使用戶眼前一亮,重拾這個游戲的興趣,又能吸引初學者的眼球。我們的創新點在於對游戲的游戲模式進行了創新和改進,使用九個小棋盤替代原有的一個棋盤,不論從游戲的視覺 ...
  • 完成狀態 編輯狀態 1_設置點擊事件和定義狀態 在GovaffairPager類中 2_在適配器中刪除選中的item 在GovaffairPager類中 在適配器中的代碼 ...
  • 1_商品總價格計算 ①在GovaffairPager類中設置 ②GovaffairPagerAdapter 2_增加商品或者減少商品的時候計算總價格 3_設置點擊某一條item 1_先定義介面和調用 2_調用介面 3_在構造方法中設置監聽 4_全選和反選 ...
  • 1_購物車頁面和標題欄的設置 govaffair_pager.xml 2_設置適配器 ...
  • 1_創建購物車類ShoppingCart 作用:購物車類繼承Wares,記錄某個商品在購物車中的狀態,例如有多少個商品,是否選中 2_創建數據存儲類CartProvider 作用:數據存儲類,存儲數據;存儲數據--把集合轉成String類型存儲(Gson);取數據--把String轉換成列表數據(G ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...