欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android 8.0 MO 通话

程序员文章站 2022-06-09 23:15:34
...

 

本文是作为本人学习总结。

首先挂一下流程图。

android 8.0 MO 通话

 

android 8.0 MO 通话

 

通话相关界面的显示和控制从/packages/apps/Dialer目录下。

一   /packages/apps/Dialer 层

  dial 从DialpadFragment 开始,DialpadFragment 显示一个12键拨号键盘。

在handleDialButtonPressed 方法中, 检查号码是否是被禁止的号码, 然后进一步处理。

  

  public void onClick(View view) {
    int resId = view.getId();
    if (resId == R.id.dialpad_floating_action_button) {
      view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
      handleDialButtonPressed();
    // 点击delete 键
    } else if (resId == R.id.deleteButton) {
      keyPressed(KeyEvent.KEYCODE_DEL);
    //点击数字键
    } else if (resId == R.id.digits) {
      if (!isDigitsEmpty()) {
        mDigits.setCursorVisible(true);
      }
    } else if (resId == R.id.dialpad_overflow) {
      mOverflowPopupMenu.show();
    } else {
      LogUtil.w("DialpadFragment.onClick", "Unexpected event from: " + view);
      return;
    }
  }
    private void handleDialButtonPressed() {
    if (isDigitsEmpty()) { // No number entered.
        // 拨打最后通话的号码
        handleDialButtonClickWithEmptyDigits();
    } else {
      final String number = mDigits.getText().toString();

      // "persist.radio.otaspdial" is a temporary hack needed for one carrier's automated
      // test equipment.
      // TODO: clean it up.
	  // 检查是否是禁止拨出的号码
      if (number != null
          && !TextUtils.isEmpty(mProhibitedPhoneNumberRegexp)
          && number.matches(mProhibitedPhoneNumberRegexp)) {
        LogUtil.i(
            "DialpadFragment.handleDialButtonPressed",
            "The phone number is prohibited explicitly by a rule.");
        if (getActivity() != null) {
          DialogFragment dialogFragment =
              ErrorDialogFragment.newInstance(R.string.dialog_phone_call_prohibited_message);
          dialogFragment.show(getFragmentManager(), "phone_prohibited_dialog");
        }

        // Clear the digits just in case.
        clearDialpad();
      } else {
        final Intent intent =
            new CallIntentBuilder(number, CallInitiationType.Type.DIALPAD).build();
        DialerUtils.startActivityWithErrorToast(getActivity(), intent);
        hideAndClearDialpad(false); //隐藏拨号键盘
      }
    }
  }

看一下 call intent 的结构。

uri 是由String number 包装得到。

  public Intent build() {
    Intent intent = new Intent(Intent.ACTION_CALL, uri);
    // 是否视频通话
    intent.putExtra(
        TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
        isVideoCall ? VideoProfile.STATE_BIDIRECTIONAL : VideoProfile.STATE_AUDIO_ONLY);

    Bundle extras = new Bundle();
    extras.putLong(Constants.EXTRA_CALL_CREATED_TIME_MILLIS, SystemClock.elapsedRealtime());
    CallIntentParser.putCallSpecificAppData(extras, callSpecificAppData);
    intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);

    if (phoneAccountHandle != null) {
      intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
    }

    if (!TextUtils.isEmpty(callSubject)) {
      intent.putExtra(TelecomManager.EXTRA_CALL_SUBJECT, callSubject);
    }

    return intent;
  }

 

