Android 7.0 去電流程

aini520vsa發表於2017-08-07

撥號的入口在TelecomManager的placeCall的方法,該方法實際呼叫TelecomServiceImpl的placeCall(),接著呼叫UserCallIntentProcessor的processIntent()方法

TelecomServiceImpl->placeCall()

public void placeCall(Uri handle, Bundle extras, String callingPackage) {
            try {

            ...

                synchronized (mLock) {
                    final UserHandle userHandle = Binder.getCallingUserHandle();
                    long token = Binder.clearCallingIdentity();
                    try {
                        final Intent intent = new Intent(Intent.ACTION_CALL, handle);
                        if (extras != null) {
                            extras.setDefusable(true);
                            intent.putExtras(extras);
                        }
                        mUserCallIntentProcessorFactory.create(mContext, userHandle)
                                .processIntent(
                                        intent, callingPackage, hasCallAppOp && hasCallPermission);//mUserCallIntentProcessorFactory是在TelecomSystem中初始化的
                    } finally {
                        Binder.restoreCallingIdentity(token);
                    }
                }
            } finally {
                Log.endSession();
            }
        }

UserCallIntentProcessor又在process(),UserCallIntentProcessor中方執行順序為
process()->processOutgoingCallIntent()->sendBroadcastToReceiver():

UserCallIntentProcessor->process():

public void processIntent(Intent intent, String callingPackageName,
            boolean canCallNonEmergency) {
        // Ensure call intents are not processed on devices that are not capable of calling.
        if (!isVoiceCapable()) {
            return;
        }

        String action = intent.getAction();

        if (Intent.ACTION_CALL.equals(action) ||
                Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
                Intent.ACTION_CALL_EMERGENCY.equals(action)) {
            processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency);
        }
    }

UserCallIntentProcessor->processOutgoingCallIntent()

private void processOutgoingCallIntent(Intent intent, String callingPackageName,
            boolean canCallNonEmergency) {

    ...

        int videoState = intent.getIntExtra(
                TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                VideoProfile.STATE_AUDIO_ONLY);
        Log.d(this, "processOutgoingCallIntent videoState = " + videoState);

        intent.putExtra(CallIntentProcessor.KEY_IS_PRIVILEGED_DIALER,
                isDefaultOrSystemDialer(callingPackageName));

        // Save the user handle of current user before forwarding the intent to primary user.
        intent.putExtra(CallIntentProcessor.KEY_INITIATING_USER, mUserHandle);

        sendBroadcastToReceiver(intent);
    }

UserCallIntentProcessor->sendBroadcastToReceiver():

private boolean sendBroadcastToReceiver(Intent intent) {
        intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);
        intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        intent.setClass(mContext, PrimaryCallReceiver.class);
        Log.d(this, "Sending broadcast as user to CallReceiver");
        mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
        return true;
    }

UserCallIntentProcessor給PrimaryCallReceiver傳送廣播後, PrimaryCallReceiver在onReceive()實際上就了呼叫CallIntentProcessor的processIntent的方法,接著呼叫CallsManager的startOutgoingCall 初始化Call,然後NewOutgoingCallIntentBroadcaster的processIntent()方法:

public void onReceive(Context context, Intent intent) {
        synchronized (getTelecomSystem().getLock()) {
            getTelecomSystem().getCallIntentProcessor().processIntent(intent);
        }
    }

在CallIntentProcessor的processIntent方法中呼叫processOutgoingCallIntent()方法。
CallIntentProcessor->processOutgoingCallIntent():

