android Telephony学习 --- 第五篇 android7.0 呼叫(MO)流程

我们先看下7.0 MO大致流程:
这里写图片描述

  • package/app/Dialer – DialpadFragment

    用户点击拨号盘的拨号按钮,此时开始呼叫长征第一步,dialpadfragment的onclick方法会响应点击事件。

@Override
public void onClick(View view) {
    int resId = view.getId();
    if (resId == R.id.dialpad_floating_action_button) {
        handleDialButtonPressed();
    }
}

我们只看关键方法,点击事件调用handleDialButtonPressed方法,号码不为空时,调用DialerUtils的startActivityWithErrorToast方法。当然其方法内部还有一些其他的判断条件,比如号码是空时,会调用handleDialButtonClickWithEmptyDigits方法,显示上次呼叫的号码。

private void handleDialButtonPressed() {
    final Intent intent = CallUtil.getCallIntent(number);
    if (!isDigitsShown) {
      // must be dial conference add extra
     intent.putExtra(EXTRA_DIAL_CONFERENCE_URI, true);
     }
     intent.putExtra(ADD_PARTICIPANT_KEY, mAddParticipant && isPhoneInUse());
     DialerUtils.startActivityWithErrorToast(getActivity(), intent);
     hideAndClearDialpad(false);
}
  • package/app/Dialer – DialerUtils
    携带Intent.ACTION_CALL的Intent Action会走到TelecomUtil placeCall流程,否则直接context.startActivity(intent);
public static void startActivityWithErrorToast(Context context, Intent intent, int msgId) {
  if ((IntentUtil.CALL_ACTION.equals(intent.getAction())
                            && context instanceof Activity)) {
     final boolean hasCallPermission =  TelecomUtil.placeCall((Activity) context, intent);
  }
}
  • package/app/Dialer – TelecomUtil
public static boolean placeCall(Activity activity, Intent intent) {
      if (hasCallPhonePermission(activity)) {
           TelecomManagerCompat.placeCall(activity, getTelecomManager(activity), intent);
           return true;
       }
       return false;
}

hasCallPhonePermission()会检查是否有呼叫权限,包含是否是默认dialer,和是否有Manifest.permission.CALL_PHONE)权限:

    public static boolean hasCallPhonePermission(Context context) {
        return isDefaultDialer(context)
                || hasPermission(context, Manifest.permission.CALL_PHONE);
    }
  • package/app/ContactsCommon – TelecomManagerCompat

    当版本大于等于6.0时,调用telecomManager.placeCall()方法,否则直接startActivity执行intent。所以我们接着看telecomManager.placeCall的方法:

    public static void placeCall(@Nullable Activity activity,
            @Nullable TelecomManager telecomManager, @Nullable Intent intent) {
        if (CompatUtils.isMarshmallowCompatible()) {
            telecomManager.placeCall(intent.getData(), intent.getExtras());
            return;
        }
        activity.startActivityForResult(intent, 0);
    }
  • frameworks/base/telecomm – TelecomManager

调用ITelecomService的placeCall方法,此处是aidl调用,对应的我们需要找到接收的地方,按照命名规则应该是TelecomServiceImpl:

    @RequiresPermission(android.Manifest.permission.CALL_PHONE)
    public void placeCall(Uri address, Bundle extras) {
        ITelecomService service = getTelecomService();
        if (service != null) {
            try {
                service.placeCall(address, extras == null ? new Bundle() : extras,
                        mContext.getOpPackageName());
            } catch (RemoteException e) {
                Log.e(TAG, "Error calling ITelecomService#placeCall", e);
            }
        }
    }
  • packages/services/Telecomm – TelecomServiceImpl

创建UserCallIntentProcessorFactory,调用processIntent方法处理呼叫:

private final ITelecomService.Stub mBinderImpl = new ITelecomService.Stub() {

        @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);
                        mUserCallIntentProcessorFactory.create(mContext, userHandle)
                                .processIntent(
                                        intent, callingPackage, hasCallAppOp && hasCallPermission);
                    }
                }
            } 
        }
}
  • packages/services/Telecomm – UserCallIntentProcessor