DialerUtils.java

    startActivityWithErrorToast 方法主要是将键盘的触点位置传入intent.

 

    public static void startActivityWithErrorToast(
        final Context context, final Intent intent, int msgId) {
        try {
        if ((Intent.ACTION_CALL.equals(intent.getAction()))) {
        // All dialer-initiated calls should pass the touch point to the InCallUI
	// 加入用户在键盘上的触点,后续启动incallUI的时候可能会使用到
        Point touchPoint = TouchPointManager.getInstance().getPoint();
        if (touchPoint.x != 0 || touchPoint.y != 0) {
          Bundle extras;
          // Make sure to not accidentally clobber any existing extras
          if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) {
            extras = intent.getParcelableExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
          } else {
            extras = new Bundle();
          }
          extras.putParcelable(TouchPointManager.TOUCH_POINT, touchPoint);
          intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
        }

	// wps 网络是美国的高优先级的网络,使得紧急通话不会被无线通话网络阻塞。
        if (shouldWarnForOutgoingWps(context, intent.getData().getSchemeSpecificPart())) {
          LogUtil.i(
              "DialUtils.startActivityWithErrorToast",
              "showing outgoing WPS dialog before placing call");
          AlertDialog.Builder builder = new AlertDialog.Builder(context);
          builder.setMessage(R.string.outgoing_wps_warning);
          builder.setPositiveButton(
              R.string.dialog_continue,
              new OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                  placeCallOrMakeToast(context, intent);
                }
              });
          builder.setNegativeButton(android.R.string.cancel, null);
          builder.create().show();
        } else {
          placeCallOrMakeToast(context, intent);
        }
      } else {
        context.startActivity(intent);
      }
    } catch (ActivityNotFoundException e) {
      Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show();
    }
  }
    private static void placeCallOrMakeToast(Context context, Intent intent) {
    final boolean hasCallPermission = TelecomUtil.placeCall(context, intent);
    if (!hasCallPermission) {
      // TODO: Make calling activity show request permission dialog and handle
      // callback results appropriately.
      Toast.makeText(context, "Cannot place call without Phone permission", Toast.LENGTH_SHORT)
          .show();
    }
  }

 

    public static boolean placeCall(Context context, Intent intent) {
    if (hasCallPhonePermission(context)) {
      getTelecomManager(context).placeCall(intent.getData(), intent.getExtras());
      return true;
    }
    return false;
  }

二 /packages/services/Telecomm 层

   /frameworks/base/telecomm/java/android/telecom/TelecomManager.java

    public void placeCall(Uri address, Bundle extras) {
        ITelecomService service = getTelecomService();
        if (service != null) {
            if (address == null) {
                Log.w(TAG, "Cannot place call to empty address.");
            }
            try {
                service.placeCall(address, extras == null ? new Bundle() : extras,
                        mContext.getOpPackageName());
            } catch (RemoteException e) {
                Log.e(TAG, "Error calling ITelecomService#placeCall", e);
            }
        }
    }

 

 

ITelecomService 在TelecomServiceImpl.java 中实现

/packages/services/Telecomm/src/com/android/server/telecom/TelecomServiceImpl.java

 public class TelecomServiceImpl {

    private static final int DEFAULT_VIDEO_STATE = -1;

    private final ITelecomService.Stub mBinderImpl = new ITelecomService.Stub() {
	
	......
	
	        /**
         * @see android.telecom.TelecomManager#placeCall
         */
        @Override
        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, isSelfManaged ||
                                                (hasCallAppOp && hasCallPermission));
                    } finally {
                        Binder.restoreCallingIdentity(token);
                    }
                }
            } finally {
                Log.endSession();
            }
        }

这里包含了/packages/services/Telecomm 暴露给外层的接口,比如acceptRingingCall,addNewIncomingCall,silenceRinger等方法。

/packages/services/Telecomm/src/com/android/server/telecom/components/UserCallIntentProcessor.java

processOutgoingCallIntent 将Intent 保存的data,Scheme 等信息重新取出并重新包装发送给PrimaryCallReceiver。

	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);
        }
    }
private void processOutgoingCallIntent(Intent intent, String callingPackageName,
            boolean canCallNonEmergency) {
        Uri handle = intent.getData();
        String scheme = handle.getScheme();
        // uriString 是电话号码
        String uriString = handle.getSchemeSpecificPart(); 

        //修正handle,判断是sip 通话还是tel 通话
        if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
            handle = Uri.fromParts(PhoneNumberUtils.isUriNumber(uriString) ?
                    PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL, uriString, null);
        }
        ......

        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.
		// 保存用户句柄mUserHandle
        intent.putExtra(CallIntentProcessor.KEY_INITIATING_USER, mUserHandle);

        sendBroadcastToReceiver(intent);
    }
   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;
    }

 

/packages/services/Telecomm/src/com/android/server/telecom/components/PrimaryCallReceiver.java

 

public class PrimaryCallReceiver extends BroadcastReceiver implements TelecomSystem.Component {

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.startSession("PCR.oR");
        synchronized (getTelecomSystem().getLock()) {
            getTelecomSystem().getCallIntentProcessor().processIntent(intent);
        }
        Log.endSession();
    }
}

 