static void processOutgoingCallIntent(
            Context context,
            CallsManager callsManager,
            Intent intent) {

            ...


        Call call = callsManager
                .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser);//初始化call及啟動InCallUI

        if (call != null) {
            // Asynchronous calls should not usually be made inside a BroadcastReceiver because once
            // onReceive is complete, the BroadcastReceiver's process runs the risk of getting
            // killed if memory is scarce. However, this is OK here because the entire Telecom
            // process will be running throughout the duration of the phone call and should never
            // be killed.
            NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
                    context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(),
                    isPrivilegedDialer);
            final int result = broadcaster.processIntent();
            final boolean success = result == DisconnectCause.NOT_DISCONNECTED;

            if (!success && call != null) {
                callsManager.clearPendingMOEmergencyCall();
                disconnectCallAndShowErrorDialog(context, call, result);
            }
        }
}

NewOutgoingCallIntentBroadcaster ->processIntent():

public int processIntent() {

    ...

        if (callImmediately) {
            boolean speakerphoneOn = mIntent.getBooleanExtra(
                    TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
            int videoState = mIntent.getIntExtra(
                    TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                    VideoProfile.STATE_AUDIO_ONLY);
            // Since we will not start NewOutgoingCallBroadcastIntentReceiver in case of
            // callImmediately is true, make sure to mark it as ready, so that when user
            // selects account, call can go ahead in case of numbers which are potential emergency
            // but not actual emergeny.
            mCall.setNewOutgoingCallIntentBroadcastIsDone();
            mCallsManager.placeOutgoingCall(mCall, Uri.fromParts(scheme, number, null), null,
                    speakerphoneOn, videoState);
        }
    }

CallsManager->placeOutgoingCall():

        ...
        if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) {
            if (!call.isEmergencyCall()) {
                updateLchStatus(call.getTargetPhoneAccount().getId());
            }
                call.startCreateConnection(mPhoneAccountRegistrar);
            }
        } 
        ...

Call->startCreateConnection():

void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {

        ...

        mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
                phoneAccountRegistrar, mContext);
        mCreateConnectionProcessor.process();
    }

CreateConnectionProcessor->process():

public void process() {
        clearTimeout();
        mAttemptRecords = new ArrayList<>();
        if (mCall.getTargetPhoneAccount() != null) {
            mAttemptRecords.add(new CallAttemptRecord(
                    mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));
        }
        adjustAttemptsForConnectionManager();
        adjustAttemptsForEmergency(mCall.getTargetPhoneAccount());
        mAttemptRecordIterator = mAttemptRecords.iterator();
        attemptNextPhoneAccount();
    }

CreateConnectionProcessor->attemptNextPhoneAccount()

 private void attemptNextPhoneAccount() {

 ...

  if (mCallResponse != null && attempt != null) {
            Log.i(this, "Trying attempt %s", attempt);
            PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;
            mService = mRepository.getService(phoneAccount.getComponentName(),
                    phoneAccount.getUserHandle());
            if (mService == null) {
                Log.i(this, "Found no connection service for attempt %s", attempt);
                attemptNextPhoneAccount();//遞迴
            } else {
                mConnectionAttempt++;
                mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);
                mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
                mCall.setConnectionService(mService);
                setTimeoutIfNeeded(mService, attempt);

                mService.createConnection(mCall, this);
            }
        }
    }

ConnectionServiceWrapper->createConnection():

public 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.getIntentExtras();
                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());
                }

                Log.event(call, Log.Events.START_CONNECTION, Log.piiHandle(call.getHandle()));
                try {
                    mServiceInterface.createConnection(
                            call.getConnectionManagerPhoneAccount(),
                            callId,
                            new ConnectionRequest(
                                    call.getTargetPhoneAccount(),
                                    call.getHandle(),
                                    extras,
                                    call.getVideoState(),
                                    callId),
                            call.shouldAttachToExistingConnection(),
                            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, call);//回撥
    }

ConnetctionService->createConnection():

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

                ...

         }