processIntent判断是否是呼叫请求:

public void processIntent(Intent intent, String callingPackageName,
            boolean canCallNonEmergency) {
   if (Intent.ACTION_CALL.equals(action) ||
           Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
           Intent.ACTION_CALL_EMERGENCY.equals(action)) {
      processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency);
   }
}

  • Intent.ACTION_CALL: 可以拨打普通呼叫
public static final String ACTION_CALL = "android.intent.action.CALL";
  • Intent.ACTION_CALL_PRIVILEGED:可以拨打任意类型号码
public static final String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED";
  • Intent.ACTION_CALL_EMERGENCY:可以拨打紧急呼叫
public static final String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY";

processOutgoingCallIntent方法:

private void processOutgoingCallIntent(Intent intent, String callingPackageName,
            boolean canCallNonEmergency) {
         ......
        sendBroadcastToReceiver(intent);
}

sendBroadcastToReceiver方法,发送广播PrimaryCallReceiver:

    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 – PrimaryCallReceiver

调用getTelecomSystem方法返回TelecomSystem对象,调用getCallIntentProcessor()返回CallIntentProcessor对象,然后调用processIntent方法

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.startSession("PCR.oR");
        synchronized (getTelecomSystem().getLock()) {
            getTelecomSystem().getCallIntentProcessor().processIntent(intent);
        }
        Log.endSession();
    }
  • packages/services/Telecomm – CallIntentProcessor
    public void processIntent(Intent intent) {
        final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false);
        Log.i(this, "onReceive - isUnknownCall: %s", isUnknownCall);

        Trace.beginSection("processNewCallCallIntent");
        if (isUnknownCall) {
            processUnknownCallIntent(mCallsManager, intent);
        } else {
            processOutgoingCallIntent(mContext, mCallsManager, intent);
        }
        Trace.endSection();
    }

processOutgoingCallIntent方法,调用CallsManager的startOutgoingCall()方法创建Call,后new NewOutgoingCallIntentBroadcaster,调用processIntent()方法:

    static void processOutgoingCallIntent(
            Context context,
            CallsManager callsManager,
            Intent intent) {
        // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
        Call call = callsManager
                .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser);

        if (call != null) {
            NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
                    context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(),
                    isPrivilegedDialer);
            final int result = broadcaster.processIntent();
        }
    }

看创建Call的备注,貌似和UI有关呢,不过我们先关注呼叫流程,回头再整理UI启动流程,这里加个*标记下,先接着查看processIntent方法:

   public int processIntent() {
        ......
        if (callImmediately) {
            mCall.setNewOutgoingCallIntentBroadcastIsDone();
            mCallsManager.placeOutgoingCall(mCall, Uri.fromParts(scheme, number, null), null,
                    speakerphoneOn, videoState);
        }
        ......
        broadcastIntent(intent, number, !callImmediately, targetUser);
    }
    private void broadcastIntent(
            Intent originalCallIntent,
            String number,
            boolean receiverRequired,
            UserHandle targetUser) {

        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
    }

callImmediately为true时,直接调用CallsManager的placeOutgoingCall()方法;
callImmediately为false时,创建NewOutgoingCallBroadcastIntentReceiver实例来接收该广播,而后调用到CallsManager的placeOutgoingCall()方法:

   public class NewOutgoingCallBroadcastIntentReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            try {
                    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));
                }
            }
        }
    }
  • packages/services/Telecomm – CallsManager
   public void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo,
            boolean speakerphoneOn, int videoState) {
        if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) {
                call.startCreateConnection(mPhoneAccountRegistrar);
        } 
    }
  • packages/services/Telecomm – Call

创建CreateConnectionProcessor,并调用process方法:

    void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
        mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
                phoneAccountRegistrar, mContext);
        mCreateConnectionProcessor.process();
    }
  • packages/services/Telecomm – CreateConnectionProcessor