/packages/services/Telecomm/src/com/android/server/telecom/CallIntentProcessor.java

CallIntentProcessor 和CallsManager是在TelecomService 启动时建立,TelecomService->TelecomSystem->CallsManager。

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

        Uri handle = intent.getData();
        String scheme = handle.getScheme();
        String uriString = handle.getSchemeSpecificPart();

        if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
            handle = Uri.fromParts(PhoneNumberUtils.isUriNumber(uriString) ?
                    PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL, uriString, null);
        }

        PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
                TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);

        Bundle clientExtras = null;
        if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) {
            clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
        }
        if (clientExtras == null) {
            clientExtras = new Bundle();
        }

        // Ensure call subject is passed on to the connection service.
        if (intent.hasExtra(TelecomManager.EXTRA_CALL_SUBJECT)) {
            String callsubject = intent.getStringExtra(TelecomManager.EXTRA_CALL_SUBJECT);
            clientExtras.putString(TelecomManager.EXTRA_CALL_SUBJECT, callsubject);
        }

        final int videoState = intent.getIntExtra( TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                VideoProfile.STATE_AUDIO_ONLY);
        clientExtras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);

        ......

        UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER);

        // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
        //根据handle 建立新的Call 对象,但是这时没有对Call 进行处理, 而是保存到Set<Call> callsManager.mCalls中.并启动InCallUi
        Call call = callsManager
                .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser,
                        intent);

        if (call != null) {
            sendNewOutgoingCallIntent(context, call, callsManager, intent);
        }
    }
    static void sendNewOutgoingCallIntent(Context context, Call call, CallsManager callsManager,
            Intent intent) {

        final boolean isPrivilegedDialer = intent.getBooleanExtra(KEY_IS_PRIVILEGED_DIALER, false);

        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) {
            disconnectCallAndShowErrorDialog(context, call, result);
        }
    }
 

CallsManager 去建立Call 对象。 CallsManager  负责对于Call 进行向下的控制, 同时监听Call 状态的改变, 并向上发出通知。

但是这里只是生成Call 对象, 还没有向下发出拨号请求。

/packages/services/Telecomm/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java

这里看到processIntent方法主要对三种类型call的一些检查, 
1.普通call Intent.ACTION_CALL 
普通call任何应用都可以发起,第三方应用拨号都是使用该intent 
2.系统call Intent.ACTION_CALL_PRIVILEGED 
系统call只有系统应用才能使用 
3.紧急呼叫call Intent.ACTION_CALL_EMERGENCY 
紧急呼叫call 同样只有系统应用才能使用,并且可以在无卡状态下拨.

 public int processIntent() {
        Log.v(this, "Processing call intent in OutgoingCallIntentBroadcaster.");

        Intent intent = mIntent;
        String action = intent.getAction();
        final Uri handle = intent.getData();

        if (handle == null) {
            Log.w(this, "Empty handle obtained from the call intent.");
            return DisconnectCause.INVALID_NUMBER;
        }

        boolean isVoicemailNumber = PhoneAccount.SCHEME_VOICEMAIL.equals(handle.getScheme());
        ......

        String number = mPhoneNumberUtilsAdapter.getNumberFromIntent(intent, mContext);
        if (TextUtils.isEmpty(number)) {
            Log.w(this, "Empty number obtained from the call intent.");
            return DisconnectCause.NO_PHONE_NUMBER_SUPPLIED;
        }

        boolean isUriNumber = mPhoneNumberUtilsAdapter.isUriNumber(number);
        if (!isUriNumber) {
            number = mPhoneNumberUtilsAdapter.convertKeypadLettersToDigits(number);
            number = mPhoneNumberUtilsAdapter.stripSeparators(number);
        }

        //重新验证紧急呼叫号码,重写action
        final boolean isPotentialEmergencyNumber = isPotentialEmergencyNumber(number);
        Log.v(this, "isPotentialEmergencyNumber = %s", isPotentialEmergencyNumber);

        rewriteCallIntentAction(intent, isPotentialEmergencyNumber);
        action = intent.getAction();
        // True for certain types of numbers that are not intended to be intercepted or modified
        // by third parties (e.g. emergency numbers).
        boolean callImmediately = false;

	//验证number和action是否匹配, 不匹配则cancle, 匹配则检验是否是紧急通话
        if (Intent.ACTION_CALL.equals(action)) {
            if (isPotentialEmergencyNumber) {
                if (!mIsDefaultOrSystemPhoneApp) {
                    Log.w(this, "Cannot call potential emergency number %s with CALL Intent %s "
                            + "unless caller is system or default dialer.", number, intent);
                    launchSystemDialer(intent.getData());
                    return DisconnectCause.OUTGOING_CANCELED;
                } else {
                    callImmediately = true;
                }
            }
        } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) {
            if (!isPotentialEmergencyNumber) {
                Log.w(this, "Cannot call non-potential-emergency number %s with EMERGENCY_CALL "
                        + "Intent %s.", number, intent);
                return DisconnectCause.OUTGOING_CANCELED;
            }
            callImmediately = true;
        } else {
            Log.w(this, "Unhandled Intent %s. Ignoring and not placing call.", intent);
            return DisconnectCause.INVALID_NUMBER;
        }

        //紧急呼叫处理,直接调用placeOutgoingCall
        if (callImmediately) {
            Log.i(this, "Placing call immediately instead of waiting for "
                    + " OutgoingCallBroadcastReceiver: %s", intent);
            String scheme = isUriNumber ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL;
            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);
            mCallsManager.placeOutgoingCall(mCall, Uri.fromParts(scheme, number, null), null,
                    speakerphoneOn, videoState);

            // Don't return but instead continue and send the ACTION_NEW_OUTGOING_CALL broadcast
            // so that third parties can still inspect (but not intercept) the outgoing call. When
            // the broadcast finally reaches the OutgoingCallBroadcastReceiver, we'll know not to
            // initiate the call again because of the presence of the EXTRA_ALREADY_CALLED extra.
        }

        UserHandle targetUser = mCall.getInitiatingUser();
        Log.i(this, "Sending NewOutgoingCallBroadcast for %s to %s", mCall, targetUser);
        broadcastIntent(intent, number, !callImmediately, targetUser);
        return DisconnectCause.NOT_DISCONNECTED;
    }