onCreateOutgoingConnection是在ConnetctionService的子類TelephonyConnectionService中實現的
TelephonyConnectionService->onCreateOutgoingConnection():

 public Connection onCreateOutgoingConnection(
            PhoneAccountHandle connectionManagerPhoneAccount,
            final ConnectionRequest request) {

            ...

            final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber);
            Connection resultConnection = getTelephonyConnection(request, numberToDial,
                    isEmergencyNumber, handle, phone);
            // If there was a failure, the resulting connection will not be a TelephonyConnection,
            // so don't place the call!
            if(resultConnection instanceof TelephonyConnection) {
                placeOutgoingConnection((TelephonyConnection) resultConnection, phone, request);
            }
            return resultConnection;
        }

TelephonyConnectionService->placeOutgoingConnection():

private void placeOutgoingConnection(
            TelephonyConnection connection, Phone phone, int videoState, Bundle extras,
            ConnectionRequest request) {

            ...

        String number = connection.getAddress().getSchemeSpecificPart();
com.android.internal.telephony.Connection originalConnection = null;
        try {
            if (phone != null) {
                if (isAddParticipant) {
                    phone.addParticipant(number);
                    return;
                } else {
                    originalConnection = phone.dial(number, null, request.getVideoState(), bundle);
                }
            }
        } catch (CallStateException e) {
            Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e);
            int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
            if (e.getError() == CallStateException.ERROR_DISCONNECTED) {
                cause = android.telephony.DisconnectCause.OUT_OF_SERVICE;
            } else if (e.getError() == CallStateException.ERROR_POWER_OFF) {
                cause = android.telephony.DisconnectCause.POWER_OFF;
            }
            connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
                    cause, e.getMessage()));
            return;
        }
    }

GsmCdmaPhone->dial():

public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
            throws CallStateException {

            ...
        if (isPhoneTypeGsm()) {
            return dialInternal(dialString, null, VideoProfile.STATE_AUDIO_ONLY, intentExtras);
        } else {
            return dialInternal(dialString, null, videoState, intentExtras);
        }
    }

GsmCdmaPhone->dialInternal():

protected Connection dialInternal(String dialString, UUSInfo uusInfo, int videoState,
                                      Bundle intentExtras)
            throws CallStateException {

        // Need to make sure dialString gets parsed properly
        String newDialString = PhoneNumberUtils.stripSeparators(dialString);

        if (isPhoneTypeGsm()) {
            // handle in-call MMI first if applicable
            if (handleInCallMmiCommands(newDialString)) {
                return null;
            }

            // Only look at the Network portion for mmi
            String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
            GsmMmiCode mmi =
                    GsmMmiCode.newFromDialString(networkPortion, this, mUiccApplication.get());
            if (DBG) logd("dialing w/ mmi '" + mmi + "'...");

            if (mmi == null) {
                return mCT.dial(newDialString, uusInfo, intentExtras);
            } else if (mmi.isTemporaryModeCLIR()) {
                return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo, intentExtras);
            } else {
                mPendingMMIs.add(mmi);
                mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
                try {
                    mmi.processCode();
                } catch (CallStateException e) {
                    //do nothing
                }

                // FIXME should this return null or something else?
                return null;
            }
        } else {
            return mCT.dial(newDialString);
        }
    }

GsmCdmaCallTracker->dial():

public synchronized Connection dial(String dialString, int clirMode, UUSInfo uusInfo,
                                        Bundle intentExtras)
            throws CallStateException {

            ...
        if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0
                || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) {
            // Phone number is invalid
            mPendingMO.mCause = DisconnectCause.INVALID_NUMBER;

            // handlePollCalls() will notice this call not present
            // and will mark it as dropped.
            pollCallsWhenSafe();
        } else {
            // Always unmute when initiating a new call
            setMute(false);

            mCi.dial(mPendingMO.getAddress(), clirMode, uusInfo, obtainCompleteMessage());
        }

        if (mNumberConverted) {
            mPendingMO.setConverted(origNumber);
            mNumberConverted = false;
        }

        updatePhoneState();
        mPhone.notifyPreciseCallStateChanged();

        return mPendingMO;
    }

相關文章