主要关注attemptNextPhoneAccount方法:

    public void process() {
        adjustAttemptsForConnectionManager();
        adjustAttemptsForEmergency(mCall.getTargetPhoneAccount());
        mAttemptRecordIterator = mAttemptRecords.iterator();
        attemptNextPhoneAccount();
    }

调用ConnectionServiceWrapper mService的createConnection方法:

    private void attemptNextPhoneAccount() {
           ......
                mConnectionAttempt++;
                mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);
                mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
                mCall.setConnectionService(mService);
                setTimeoutIfNeeded(mService, attempt);

                mService.createConnection(mCall, this);
        }
    }
  • packages/services/Telecomm – ConnectionServiceWrapper

通过bind建立成功后,会回调onSuccess方法,调用ConnectionService的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);
                try {
                    mServiceInterface.createConnection(
                            call.getConnectionManagerPhoneAccount(),
                            callId,
                            new ConnectionRequest(
                                    call.getTargetPhoneAccount(),
                                    call.getHandle(),
                                    extras,
                                    call.getVideoState(),
                                    callId),
                            call.shouldAttachToExistingConnection(),
                            call.isUnknown());
                } 
            }
        mBinder.bind(callback, call);
    }
  • frameworks/base/telecomm – ConnectionService
    由于TelephonyConnectionService是ConnectionService的实例,所以我们接着跟踪
    TelephonyConnectionService的onCreateOutgoingConnection方法:
                case MSG_CREATE_CONNECTION: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    try {
                        else {
                            createConnection(
                                    connectionManagerPhoneAccount,
                                    id,
                                    request,
                                    isIncoming,
                                    isUnknown);
                        }
                    }
                    break;
                }

这里可以看到有来电和去电的逻辑,我们先跟踪呼出的逻辑:

    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);
  • packages/services/Telecomm – TelephonyConnectionService
    @Override
    public Connection onCreateOutgoingConnection(
            PhoneAccountHandle connectionManagerPhoneAccount,
            final ConnectionRequest request) {
            .......
            placeOutgoingConnection((TelephonyConnection) resultConnection, phone, request);
   }

吼吼,是不是看到曙光了呢,有木有看到dial()方法,快接近曙光了有木有:

    private void placeOutgoingConnection(
            TelephonyConnection connection, Phone phone, int videoState, Bundle extras,
            ConnectionRequest request) {
            ......
            originalConnection = phone.dial(number, null, request.getVideoState(), bundle);
   }
  • frameworks/opt/telephony – GsmCdmaPhone
    @Override
    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);
        }
    }

接着就是调用GsmCdmaCallTracker的dial方法:

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

            return mCT.dial(newDialString);
    }
  • frameworks/opt/telephony – GsmCdmaCallTracker

mCi就是RIL的实例,也就是说调用的是RIL的dial()方法:

 public synchronized Connection dial(String dialString, int clirMode, UUSInfo uusInfo,
                                        Bundle intentExtras)
            throws CallStateException {
        } else {
            // Always unmute when initiating a new call
            setMute(false);

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

        return mPendingMO;
  }
  • frameworks/opt/telephony – RIL

到这就是常见的 > DIAL命令了,有木有:

    @Override
    public void
    dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
        RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);

        rr.mParcel.writeString(address);
        rr.mParcel.writeInt(clirMode);

        if (uusInfo == null) {
            rr.mParcel.writeInt(0); // UUS information is absent
        } else {
            rr.mParcel.writeInt(1); // UUS information is present
            rr.mParcel.writeInt(uusInfo.getType());
            rr.mParcel.writeInt(uusInfo.getDcs());
            rr.mParcel.writeByteArray(uusInfo.getUserData());
        }

        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));

        send(rr);
    }

当然上面都是上层的流程,接着就是RIL层和modem信令的事情喽。

猜你喜欢

转载自blog.csdn.net/tingsky9985/article/details/79914619