将Intent发送给NewOutgoingCallBroadcastIntentReceiver

  private void broadcastIntent(
            Intent originalCallIntent,
            String number,
            boolean receiverRequired,
            UserHandle targetUser) {
        Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
        if (number != null) {
            broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
        }

        // Force receivers of this broadcast intent to run at foreground priority because we
        // want to finish processing the broadcast intent as soon as possible.
        broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
        Log.v(this, "Broadcasting intent: %s.", broadcastIntent);

        checkAndCopyProviderExtras(originalCallIntent, broadcastIntent);

        mContext.sendOrderedBroadcastAsUser(
                broadcastIntent,
                targetUser,
                android.Manifest.permission.PROCESS_OUTGOING_CALLS,
                AppOpsManager.OP_PROCESS_OUTGOING_CALLS,
                receiverRequired ? new NewOutgoingCallBroadcastIntentReceiver() : null,
                null,  // scheduler
                Activity.RESULT_OK,  // initialCode
                number,  // initialData: initial value for the result data (number to be modified)
                null);  // initialExtras
    }
 public class NewOutgoingCallBroadcastIntentReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            try {
                ...... 
                    Uri resultHandleUri = Uri.fromParts(
                            mPhoneNumberUtilsAdapter.isUriNumber(resultNumber) ?
                                    PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL,
                            resultNumber, null);

                    Uri originalUri = mIntent.getData();
                    ......

                    GatewayInfo gatewayInfo = getGateWayInfoFromIntent(intent, resultHandleUri);
                    mCall.setNewOutgoingCallIntentBroadcastIsDone();
                    mCallsManager.placeOutgoingCall(mCall, resultHandleUri, gatewayInfo,
                            mIntent.getBooleanExtra(
                                    TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false),
                            mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                                    VideoProfile.STATE_AUDIO_ONLY));
                }
            } finally {
                Trace.endSection();
                Log.endSession();
            }
        }
    }

 

这时mCallsManager 的placeOutgoingCall 处理call.

 

这里主要是对Call 的状态进行一些判断和设置,比如紧急通话等, 然后call 去建立连接。

public void placeoutgoingCall(Call call, uri handle, GatewayInfo gatewayInfo,
        boolean speakerPhoneOn, int videostate) {
    final Uri uriHandle  = (gatewayInfo == null ) ? handle : gatewayInfo.getGatewayAddress();
    call.setHandle(uriHandle);
	call.setGatewayInfo(gatewayInfo);
    ......
	if(call.getTargetPhoneAccount() != null || call.isEmergencyCall()){
	    if (call.isSelfManaged() && !isOutgoingCallPermitted) {
            notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call);
        } else if (!call.isSelfManaged() && hasSelfManagedCalls() && !call.isEmergencyCall()) {
            markCallDisconnectedDueToSelfManagedCall(call);
        } else {
            if (call.isEmergencyCall()) {
                // Disconnect all self-managed calls to make priority for emergency call.
                disconnectSelfManagedCalls();
            }

            call.startCreateConnection(mPhoneAccountRegistrar);
        }
	}
......
}

/packages/services/Telecomm/src/com/android/server/telecom/Call.java

    void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
        if (mCreateConnectionProcessor != null) {
            Log.w(this, "mCreateConnectionProcessor in startCreateConnection is not null. This is" +
                    " due to a race between NewOutgoingCallIntentBroadcaster and " +
                    "phoneAccountSelected, but is harmlessly resolved by ignoring the second " +
                    "invocation.");
            return;
        }
        mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
                phoneAccountRegistrar, mContext);
        mCreateConnectionProcessor.process();
    }

/packages/services/Telecomm/src/com/android/server/telecom/CreateConnectionProcessor.java
 

 private void attemptNextPhoneAccount() {
        Log.v(this, "attemptNextPhoneAccount");
        CallAttemptRecord attempt = null;
        ......

        if (mCallResponse != null && attempt != null) {
            Log.i(this, "Trying attempt %s", attempt);
            PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;
	    //得到建立连接的service
            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);
            }
        } else {
            Log.v(this, "attemptNextPhoneAccount, no more accounts, failing");
            DisconnectCause disconnectCause = mLastErrorDisconnectCause != null ?
                    mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR);
            notifyCallConnectionFailure(disconnectCause);
        }
    }

/packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java

如果BindService 成功,则调用onSuccess()。
onSuccess() 方法将生成ConnectionRequest 然后去调用ConnectionService 建立连接。

   @VisibleForTesting
    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() {
                ......

		//初始化一个 ConnectionRequest,ConnectionRequest 保存重要参数
                ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
                        .setAccountHandle(call.getTargetPhoneAccount())
                        .setAddress(call.getHandle())
                        .setExtras(extras)
                        .setVideoState(call.getVideoState())
                        .setTelecomCallId(callId)
                        // For self-managed incoming calls, if there is another ongoing call Telecom
                        // is responsible for showing a UI to ask the user if they'd like to answer
                        // this new incoming call.
                        .setShouldShowIncomingCallUi(
                                !mCallsManager.shouldShowSystemIncomingCallUi(call))
                        .setRttPipeFromInCall(call.getInCallToCsRttPipeForCs())
                        .setRttPipeToInCall(call.getCsToInCallRttPipeForCs())
                        .build();

                try {
                    mServiceInterface.createConnection(
                            call.getConnectionManagerPhoneAccount(),
                            callId,
                            connectionRequest,
                            call.shouldAttachToExistingConnection(),
                            call.isUnknown(),
                            Log.getExternalSession());

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

ConnectionRequest 保存了哪些重要参数

public final class ConnectionRequest implements Parcelable {

    /**
     * Builder class for {@link ConnectionRequest}
     * @hide
     */
    public static final class Builder {
        private PhoneAccountHandle mAccountHandle;
        private Uri mAddress;
        private Bundle mExtras;
        private int mVideoState = VideoProfile.STATE_AUDIO_ONLY;
        private String mTelecomCallId;
        private boolean mShouldShowIncomingCallUi = false;
        private ParcelFileDescriptor mRttPipeToInCall;
        private ParcelFileDescriptor mRttPipeFromInCall;

        public Builder() { }
        ......		
	}	
}

 

三 /packages/services/Telephony 层

/frameworks/base/telecomm/java/android/telecom/ConnectionService.java

 

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

	// 建立 Connection
        Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
                : isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
                : onCreateOutgoingConnection(callManagerAccount, request);
 

        connection.setTelecomCallId(callId);
	//这里将向connection 设置Listener 回调, 当connection 发出通知后,一直调用到Call对象来处理
        if (connection.getState() != Connection.STATE_DISCONNECTED) {
            addConnection(callId, connection);
        }

	//回调接口,mAdapter处理从connection listener 发出的通知, 然后回调到call 处理。
        mAdapter.handleCreateConnectionComplete(
                callId,
                request,
                new ParcelableConnection(
                        request.getAccountHandle(),
                        connection.getState(),
                        connection.getConnectionCapabilities(),
                        connection.getConnectionProperties(),
                        connection.getSupportedAudioRoutes(),
                        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()));
    }

/packages/services/Telephony/src/com/android/services/telephony/TelephonyConnectionService.java
 

TelephonyConnectionService 开始主要是状态和参数检验,

	
   @Override
    public Connection onCreateOutgoingConnection(
            PhoneAccountHandle connectionManagerPhoneAccount,
            final ConnectionRequest request) {
        Log.i(this, "onCreateOutgoingConnection, request: " + request);

        Uri handle = request.getAddress();
        if (handle == null) {
            Log.d(this, "onCreateOutgoingConnection, handle is null");
            return Connection.createFailedConnection(
                    DisconnectCauseUtil.toTelecomDisconnectCause(
                            android.telephony.DisconnectCause.NO_PHONE_NUMBER_SUPPLIED,
                            "No phone number supplied"));
        }

        String scheme = handle.getScheme();
        String number;
		
	//对scheme 和number进行校验,不符合条件返回failconnection 
        if (PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
            // TODO: We don't check for SecurityException here (requires
            // CALL_PRIVILEGED permission).
            final Phone phone = getPhoneForAccount(request.getAccountHandle(), false);
            if (phone == null) {
                Log.d(this, "onCreateOutgoingConnection, phone is null");
                return Connection.createFailedConnection(
                        DisconnectCauseUtil.toTelecomDisconnectCause(
                                android.telephony.DisconnectCause.OUT_OF_SERVICE,
                                "Phone is null"));
            }
            number = phone.getVoiceMailNumber();
            if (TextUtils.isEmpty(number)) {
                Log.d(this, "onCreateOutgoingConnection, no voicemail number set.");
                return Connection.createFailedConnection(
                        DisconnectCauseUtil.toTelecomDisconnectCause(
                                android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING,
                                "Voicemail scheme provided but no voicemail number set."));
            }

            // Convert voicemail: to tel:
	    // 将voicemail 转化为tel.
            handle = Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
        } else {
            if (!PhoneAccount.SCHEME_TEL.equals(scheme)) {
                Log.d(this, "onCreateOutgoingConnection, Handle %s is not type tel", scheme);
                return Connection.createFailedConnection(
                        DisconnectCauseUtil.toTelecomDisconnectCause(
                                android.telephony.DisconnectCause.INVALID_NUMBER,
                                "Handle scheme is not type tel"));
            }

            number = handle.getSchemeSpecificPart();
            if (TextUtils.isEmpty(number)) {
                Log.d(this, "onCreateOutgoingConnection, unable to parse number");
                return Connection.createFailedConnection(
                        DisconnectCauseUtil.toTelecomDisconnectCause(
                                android.telephony.DisconnectCause.INVALID_NUMBER,
                                "Unable to parse number"));
            }

            final Phone phone = getPhoneForAccount(request.getAccountHandle(), false);
            ......

        // Convert into emergency number if necessary
        // This is required in some regions (e.g. *).
        if (!PhoneNumberUtils.isLocalEmergencyNumber(this, number) &&
                PhoneNumberUtils.isConvertToEmergencyNumberEnabled()) {
            final Phone phone = getPhoneForAccount(request.getAccountHandle(), false);
            // We only do the conversion if the phone is not in service. The un-converted
            // emergency numbers will go to the correct destination when the phone is in-service,
            // so they will only need the special emergency call setup when the phone is out of
            // service.
            if (phone == null || phone.getServiceState().getState()
                    != ServiceState.STATE_IN_SERVICE) {
                String convertedNumber = PhoneNumberUtils.convertToEmergencyNumber(number);
                if (!TextUtils.equals(convertedNumber, number)) {
                    Log.i(this, "onCreateOutgoingConnection, converted to emergency number");
                    number = convertedNumber;
                    handle = Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
                }
            }
        }
        final String numberToDial = number;

        final boolean isEmergencyNumber =
                PhoneNumberUtils.isLocalEmergencyNumber(this, numberToDial);

        final boolean isAirplaneModeOn = Settings.Global.getInt(getContentResolver(),
                Settings.Global.AIRPLANE_MODE_ON, 0) > 0;
            
	//此处是在rodio 关闭情况下的紧急通话。尝试去打开radio,在拨打紧急通话。
            ......
        } else {
            // Get the right phone object from the account data passed in.
            final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber);
	    // 1 得到Connection
            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) {
	        // 2 处理电话连接
                placeOutgoingConnection((TelephonyConnection) resultConnection, phone, request);
            }
            return resultConnection;
        }
    }

placeOutgoingConnection 方法很简单。 调用Phone.dial(), 返回一个telephony.Connection 。

然后将telephony.Connection 传递给TelephonyConnection。

private void placeOutgoingConnection(
            TelephonyConnection connection, Phone phone, int videoState, Bundle extras) {
        String number = connection.getAddress().getSchemeSpecificPart();

        com.android.internal.telephony.Connection originalConnection = null;
        try {
            if (phone != null) {
			    //1. 调用phone处理电话
                originalConnection = phone.dial(number, null, videoState, extras);
            }
        } 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;
        }

        if (originalConnection == null) {
            int telephonyDisconnectCause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
            // On GSM phones, null connection means that we dialed an MMI code
			//2. 如果是gsmphone, phone返回connection 为null,则是MMI code。
            if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
                Log.d(this, "dialed MMI code");
                int subId = phone.getSubId();
                Log.d(this, "subId: "+subId);
                telephonyDisconnectCause = android.telephony.DisconnectCause.DIALED_MMI;
                final Intent intent = new Intent(this, MMIDialogActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                        Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                if (SubscriptionManager.isValidSubscriptionId(subId)) {
                    intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
                }
                startActivity(intent);
            }
            Log.d(this, "placeOutgoingConnection, phone.dial returned null");
            connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
                    telephonyDisconnectCause, "Connection is null"));
        } else {
            connection.setOriginalConnection(originalConnection);
        }
    }
	

 connection.setOriginalConnection 向phone 注册call状态改变的通知消息 和一些重要参数。

 

void setOriginalConnection(com.android.internal.telephony.Connection originalConnection) {
        Log.v(this, "new TelephonyConnection, originalConnection: " + originalConnection);
        clearOriginalConnection();
        mOriginalConnectionExtras.clear();
        mOriginalConnection = originalConnection;
        mOriginalConnection.setTelecomCallId(getTelecomCallId());
		
		//向phone注册状态改变的通知消息。
        getPhone().registerForPreciseCallStateChanged(
                mHandler, MSG_PRECISE_CALL_STATE_CHANGED, null);
        getPhone().registerForHandoverStateChanged(
                mHandler, MSG_HANDOVER_STATE_CHANGED, null);
        getPhone().registerForRingbackTone(mHandler, MSG_RINGBACK_TONE, null);
        getPhone().registerForDisconnect(mHandler, MSG_DISCONNECT, null);
        getPhone().registerForSuppServiceNotification(mHandler, MSG_SUPP_SERVICE_NOTIFY, null);
        getPhone().registerForOnHoldTone(mHandler, MSG_ON_HOLD_TONE, null);
        mOriginalConnection.addPostDialListener(mPostDialListener);
        mOriginalConnection.addListener(mOriginalConnectionListener);

        // 设置一些重要的参数
		// Set video state and capabilities
        setVideoState(mOriginalConnection.getVideoState());
        setOriginalConnectionCapabilities(mOriginalConnection.getConnectionCapabilities());
        setWifi(mOriginalConnection.isWifi());
        setVideoProvider(mOriginalConnection.getVideoProvider());
        setAudioQuality(mOriginalConnection.getAudioQuality());
        setTechnologyTypeExtra();

        // Post update of extras to the handler; extras are updated via the handler to ensure thread
        // safety. The Extras Bundle is cloned in case the original extras are modified while they
        // are being added to mOriginalConnectionExtras in updateExtras.
        Bundle connExtras = mOriginalConnection.getConnectionExtras();
            mHandler.obtainMessage(MSG_CONNECTION_EXTRAS_CHANGED, connExtras == null ? null :
                    new Bundle(connExtras)).sendToTarget();

        if (PhoneNumberUtils.isEmergencyNumber(mOriginalConnection.getAddress())) {
            mTreatAsEmergencyCall = true;
        }

        if (isImsConnection()) {
            mWasImsConnection = true;
        }
        mIsMultiParty = mOriginalConnection.isMultiparty();

        // updateState can set mOriginalConnection to null if its state is DISCONNECTED, so this
        // should be executed *after* the above setters have run.
        updateState();
        if (mOriginalConnection == null) {
            Log.w(this, "original Connection was nulled out as part of setOriginalConnection. " +
                    originalConnection);
        }

        fireOnOriginalConnectionConfigured();
    }

四  frameworks/opt/telephony 层

   @Override
    public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
            throws CallStateException {
        if (!isPhoneTypeGsm() && uusInfo != null) {
            throw new CallStateException("Sending UUS information NOT supported in CDMA!");
        }
        ......
	// IMS call
        if ((imsUseEnabled && (!isUt || useImsForUt)) || useImsForEmergency) {
            try {
                if (DBG) logd("Trying IMS PS call");
                return imsPhone.dial(dialString, uusInfo, videoState, intentExtras);
            } catch (CallStateException e) {
                if (DBG) logd("IMS PS call exception " + e +
                        "imsUseEnabled =" + imsUseEnabled + ", imsPhone =" + imsPhone);
                if (!Phone.CS_FALLBACK.equals(e.getMessage())) {
                    CallStateException ce = new CallStateException(e.getMessage());
                    ce.setStackTrace(e.getStackTrace());
                    throw ce;
                }
            }
        }

        ......
		
        if (isPhoneTypeGsm()) {
            return dialInternal(dialString, null, VideoProfile.STATE_AUDIO_ONLY, intentExtras);
        } else {
            return dialInternal(dialString, null, videoState, intentExtras);
        }
    }
    protected Connection dialInternal(String dialString, UUSInfo uusInfo, int videoState,
                                      Bundle intentExtras, ResultReceiver wrappedCallback)
            throws CallStateException {

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

        if (isPhoneTypeGsm()) {
            // Only look at the Network portion for mmi
            String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
            GsmMmiCode mmi = GsmMmiCode.newFromDialString(networkPortion, this,
                    mUiccApplication.get(), wrappedCallback);
            if (DBG) logd("dialInternal: 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));
                mmi.processCode();
                return null;
            }
        } else {
            return mCT.dial(newDialString);
        }
    }

/frameworks/opt/telephony/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java

//GSM
    /**
     * clirMode is one of the CLIR_ constants
     */
    public synchronized Connection dial(String dialString, int clirMode, UUSInfo uusInfo,
                                        Bundle intentExtras)
            throws CallStateException {
   
        String origNumber = dialString;
        dialString = convertNumberIfNecessary(mPhone, dialString);

        // The new call must be assigned to the foreground call.
        // That call must be idle, so place anything that's
        // there on hold
	//如果当前有一个call处于前台,我们需要将它hold住,并通过一个500毫秒的延时来执行hold这个操作,如果不延时在多方会议通话的时候可能出现问题
        if (mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE) {
            // this will probably be done by the radio anyway
            // but the dial might fail before this happens
            // and we need to make sure the foreground call is clear
            // for the newly dialed connection
            switchWaitingOrHoldingAndActive();
            // This is a hack to delay DIAL so that it is sent out to RIL only after
            // EVENT_SWITCH_RESULT is received. We've seen failures when adding a new call to
            // multi-way conference calls due to DIAL being sent out before SWITCH is processed
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                // do nothing
            }

            // Fake local state so that
            // a) foregroundCall is empty for the newly dialed connection
            // b) hasNonHangupStateChanged remains false in the
            // next poll, so that we don't clear a failed dialing call
            fakeHoldForegroundBeforeDial();
        }

        boolean isEmergencyCall = PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(),
                dialString);
        mPendingMO = new GsmCdmaConnection(mPhone, checkForTestEmergencyNumber(dialString),
                this, mForegroundCall, isEmergencyCall);
        mHangupPendingMO = false;
        mMetrics.writeRilDial(mPhone.getPhoneId(), mPendingMO, clirMode, uusInfo);


        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);
            // 1. RIL 的 dial
            mCi.dial(mPendingMO.getAddress(), clirMode, uusInfo, obtainCompleteMessage());
        }

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

        updatePhoneState();
        mPhone.notifyPreciseCallStateChanged();

        return mPendingMO;
    }