Phone撥號流程(Android9.0 Phone撥號)

今天講解一下Dialer呼出 直達底層

目錄結構

  1. packages/app/Dialer
    撥打電話的入口,來電不會經過Dialer。但是撥打電話的出口不光是Dialer,在聯繫人和短信裏也有撥打電話的出口。代碼運行在dialer進程。
  2. packages/app/InCallUI
    負責顯示通話界面的信息,來電信息。dialer進程。
  3. packages/services/Telecomm
    處理Intent,發送廣播,設置call的狀態,audio狀態。system_process和telecomm:ui進程。
  4. packages/services/Telephony
    向下層傳遞撥號,註冊了很多廣播,申請很多權限(service data sms wap network)。 phone進程
  5. framework/base/telecom
    提供placeCall的接口(自android M開始),創建outgoingCall的connection,通知上層成功建立connection
  6. framewrok/opt/telephony
    撥號 也就是dial命令的下發,但是如果是Ims網絡就會有下面一步
  7. Vendor/ims
    創建ImsConnection,ImsCall,撥號。phone進程。

流程圖:

主要的類以及作用


Dialer

  1. DialpadFragment.java               撥號鍵盤
  2. DialerUtils.java                          撥號工具
  3. TelecomUtil.java                        通訊工具 
  4. InCallServiceImpl.java               InCallUi開啓服務,他繼承的是InCallService。

service/telecomm

  1. TelecomService.java                  系統初始化的時候就會進行綁定,並不  是在打電話的時候才綁TelecomSystem會被初始                                                        化,期間構 建 一 些列必須源, 包括Phone和CI(和底層溝通的對象)    的構建。
  2. TelecomServiceImpl.java            提供Binder接口。
  3. UserCallIntentProcessor.java      用戶撥號處理,處理 內部校驗一些撥號的權限,以及其它作 權限看看是不是需要彈框                                                            絕。 比如《只允許打緊急電話沒有電話許可,此應用程序不能發 出呼叫
  4. PrimaryCallReceiver.java            廣 播,作爲撥號的單一入口, 其到消息後,就轉發TelecomSystem的。
  5. CallIntentProcessor.java              判斷Intent裏面的數據,比如  號碼是否爲空等等....。
  6. CallsManager.java                       官方聲明  :CallManager對象爲 Phone應用程序提供通用 Call API的方法。 SipPhone類                                                       實現 Phone接口,但大部分由虛擬方法填充,實際現使用的是 SipManager,訪問 SIP通                                                       信框架並控 制 SIP Call. 他的主要作用是  呼出入口以及控制InCallController
  7. InCallController.java                     控制電話App的邏輯和UI 。
  8. Call.java                                        撥打電話或者來電入口。
  9. CreateConnectionProcessor.java  創建連接
  10. ConnectionServiceWrapper.java    主要作用是綁定該Adapter與framework/base/Telecomm中的ConnectionService 之間                                                             創建一個監聽,再有創建了一個連接
  11. ServiceBinder.java                          serviceBinder是ConnectionSerivceWrapper中的父類

service/telephony

  1. TelephonyConnectionService.java  他繼承自ConnectionService這個  GSM和CDMA連接的服務。
  2. TelephneyConnection.java              CDMA和GSM連接的基類。
  3. GsmConnection.java                       管理GSM處理的單個電話呼叫。
  4. CmdaConnection.java                     管理CDMA處理的單個電話呼叫。        

framework/base/telecom   這個包中的全部可以給第三方調用

  1. TelecomManager.java                 向外部提供接口,有很多地方會調用這個接口進行撥號。
  2. InCallService.java                       提供創建連接創建連接 ,也是播出入口,在InCallUI中InCallServiceImpl實現了該服務,                                                        InCallService是UI和telecom的接口。InCallController綁定InCallService。

 

代碼流程:

第一步進入呼出Dialer

DialerUtils.java  的作用提供一些工具方法給Dialer進行處理

 public static void startActivityWithErrorToast(
      final Context context, final Intent intent, int msgId) {
    try {
      if ((Intent.ACTION_CALL.equals(intent.getAction()))) {
      ......

        if (shouldWarnForOutgoingWps(context, intent.getData().getSchemeSpecificPart())) {

          AlertDialog.Builder builder = new AlertDialog.Builder(context);
          builder.setMessage(R.string.outgoing_wps_warning);//彈出對話框 撥打 WPS 電話會中斷現有通話。
          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);//核心入口
  }

TelecomUtil.java  提供通話工具

 public static boolean placeCall(Context context, Intent intent) {
    if (hasCallPhonePermission(context)) {
        // 核心,主過程  這個方法主要是調用TelecomManager.placeCall();//TelecomManager.java屬於Framework/Telephony代碼
      getTelecomManager(context).placeCall(intent.getData(), intent.getExtras());
      return true;
    }
    return false;
  }

第二部進入phone進程 framework/base/telecomm

base/telecomm主要的作用是提供接口給外部進行調用,比如短信,聯繫人 等等.....

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

 

第三步進入 system進程,也就是packager/sevice/Telecomm

進入System進程TelecomService是屬於系統框架向外提供的服務,隨着系統啓動而啓動,並由TelecomManager向外提供訪問接。在TelecomSystem第一次綁定時(系統初始化的時候會進行綁定,並不是在打電話的時候才綁定),TelecomSystem會被初始化期間構建一些列必須資源,包括Phone和CI(和底層溝通的對象)的構建

TelecomService.java

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(this, "onBind");
        initializeTelecomSystem(this);//系統初始化的時候就會進行綁定,並不是在打電話的時候才綁定),TelecomSystem 會被初始化,期間構建一些列必須資源,包括Phone和CI(和底層溝通的對象)的構建
        synchronized (getTelecomSystem().getLock()) {
            return getTelecomSystem().getTelecomServiceImpl().getBinder();
        }
    }


 static void initializeTelecomSystem(Context context) {
        if (TelecomSystem.getInstance() == null) {
            .....
            // TelecomSystem被初始化  構造都是在打電話前執行的
            TelecomSystem.setInstance(
                    new TelecomSystem(
                            context,
                            new MissedCallNotifierImpl.MissedCallNotifierImplFactory() {
                                @Override
                                public MissedCallNotifierImpl makeMissedCallNotifierImpl(
                                        Context context,
                                        PhoneAccountRegistrar phoneAccountRegistrar,
                                        DefaultDialerCache defaultDialerCache) {
                                    return new MissedCallNotifierImpl(context,
                                            phoneAccountRegistrar, defaultDialerCache);
                                }
                            },
                            .....
                            .......
                            ........
        }
        if (BluetoothAdapter.getDefaultAdapter() != null) {
            context.startService(new Intent(context, BluetoothPhoneService.class));
        }
    }

TelecomSystem 系統進程中,通話模塊的核心

TelecomSystem 構造函數

 

 public TelecomSystem(
            Context context,
             ......) {
        mContext = context.getApplicationContext();
        ......
        .....
        // 構建CallsManager
        mCallsManager = new CallsManager(
                mContext,
                mLock,
                mContactsAsyncHelper,
                ..........);

        mIncomingCallNotifier = incomingCallNotifier;
        incomingCallNotifier.setCallsManagerProxy(......)
        mCallsManager.setIncomingCallNotifier(mIncomingCallNotifier);

        mRespondViaSmsManager = new RespondViaSmsManager(mCallsManager, mLock);
        mCallsManager.setRespondViaSmsManager(mRespondViaSmsManager);
        //註冊廣播
        mContext.registerReceiver(mUserSwitchedReceiver, USER_SWITCHED_FILTER);
        mContext.registerReceiver(mUserStartingReceiver, USER_STARTING_FILTER);
        mContext.registerReceiver(mBootCompletedReceiver, BOOT_COMPLETE_FILTER);

        mBluetoothPhoneServiceImpl = bluetoothPhoneServiceImplFactory.makeBluetoothPhoneServiceImpl(
                mContext, mLock, mCallsManager, mPhoneAccountRegistrar);
        // 構建CallIntentProcessor, 執行Intent的請求
        mCallIntentProcessor = new CallIntentProcessor(mContext, mCallsManager);
        mTelecomBroadcastIntentProcessor = new TelecomBroadcastIntentProcessor(
                mContext, mCallsManager);

        // Register the receiver for the dialer secret codes, used to enable extended logging.
        mDialerCodeReceiver = new DialerCodeReceiver(mCallsManager);
        mContext.registerReceiver(mDialerCodeReceiver, DIALER_SECRET_CODE_FILTER,
                Manifest.permission.CONTROL_INCALL_EXPERIENCE, null);
    // 構建TelecomServiceImpl,這個是TelecomSystem的真正實現,返回的binder由這裏提供,
    // 外部對TelecomService的操作實際上都是在TelecomServiceImpl裏面進行的。
        mTelecomServiceImpl = new TelecomServiceImpl(
                mContext, mCallsManager, mPhoneAccountRegistrar,
                new CallIntentProcessor.AdapterImpl(),
                new UserCallIntentProcessorFactory() {
                    @Override
                    public UserCallIntentProcessor create(Context context, UserHandle userHandle) {
                        return new UserCallIntentProcessor(context, userHandle);
                    }
                },
                defaultDialerCache,
                new TelecomServiceImpl.SubscriptionManagerAdapterImpl(),
                mLock);
        Log.endSession();
    }

上面的構造都是在打電話之前執行的,下面繼續回到調起InCallUi流程

上面placeCall() 方法會進入到 TelecomServiceImpl.java中ITelecomService.Stub中的placeCall()方法,ITelecomService.Stub是一提供一個撥號入口。

TelecomServiceImpl.java

        @Override
        public void placeCall(Uri handle, Bundle extras, String callingPackage) {
            try {
                Log.startSession("TSI.pC");
                enforceCallingPackage(callingPackage);//權限檢查,傳入對應的調用位置(調用位置org.codeaurora.dialer)

                PhoneAccountHandle phoneAccountHandle = null;
                if (extras != null) {
                    phoneAccountHandle = extras.getParcelable(
                            TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
                    if (extras.containsKey(TelecomManager.EXTRA_IS_HANDOVER)) {
                        // This extra is for Telecom use only so should never be passed in.
                        extras.remove(TelecomManager.EXTRA_IS_HANDOVER);
                    }
                }
                boolean isSelfManaged = phoneAccountHandle != null &&
                        isSelfManagedConnectionService(phoneAccountHandle);
                if (isSelfManaged) {
                    mContext.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_OWN_CALLS,
                            "Self-managed ConnectionServices require MANAGE_OWN_CALLS permission.");

                    if (!callingPackage.equals(
                            phoneAccountHandle.getComponentName().getPackageName())
                            && !canCallPhone(callingPackage,
                            "CALL_PHONE permission required to place calls.")) {

                        throw new SecurityException("Self-managed ConnectionServices can only "
                                + "place calls through their own ConnectionService.");
                    }
                } else if (!canCallPhone(callingPackage, "placeCall")) {
                    throw new SecurityException("Package " + callingPackage
                            + " is not allowed to place phone calls");
                }



                final boolean hasCallAppOp = mAppOpsManager.noteOp(AppOpsManager.OP_CALL_PHONE,
                        Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED;

                final boolean hasCallPermission = mContext.checkCallingPermission(CALL_PHONE) ==
                        PackageManager.PERMISSION_GRANTED;

                final boolean hasCallPrivilegedPermission = mContext.checkCallingPermission(
                        CALL_PRIVILEGED) == PackageManager.PERMISSION_GRANTED;

                synchronized (mLock) {
                    final UserHandle userHandle = Binder.getCallingUserHandle();
                    long token = Binder.clearCallingIdentity();
                    try {
                        final Intent intent = new Intent(hasCallPrivilegedPermission ?
                                Intent.ACTION_CALL_PRIVILEGED : Intent.ACTION_CALL, handle);
                        if (extras != null) {
                            extras.setDefusable(true);
                            intent.putExtras(extras);
                        }
                        // 核心,主過程
                        mUserCallIntentProcessorFactory.create(mContext, userHandle)
                                .processIntent(
                                        intent, callingPackage, isSelfManaged ||
                                                (hasCallAppOp && hasCallPermission),
                                        true /* isLocalInvocation */);
                    } finally {
                        Binder.restoreCallingIdentity(token);
                    }
                }
            } finally {
                Log.endSession();
            }
        }

接下來UserCallIntentProcessor.java主要處理一些 內部校驗一些撥號的權限,以及其它操作權限看看是不是需要彈框拒絕。比如 《只允許打緊急電話,沒有電話許可,此應用程序不能發出呼叫》

UserCallIntentProcessor.java

    public void processIntent(Intent intent, String callingPackageName,
            boolean canCallNonEmergency, boolean isLocalInvocation) {
        // 確保無法調用的設備上沒有處理調用意圖。
        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,
                    isLocalInvocation); //調用processOutgoingCallIntent()方法緊接着執行
        }
    }
    // 核心,主過程 內部校驗一些撥號的權限,以及其它操作權限看看是不是需要彈框拒絕。比如 《只允許打緊急電話,沒有電話許可,此應用程序不能發出呼叫》
    private void processOutgoingCallIntent(Intent intent, String callingPackageName,
            boolean canCallNonEmergency, boolean isLocalInvocation) {
        Uri handle = intent.getData();
        String scheme = handle.getScheme();
        String uriString = handle.getSchemeSpecificPart();

        //  確保使用TEL方案撥打的sip uri轉換爲sip方案。
        if (PhoneAccount.SCHEME_TEL.equals(scheme) && PhoneNumberUtils.isUriNumber(uriString)) {
            handle = Uri.fromParts(PhoneAccount.SCHEME_SIP, uriString, null);
        }
        //檢查DISALLOW_OUTGOING_CALLS限制。注意:我們在託管配置文件用戶中跳過此檢查,因爲總是可以通過複製和粘貼電話號碼到個人撥號器來繞過此檢查。
        if (!UserUtil.isManagedProfile(mContext, mUserHandle)) {
          
            // restriction. 只有使用DISALLOW_OUTGOING_CALLS限制的用戶才允許使用緊急呼叫。
            if (!TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
                final UserManager userManager = (UserManager) mContext.getSystemService(
                        Context.USER_SERVICE);
                if (userManager.hasBaseUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
                        mUserHandle)) {
                    showErrorDialogForRestrictedOutgoingCall(mContext,
                            R.string.outgoing_call_not_allowed_user_restriction);
             
                    return;
                } else if (userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
                        mUserHandle)) {
                    final DevicePolicyManager dpm =
                            mContext.getSystemService(DevicePolicyManager.class);
                    if (dpm == null) {
                        return;
                    }
                    final Intent adminSupportIntent = dpm.createAdminSupportIntent(
                            UserManager.DISALLOW_OUTGOING_CALLS);
                    if (adminSupportIntent != null) {
                        mContext.startActivity(adminSupportIntent);
                    }
                    return;
                }
            }
        }

        if (!canCallNonEmergency && !TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
            showErrorDialogForRestrictedOutgoingCall(mContext,
                    R.string.outgoing_call_not_allowed_no_permission);
            return;
        }

        int videoState = intent.getIntExtra(
                TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                VideoProfile.STATE_AUDIO_ONLY);
        intent.putExtra(CallIntentProcessor.KEY_IS_PRIVILEGED_DIALER,
                isDefaultOrSystemDialer(callingPackageName));

        //在將意圖轉發給主用戶之前,保存當前用戶的用戶handle。
        intent.putExtra(CallIntentProcessor.KEY_INITIATING_USER, mUserHandle);
        // 核心,主過程 //檢查一些權限後,執行那個下面代碼發送廣播
        sendIntentToDestination(intent, isLocalInvocation);
    }



    /**
     * rebroadcasting it. 潛在地將意圖傳遞給僅作爲主要用戶運行的廣播接收器。如果調用方是電信服務的本地調用方,我們將發送意圖給電信,而不進行重播。
     */
    private boolean sendIntentToDestination(Intent intent, boolean isLocalInvocation) {
        intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);
        intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        intent.setClass(mContext, PrimaryCallReceiver.class); // //開啓廣播 最後進入到 PrimaryCallReceiver
        if (isLocalInvocation) {
            synchronized (TelecomSystem.getInstance().getLock()) {
              TelecomSystem.getInstance().getCallIntentProcessor().processIntent(intent);
            }
        } else {
            mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM); //發送廣播
        }
        return true;
    }

接下來進入關播PrimaryCallReceiver中  ,PrimaryCallReceiver作爲撥號的單一入口,其收到消息後就轉給TelecomSystem中的CallIntentProcessor進行處理

PrimartCallReceiver.java
 

@Override
    public void onReceive(Context context, Intent intent) {//作爲撥號的單一入口, 其收到消息後,就轉發給 TelecomSystem的
        Log.startSession("PCR.oR");
        synchronized (getTelecomSystem().getLock()) {
            getTelecomSystem().getCallIntentProcessor().processIntent(intent);//調用所有呼出和呼入電話的單一入口
        }
        Log.endSession();
    }

CallIntentProcessor.java

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

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

        Uri handle = intent.getData();
        String scheme = handle.getScheme();
        String uriString = handle.getSchemeSpecificPart();
        boolean isSkipSchemaParsing = intent.getBooleanExtra(
                TelephonyProperties.EXTRA_SKIP_SCHEMA_PARSING, false);
        Log.d(CallIntentProcessor.class, "isSkipSchemaParsing = " + isSkipSchemaParsing);


        if (PhoneAccount.SCHEME_TEL.equals(scheme) && PhoneNumberUtils.isUriNumber(uriString) && !isSkipSchemaParsing) {
            handle = Uri.fromParts(PhoneAccount.SCHEME_SIP, 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();
        }
        if (isSkipSchemaParsing) {
            clientExtras.putBoolean(TelephonyProperties.EXTRA_SKIP_SCHEMA_PARSING,
                    isSkipSchemaParsing);
            handle = Uri.fromParts(PhoneAccount.SCHEME_TEL, handle.toString(), null);
        }
        boolean isConferenceUri = intent.getBooleanExtra(
                TelephonyProperties.EXTRA_DIAL_CONFERENCE_URI, false);
        Log.d(CallIntentProcessor.class, "isConferenceUri = "+isConferenceUri);
        if (isConferenceUri) {
            clientExtras.putBoolean(TelephonyProperties.EXTRA_DIAL_CONFERENCE_URI, isConferenceUri);
        }
        boolean isAddParticipant = intent.getBooleanExtra(
                TelephonyProperties.ADD_PARTICIPANT_KEY, false);
        Log.d(CallIntentProcessor.class, "isAddparticipant = "+isAddParticipant);
        if (isAddParticipant) {
            clientExtras.putBoolean(TelephonyProperties.ADD_PARTICIPANT_KEY, isAddParticipant);
        }

        if (intent.hasExtra(TelecomManager.EXTRA_CALL_SUBJECT)) {
            String callsubject = intent.getStringExtra(TelecomManager.EXTRA_CALL_SUBJECT);
            clientExtras.putString(TelecomManager.EXTRA_CALL_SUBJECT, callsubject);
        }

        final int callDomain = intent.getIntExtra(
                QtiCallConstants.EXTRA_CALL_DOMAIN, QtiCallConstants.DOMAIN_AUTOMATIC);
        Log.d(CallIntentProcessor.class, "callDomain = " + callDomain);
        clientExtras.putInt(QtiCallConstants.EXTRA_CALL_DOMAIN, callDomain);

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

        if (!callsManager.isSelfManaged(phoneAccountHandle,
                (UserHandle) intent.getParcelableExtra(KEY_INITIATING_USER))) {
            boolean fixedInitiatingUser = fixInitiatingUserIfNecessary(context, intent);
 
            if (fixedInitiatingUser) {
                Toast.makeText(context, R.string.toast_personal_call_msg, Toast.LENGTH_LONG).show();
            }
        } else {
            Log.i(CallIntentProcessor.class,
                    "processOutgoingCallIntent: skip initiating user check");
        }

        UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER);


        TelecomManager telecomManager =
                (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
        List<PhoneAccountHandle> mSubscriptionAccountHandles = telecomManager.getCallCapablePhoneAccounts();

        boolean isDualCard = (mSubscriptionAccountHandles != null && mSubscriptionAccountHandles.size() > 1);
        if(isDualCard) {

            String phoneNumber = intent.getData().getSchemeSpecificPart();
            phoneNumber = PhoneNumberUtils.stripSeparators(phoneNumber);
            int simGroup = SimSelectDialog.getCardByNumber(context, phoneNumber) - 1;
            if (simGroup >= 0) {

                if (phoneAccountHandle == null) {
                    phoneAccountHandle = telecomManager.getUserSelectedOutgoingPhoneAccount();
                }
                if (phoneAccountHandle != null) {
                    if (!phoneAccountHandle.equals(mSubscriptionAccountHandles.get(simGroup))) {

                        ......
        }
        /**end*/
        // 創建一個call.startOutgoingCall 新建一個(或者重用) call, 將CallsManager綁定監聽該 call, 然後通知 callsManager 的監聽者, 添加了一個 call
        // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
        Call call = callsManager
                .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser,
                        intent);
        //這是一個斷點,因爲下面將會詳細地深入分析 callsManager.startOutgoingCall 的流程,下面分析 startOutgoingCall 完成後, 再從 NewOutgoingCallIntentBroadcaster.processIntent()往下分析
        if (call != null) {
            sendNewOutgoingCallIntent(context, call, callsManager, intent);//拿到上面創建的call, 進行下一步
        }
    }

這裏重點看callsManager.startOutgoingCall(......);這裏就是調起InCallUi的入口,startOutgoingCall新建一個(或者重用)call,將CallManager綁定監聽該call,然後通知callsManager的監聽,添加了一個call
sendNewOutgoingCallIntent()方法

static void sendNewOutgoingCallIntent(Context context, Call call, CallsManager callsManager,
            Intent intent) {
        NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
                context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(),
                isPrivilegedDialer);
        final int result = broadcaster.processIntent();  //核心入口
        ......
    }

NewOutgoingCallIntentBroadcaster.java

@VisibleForTesting
    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());
        if (isVoicemailNumber) {
            if (Intent.ACTION_CALL.equals(action)
                    || Intent.ACTION_CALL_PRIVILEGED.equals(action)) {
                // 語音郵件呼叫將由電話連接管理器直接處理

                boolean speakerphoneOn = mIntent.getBooleanExtra(
                        TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
                placeOutgoingCallImmediately(mCall, handle, null, speakerphoneOn,
                        VideoProfile.STATE_AUDIO_ONLY);//核心入口

                return DisconnectCause.NOT_DISCONNECTED;
            } else {
                Log.i(this, "Unhandled intent %s. Ignoring and not placing call.", intent);
                return DisconnectCause.OUTGOING_CANCELED;
            }
        }
        ........

        return DisconnectCause.NOT_DISCONNECTED;
    }
    private void placeOutgoingCallImmediately(Call call, Uri handle, GatewayInfo gatewayInfo,
            boolean speakerphoneOn, int videoState) {
        Log.i(this,
                "Placing call immediately instead of waiting for OutgoingCallBroadcastReceiver");
        // 準備開啓廣播進行呼叫
        mCall.setNewOutgoingCallIntentBroadcastIsDone();
        mCallsManager.placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn, videoState);//核心入口
    }

 

CallManager.java

    public void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo,
            boolean speakerphoneOn, int videoState) {
        if (call == null) {
            // 如果不存在就退出
            return;
        }

        final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress();

        if (gatewayInfo == null) {

        } else {
        call.setHandle(uriHandle);
        call.setGatewayInfo(gatewayInfo);

        final boolean useSpeakerWhenDocked = mContext.getResources().getBoolean(
                R.bool.use_speaker_when_docked);
        final boolean useSpeakerForDock = isSpeakerphoneEnabledForDock();
        final boolean useSpeakerForVideoCall = isSpeakerphoneAutoEnabledForVideoCalls(videoState);

        // Auto-enable speakerphone if the originating intent specified to do so, if the call
        // is a video call, of if using speaker when docked
        call.setStartWithSpeakerphoneOn(speakerphoneOn || useSpeakerForVideoCall
                || (useSpeakerWhenDocked && useSpeakerForDock));
        call.setVideoState(videoState);

        if (speakerphoneOn) {
            Log.i(this, "%s Starting with speakerphone as requested", call);
        } else if (useSpeakerWhenDocked && useSpeakerForDock) {
            Log.i(this, "%s Starting with speakerphone because car is docked.", call);
        } else if (useSpeakerForVideoCall) {
            Log.i(this, "%s Starting with speakerphone because its a video call.", call);
        }

        if (call.isEmergencyCall()) {
            new AsyncEmergencyContactNotifier(mContext).execute();
        }

        final boolean requireCallCapableAccountByHandle = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_requireCallCapableAccountForHandle);
        final boolean isOutgoingCallPermitted = isOutgoingCallPermitted(call,
                call.getTargetPhoneAccount());
        final String callHandleScheme =
                call.getHandle() == null ? null : call.getHandle().getScheme();
        if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) {
            // If the account has been set, proceed to place the outgoing call.
            // Otherwise the connection will be initiated when the account is set by the user.
            if (call.isSelfManaged() && !isOutgoingCallPermitted) {
                notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call);
            } else {
                if (call.isEmergencyCall()) {
                    // Drop any ongoing self-managed calls to make way for an emergency call
                    disconnectSelfManagedCalls("place emerg call" /* reason */);
                }

                if (mPendingMOEmerCall == null) {
                    //如果已設置帳戶,則繼續撥打呼出電話。否則,當用戶設置帳戶時將啓動連接。
                    call.startCreateConnection(mPhoneAccountRegistrar);//核心入口
                }
            }
        } else if (mPhoneAccountRegistrar.getCallCapablePhoneAccounts(
                requireCallCapableAccountByHandle ? callHandleScheme : null, false,
                call.getInitiatingUser()).isEmpty()) {
            markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.CANCELED,
                    "No registered PhoneAccounts"));
            markCallAsRemoved(call);
        }
    }

 

Call.java

    //啓動創建連接序列。完成後,應該存在通過連接服務的活動連接(   
    void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
        if (mCreateConnectionProcessor != null) {
            return;
        }
        mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
                phoneAccountRegistrar, mContext);  //Call 由 CallsManager 維護的撥號,或者來電實例
        mCreateConnectionProcessor.process();//核心入口
    }

CreateConnectionProcessor.java

    @VisibleForTesting
    public void process() {
        Log.v(this, "process");
        clearTimeout();
        mAttemptRecords = new ArrayList<>();
        if (mCall.getTargetPhoneAccount() != null) {
            mAttemptRecords.add(new CallAttemptRecord(
                    mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));
        }
        if (!mCall.isSelfManaged()) {
            adjustAttemptsForConnectionManager();
            adjustAttemptsForEmergency(mCall.getTargetPhoneAccount());
        }
        mAttemptRecordIterator = mAttemptRecords.iterator();
        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());// 取到一個 ConnectionServiceWrapper (有緩存)
            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);
                if (mCall.isIncoming()) {
                    mService.createConnection(mCall, CreateConnectionProcessor.this); // 開始進行鏈接,核心過程
                } else {
                    // Start to create the connection for outgoing call after the ConnectionService
                    // of the call has gained the focus.
                    mCall.getConnectionServiceFocusManager().requestFocus(
                            mCall,
                            new CallsManager.RequestCallback(new CallsManager.PendingAction() {
                                @Override
                                public void performAction() {
                                    Log.d(this, "perform create connection");
                                    mService.createConnection(
                                            mCall,
                                            CreateConnectionProcessor.this);
                                }
                            }));

                }
            }
        } else {
            DisconnectCause disconnectCause = mLastErrorDisconnectCause != null ?
                    mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR);
            notifyCallConnectionFailure(disconnectCause);
        }
    }

ConnectionServiceWrapper.java

ConnectionServiceWrapper 負責和 Telephony 溝通  framework/base/telephony

創建鏈接就是一個綁定服務的過程  

/**
     * 爲新的傳出呼叫或附加到現有的傳入呼叫創建新連接。
     */
    @VisibleForTesting
    public void createConnection(final Call call, final CreateConnectionResponse response) {//創建鏈接就是一個綁定服務的過程
        Log.d(this, "createConnection(%s) via %s.", call, getComponentName());
        BindCallback callback = new BindCallback() {// 鏈接創建成功後 onSuccess 會被調用
            @Override
            public void onSuccess() {
               .......
               ........
                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() {
              ....
            }
        };
        //綁定
        mBinder.bind(callback, call);
    }

進入mBinder.bind中   ConnectionSerivceWrapper.java是ServiceBinder的子類

bind是進行綁定服務,回調裏面的BindCallbak纔會創建

SerivceBinder.java

  void bind(BindCallback callback, Call call) {
             ......

            //在類裏面綁定成功後mBinder.bind(callback, call);在父類ServiceBinder類裏面mCallbacks.add(callback);吧回調用集合給裝載起來,然後進行便利數據
            //成功獲取到aidl接口後將其賦值mServiceInterface,
            mCallbacks.add(callback);
            if (mServiceConnection == null) {
                Intent serviceIntent = new Intent(mServiceAction).setComponent(mComponentName);
                ServiceConnection connection = new ServiceBinderConnection(call);

                final int bindingFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
                final boolean isBound;
                if (mUserHandle != null) {// 進行綁定
                    isBound = mContext.bindServiceAsUser(serviceIntent, connection, bindingFlags,  //如果綁定成功調用connection方法  這裏綁定是綁定了framework/telepony
                            mUserHandle);
                } else {
                    isBound = mContext.bindService(serviceIntent, connection, bindingFlags);
                }
                if (!isBound) {
                    handleFailedConnection();
                    return;
                }
            } else {
                Log.d(ServiceBinder.this, "Service is already bound.");
                Preconditions.checkNotNull(mBinder);
                handleSuccessfulConnection();
            }
        }
    }

這裏綁定的是ConnectionSerivce  如果綁定成功調用connection方法  這裏綁定的是framework/telecomm中的ConnectionSerivce
執行到bindSerivce就已經初始化ConnectionSerivce了 ,然後回調connection,connection繼承了SeviceConnection,所以連接成功後會回調回來。

 private final class ServiceBinderConnection implements ServiceConnection {
        /**
         * 綁定服務的初始調用。
         */
        private Call mCall;

        ServiceBinderConnection(Call call) {
            mCall = call;
        }

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder binder) {
            try {
                Log.startSession("SBC.oSC");
                synchronized (mLock) {
                    Log.i(this, "Service bound %s", componentName);

                    Log.addEvent(mCall, LogUtils.Events.CS_BOUND, componentName);
                    mCall = null;

                    // 取消綁定請求被排隊,因此立即取消綁定。
                    if (mIsBindingAborted) {
                        clearAbort();
                        logServiceDisconnected("onServiceConnected");
                        mContext.unbindService(this);
                        handleFailedConnection();
                        return;
                    }
                    if (binder != null) {
                        mServiceDeathRecipient = new ServiceDeathRecipient(componentName);
                        try {

                            binder.linkToDeath(mServiceDeathRecipient, 0);
                            mServiceConnection = this;
                            // 設置 mServiceInterface
                            // 會觸發 ConnectionServiceWrapper.setServiceInterface  ==>  ConnectionServiceWrapper.addConnectionServiceAdapter
                            // 通過mServiceInterface,給綁定的服務,提供一個訪問自己的接口
                            setBinder(binder);// 觸發bind(BindCallback callback, Call call) 中 callback 的 onSuccess
                            handleSuccessfulConnection();  //回調進入子類
                            //整個綁定過程,只做2件事,一是給遠程服務提供訪問自己的接口,二是利用遠程接口創建一個通話鏈接。
                            // 這2件事都是跨進程進行的。遠程服務訪問自己的接口是
                        } catch (RemoteException e) {
                            Log.w(this, "onServiceConnected: %s died.");
                            if (mServiceDeathRecipient != null) {
                                mServiceDeathRecipient.binderDied();
                            }
                        }
                    }
                }
            } finally {
                Log.endSession();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {//連接失敗
           ........ 
        }
    }

  private void setBinder(IBinder binder) {
        if (mBinder != binder) {
            if (binder == null) {
                  ......
            } else {
                mBinder = binder;
                setServiceInterface(binder);//進入子類
            }
        }
    }

  private void handleSuccessfulConnection() {
        for (BindCallback callback : mCallbacks) {
            callback.onSuccess();  //回調傳入的callback
        }
        mCallbacks.clear();
    }

這裏的setBinder();
這個方法主要是調用子類去與ConnectionService進行綁定  ,綁定成功後可以進行隨時改變InCallUI的狀態  。

 



這裏我們講解一下綁定的邏輯

綁定的入口在ConnectionServiceWrapper.java中開始綁入口,在哪createConnection方法中

先看流程圖  流程圖從ConnectionSerivceWrapper中的createConnection入口

這裏連接我會分開寫,單獨寫一篇文章已代碼方式進行處理

鏈接:https://blog.csdn.net/qq_35427437/article/details/101067804

流程圖:

 



ConnectionServiceWrapper.java

ConnectionServiceWrapper.java中的BindCallback裏面的onSuccess方法

public void onSuccess() {
                .....

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

利用遠程接口創建一個通話鏈接完成,進入createConnection中進行處理電話

第四部 Phone進程

ConnectionService.java

 public void createConnection(
                PhoneAccountHandle connectionManagerPhoneAccount,
                String id,
                ConnectionRequest request,
                boolean isIncoming,
                boolean isUnknown,
                Session.Info sessionInfo) {
            Log.startSession(sessionInfo, SESSION_CREATE_CONN);
            try {
                SomeArgs args = SomeArgs.obtain();
                args.arg1 = connectionManagerPhoneAccount;
                args.arg2 = id;
                args.arg3 = request;
                args.arg4 = Log.createSubsession();
                args.argi1 = isIncoming ? 1 : 0;
                args.argi2 = isUnknown ? 1 : 0;
                mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget();
            } finally {
                Log.endSession();
            }
        }

 

傳遞給Handler

 private final Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
        
                case MSG_CREATE_CONNECTION: { // 創建鏈接
                    SomeArgs args = (SomeArgs) msg.obj;
                    Log.continueSession((Session) args.arg4, SESSION_HANDLER + SESSION_CREATE_CONN);
                    try {
                        final PhoneAccountHandle connectionManagerPhoneAccount =
                                (PhoneAccountHandle) args.arg1;
                        final String id = (String) args.arg2;
                        final ConnectionRequest request = (ConnectionRequest) args.arg3;
                        final boolean isIncoming = args.argi1 == 1;
                        final boolean isUnknown = args.argi2 == 1;
                        if (!mAreAccountsInitialized) {
                            Log.d(this, "Enqueueing pre-init request %s", id);
                            mPreInitializationConnectionRequests.add(
                                    new android.telecom.Logging.Runnable(
                                            SESSION_HANDLER + SESSION_CREATE_CONN + ".pICR",
                                            null /*lock*/) {
                                @Override
                                public void loggedRun() {
                                    createConnection(
                                            connectionManagerPhoneAccount,
                                            id,
                                            request,
                                            isIncoming,
                                            isUnknown);
                                }
                            }.prepare());
                        } else {
                            createConnection(
                                    connectionManagerPhoneAccount,
                                    id,
                                    request,
                                    isIncoming,
                                    isUnknown);
                        }
                    } finally {
                        args.recycle();
                        Log.endSession();
                    }
                    break;
                }
}

進入createConnection()方法


    private void createConnection(
            final PhoneAccountHandle callManagerAccount,
            final String callId,
            final ConnectionRequest request,
            boolean isIncoming,
            boolean isUnknown) {
        boolean isLegacyHandover = request.getExtras() != null &&
                request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER, false);
        boolean isHandover = request.getExtras() != null && request.getExtras().getBoolean(
                TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, false);
        Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
                        "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b",
                callManagerAccount, callId, request, isIncoming, isUnknown, isLegacyHandover,
                isHandover);

        Connection connection = null;
        if (isHandover) {
            PhoneAccountHandle fromPhoneAccountHandle = request.getExtras() != null
                    ? (PhoneAccountHandle) request.getExtras().getParcelable(
                    TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT) : null;
            if (!isIncoming) {
                connection = onCreateOutgoingHandoverConnection(fromPhoneAccountHandle, request);
            } else {
                connection = onCreateIncomingHandoverConnection(fromPhoneAccountHandle, request);
            }
        } else {
            connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
                    : isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
                    : onCreateOutgoingConnection(callManagerAccount, request);//onCreateOutgoingConnection撥號入口
        }
       
        if (connection == null) {
            Log.i(this, "createConnection, implementation returned null connection.");
            connection = Connection.createFailedConnection(
                    new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION"));
        }

        connection.setTelecomCallId(callId);
        if (connection.getState() != Connection.STATE_DISCONNECTED) {
            // 監聽 Connection 的狀態變化,通知 system 進程(CallsManager)更新
            // 通過 system 進程提供的 adapter 實現
            addConnection(request.getAccountHandle(), callId, connection);
        }

        Uri address = connection.getAddress();
        String number = address == null ? "null" : address.getSchemeSpecificPart();
     

        Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);
        //mAdapter.handleCreateConnectionComplete 調用回到 ConnectionServiceWrapper 的 handleCreateConnectionComplete,
        // 然後回到 Call 的 handleCreateConnectionSuccess
        mAdapter.handleCreateConnectionComplete(// 通知 system 進程(即CallsManager) 電話鏈接已經創建完成
                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.getConnectElapsedTimeMillis(),
                        connection.getStatusHints(),
                        connection.getDisconnectCause(),
                        createIdList(connection.getConferenceables()),
                        connection.getExtras()));

        if (isIncoming && request.shouldShowIncomingCallUi() &&
                (connection.getConnectionProperties() & Connection.PROPERTY_SELF_MANAGED) ==
                        Connection.PROPERTY_SELF_MANAGED) {
            // Tell ConnectionService to show its incoming call UX.
            connection.onShowIncomingCallUi();
        }
        if (isUnknown) {
            triggerConferenceRecalculate();
        }
    }

 

進入onCreateOutgoingConnection()方法這個方法在子類實現,而子類在packeger/service/telephony裏面

TelephonyConnectionService.java

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

  
            if (resultConnection instanceof TelephonyConnection) {
                if (request.getExtras() != null && request.getExtras().getBoolean(
                        TelecomManager.EXTRA_USE_ASSISTED_DIALING, false)) {
                    ((TelephonyConnection) resultConnection).setIsUsingAssistedDialing(true);
                }
                placeOutgoingConnection((TelephonyConnection) resultConnection, phone, request);//進行撥號流程,主流程
            
            return resultConnection;
        }
    }

private void placeOutgoingConnection(
            TelephonyConnection connection, Phone phone, ConnectionRequest request) {
        placeOutgoingConnection(connection, phone, request.getVideoState(), request.getExtras());
    }

    private void placeOutgoingConnection(
            TelephonyConnection connection, Phone phone, int videoState, Bundle extras) {
        String number = connection.getAddress().getSchemeSpecificPart();
        boolean isAddParticipant = (extras != null) && extras
                .getBoolean(TelephonyProperties.ADD_PARTICIPANT_KEY, false);
        Log.d(this, "placeOutgoingConnection isAddParticipant = " + isAddParticipant);

        updatePhoneAccount(connection, phone);

        com.android.internal.telephony.Connection originalConnection = null;
        try {
            if (phone != null) {
                if (isAddParticipant) {
                    phone.addParticipant(number);// 爲緊急號碼做一些處理
                    return;
                } else {
                    originalConnection = phone.dial(number, new ImsPhone.ImsDialArgs.Builder()    //呼叫
                            .setVideoState(videoState)
                            .setIntentExtras(extras)
                            .setRttTextStream(connection.getRttTextStream())
                            .build());
                }
            }
        } catch (CallStateException e) {     // 失敗處理
            Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e);
            int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
            if (e.getError() == CallStateException.ERROR_OUT_OF_SERVICE) {
                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(), phone.getPhoneId()));
            connection.clearOriginalConnection();
            connection.destroy();
            return;
        }

        if (originalConnection == null) { // 失敗處理
            int telephonyDisconnectCause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
            // On GSM phones, null connection means that we dialed an 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", phone.getPhoneId()));
            connection.clearOriginalConnection();
            connection.destroy();
        } else {
            connection.setOriginalConnection(originalConnection); // TelephonyConnection 監聽 phone, 更新時,從originalConnection裏面取。
    //TelephonyConnection.setOriginalConnection 的實現, 目的主要是爲將 mHandler 註冊註冊到 
//phone 的監聽者列表裏面, phone 變化, 會觸發 mHandler 裏面的handleMessage 被調用。
        }
    }

placeOutgoingConnection只做2件事,一是使用對應的phone創建對應的連接,二是讓TelephonyConnectijon和連接建立起關聯,一個TelephonyConnection維護一個連接,Original變化會引起TelephonyCnnnection變化。

 

 

 

進入了framewrok/opt/telephony

GsmCdmaPhone.java

現在流程回到 phone.dial 只分析GSM類型的phone, GSMPhone.dial 如下

 @Override
    public Connection dial(String dialString, @NonNull DialArgs dialArgs)
            throws CallStateException {
        if (!isPhoneTypeGsm() && dialArgs.uusInfo != null) {
            throw new CallStateException("Sending UUS information NOT supported in CDMA!");
        }

        boolean isEmergency = isEmergencyNumber(dialString);
        Phone imsPhone = mImsPhone;

        CarrierConfigManager configManager =
                (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
        boolean alwaysTryImsForEmergencyCarrierConfig = configManager.getConfigForSubId(getSubId())
                .getBoolean(CarrierConfigManager.KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL);

        boolean useImsForCall = isImsUseEnabled()
                 && imsPhone != null
                 && (imsPhone.isVolteEnabled() || imsPhone.isWifiCallingEnabled() ||
                 (imsPhone.isVideoEnabled() && VideoProfile.isVideo(dialArgs.videoState)))
                 && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
                 && !shallDialOnCircuitSwitch(dialArgs.intentExtras);

        boolean useImsForEmergency = imsPhone != null
                && isEmergency
                && alwaysTryImsForEmergencyCarrierConfig
                && ImsManager.getInstance(mContext, mPhoneId).isNonTtyOrTtyOnVolteEnabled()
                && imsPhone.isImsAvailable();

        String dialPart = PhoneNumberUtils.extractNetworkPortionAlt(PhoneNumberUtils.
                stripSeparators(dialString));
        boolean isUt = (dialPart.startsWith("*") || dialPart.startsWith("#"))
                && dialPart.endsWith("#");

        boolean useImsForUt = imsPhone != null && imsPhone.isUtEnabled();

        if (DBG) {
            logd("useImsForCall=" + useImsForCall
                    + ", useImsForEmergency=" + useImsForEmergency
                    + ", useImsForUt=" + useImsForUt
                    + ", isUt=" + isUt
                    + ", imsPhone=" + imsPhone
                    + ", imsPhone.isVolteEnabled()="
                    + ((imsPhone != null) ? imsPhone.isVolteEnabled() : "N/A")
                    + ", imsPhone.isVowifiEnabled()="
                    + ((imsPhone != null) ? imsPhone.isWifiCallingEnabled() : "N/A")
                    + ", imsPhone.isVideoEnabled()="
                    + ((imsPhone != null) ? imsPhone.isVideoEnabled() : "N/A")
                    + ", imsPhone.getServiceState().getState()="
                    + ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A"));
        }

        Phone.checkWfcWifiOnlyModeBeforeDial(mImsPhone, mPhoneId, mContext);

        if ((useImsForCall && !isUt) || (isUt && useImsForUt) || useImsForEmergency) {
            try {
                if (DBG) logd("Trying IMS PS call");
                return imsPhone.dial(dialString, dialArgs);
            } catch (CallStateException e) {
                if (DBG) logd("IMS PS call exception " + e +
                        "useImsForCall =" + useImsForCall + ", imsPhone =" + imsPhone);
                // Do not throw a CallStateException and instead fall back to Circuit switch
                // for emergency calls and MMI codes.
                if (Phone.CS_FALLBACK.equals(e.getMessage()) || isEmergency) {
                    logi("IMS call failed with Exception: " + e.getMessage() + ". Falling back "
                            + "to CS.");
                } else {
                    CallStateException ce = new CallStateException(e.getMessage());
                    ce.setStackTrace(e.getStackTrace());
                    throw ce;
                }
            }
        }

        if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_OUT_OF_SERVICE
                && mSST.mSS.getDataRegState() != ServiceState.STATE_IN_SERVICE && !isEmergency) {
            throw new CallStateException("cannot dial in current state");
        }
        // Check non-emergency voice CS call - shouldn't dial when POWER_OFF
        if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_POWER_OFF /* CS POWER_OFF */
                && !VideoProfile.isVideo(dialArgs.videoState) /* voice call */
                && !isEmergency /* non-emergency call */
                && !(isUt && useImsForUt) /* not UT */) {
            throw new CallStateException(
                CallStateException.ERROR_POWER_OFF,
                "cannot dial voice call in airplane mode");
        }
        // Check for service before placing non emergency CS voice call.
        // Allow dial only if either CS is camped on any RAT (or) PS is in LTE service.
        if (mSST != null
                && mSST.mSS.getState() == ServiceState.STATE_OUT_OF_SERVICE /* CS out of service */
                && !(mSST.mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE
                    && ServiceState.isLte(mSST.mSS.getRilDataRadioTechnology())) /* PS not in LTE */
                && !VideoProfile.isVideo(dialArgs.videoState) /* voice call */
                && !isEmergency /* non-emergency call */) {
            throw new CallStateException(
                CallStateException.ERROR_OUT_OF_SERVICE,
                "cannot dial voice call in out of service");
        }
        if (DBG) logd("Trying (non-IMS) CS call");

        if (isPhoneTypeGsm()) {
            return dialInternal(dialString, new DialArgs.Builder<>()
                    .setIntentExtras(dialArgs.intentExtras)
                    .build());
        } else {
            return dialInternal(dialString, dialArgs);
        }
    }

進入dialInternal方法

  @Override
    protected Connection dialInternal(String dialString, DialArgs dialArgs)
            throws CallStateException {
        return dialInternal(dialString, dialArgs, null);
    }

    protected Connection dialInternal(String dialString, DialArgs dialArgs,
            ResultReceiver wrappedCallback)
            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(), wrappedCallback);
            if (DBG) logd("dialInternal: dialing w/ mmi '" + mmi + "'...");

            if (mmi == null) { //return mCT.dial(newDialString, uusInfo, intentExtras);
                return mCT.dial(newDialString, dialArgs.uusInfo, dialArgs.intentExtras);   // 流程會進入這裏, mCT 是 GsmCdmaCallTracker
            } else if (mmi.isTemporaryModeCLIR()) {
                return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), dialArgs.uusInfo,
                        dialArgs.intentExtras);
            } else { // MTK 爲了支持VT加入的代碼
                mPendingMMIs.add(mmi);
                mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
                mmi.processCode();
                return null;
            }
        } else {
            return mCT.dial(newDialString);
        }
    }

GsmCallTracker 調用CI 執行指令,返回結果,通知GSMPhone

GsmCallTracker 會藉助 mCi 發送指令,這裏面會將包含 mCi 執行結果的 Message 提前發送出去,等到 mCi 執行完畢後, Message 會被髮送到對應的 Handler 裏面去。

  //GSM
    /**
     * clirMode is one of the CLIR_ constants  呼出入口
     */
    public synchronized Connection dial(String dialString, int clirMode, UUSInfo uusInfo,
                                        Bundle intentExtras)
            throws CallStateException {
        // note that this triggers call state changed notif
        clearDisconnected();

        if (!canDial()) {
            throw new CallStateException("cannot dial in current state");
        }

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

        if (mForegroundCall.getState() != GsmCdmaCall.State.IDLE) {
            //we should have failed in !canDial() above before we get here
            throw new CallStateException("cannot dial in current state");
        }
        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);// 撥號時設置不能靜音
            boolean isPhoneInEcmMode = EcbmHandler.getInstance().isInEcm();
            // In Ecm mode, if another emergency call is dialed, Ecm mode will not exit.
            if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) {
                mCi.dial(mPendingMO.getAddress(), clirMode, uusInfo, obtainCompleteMessage());// 正常撥號// 需要注意, EVENT_DIAL_CALL_RESULT 是對應操作的一個句柄, 等到結果返回時, 會讓 Handler 進行處理
            } else {
                EcbmHandler emergencyHandler = EcbmHandler.getInstance();
                try {
                    emergencyHandler.exitEmergencyCallbackMode();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                emergencyHandler.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null);
                mPendingCallClirMode = clirMode;
                mPendingCallUusInfo = uusInfo;
                mPendingCallInEcm = true;
            }
        }
        if (mNumberConverted) {
            mPendingMO.setConverted(origNumber);
            mNumberConverted = false;
        }

        updatePhoneState();
        mPhone.notifyPreciseCallStateChanged();

        return mPendingMO;
    }

GsmCallTracker 本身就是一個 Handler, 它的 handleMessage 將會處理 mCi 返回的結果。

public void handleMessage(Message msg) {
        AsyncResult ar;

        switch (msg.what) {
            case EVENT_OPERATION_COMPLETE:
                 // 再次獲取最新信息
                operationComplete();
            break;
           省略.......
        }
}

mCi 獲取最新狀態, 返回到 handleMessage 的 EVENT_POLL_CALLS_RESULT

  private void operationComplete() {
        mPendingOperations--;

        if (DBG_POLL) log("operationComplete: pendingOperations=" +
                mPendingOperations + ", needsPoll=" + mNeedsPoll);

        if (mPendingOperations == 0 && mNeedsPoll) {
            mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);//EVENT_POLL_CALLS_RESULT 在調用Handler
            mCi.getCurrentCalls(mLastRelevantPoll);//獲取最新狀態
        } else if (mPendingOperations < 0) {
            // this should never happen
            Rlog.e(LOG_TAG,"GsmCdmaCallTracker.pendingOperations < 0");
            mPendingOperations = 0;
        }
    }
   @Override
    public void handleMessage(Message msg) {
        AsyncResult ar;

        switch (msg.what) {
            case EVENT_POLL_CALLS_RESULT:
                Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received");

                if (msg == mLastRelevantPoll) {
                    if (DBG_POLL) log(
                            "handle EVENT_POLL_CALL_RESULT: set needsPoll=F");
                    mNeedsPoll = false;
                    mLastRelevantPoll = null;
                    handlePollCalls((AsyncResult)msg.obj);//handlePollCalls 處理4種狀態後,通知 phone 更新狀態。
                }
            break;
        }
}

handlePollCalls出來電話轉態太長 剪數據

   @Override
    protected synchronized void handlePollCalls(AsyncResult ar) {//handlePollCalls 處理4種狀態後,通知 phone 更新狀態。
        List polledCalls;

         .....

        //CDMA
        boolean noConnectionExists = true;

        for (int i = 0, curDC = 0, dcSize = polledCalls.size()
                ; i < mConnections.length; i++) {
            GsmCdmaConnection conn = mConnections[i];
            DriverCall dc = null;

            // polledCall list is sparse
            if (curDC < dcSize) {
                dc = (DriverCall) polledCalls.get(curDC);

                if (dc.index == i+1) {
                    curDC++;
                } else {
                    dc = null;
                }
            }

            //CDMA
            if (conn != null || dc != null) {// 新的電話
                noConnectionExists = false;
            }

            if (DBG_POLL) log("poll: conn[i=" + i + "]=" +
                    conn+", dc=" + dc);

            if (conn == null && dc != null) {
                // Connection appeared in CLCC response that we don't know about
                if (mPendingMO != null && mPendingMO.compareTo(dc)) {

                    ......
                    if (mHangupPendingMO) {
                     ......
                    }
                } else {
                .....
                }
                hasNonHangupStateChanged = true;
            } else if (conn != null && dc == null) { // 舊的電話消失,可能時掛斷
                if (isPhoneTypeGsm()) {
                    // Connection missing in CLCC response that we were
                    // tracking.
                    mDroppedDuringPoll.add(conn);
                } else {
                .....
                  
                }
                // Dropped connections are removed from the CallTracker
                // list but kept in the Call list
                mConnections[i] = null;
            } else if (conn != null && dc != null && !conn.compareTo(dc) && isPhoneTypeGsm()) {
                // Connection in CLCC response does not match what
                // we were tracking. Assume dropped call and new call

                mDroppedDuringPoll.add(conn);
                mConnections[i] = new GsmCdmaConnection (mPhone, dc, this, i);

                if (mConnections[i].getCall() == mRingingCall) {
                    newRinging = mConnections[i];
                } // else something strange happened
                hasNonHangupStateChanged = true;
            } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */ // 舊的電話更新, 正在打的電話時,狀態更新
             ......
            }

            if (REPEAT_POLLING) {
               ......
            }
        }

       
        if (!isPhoneTypeGsm() && noConnectionExists) {
            checkAndEnableDataCallAfterEmergencyCallDropped();
        }

        
        if (mPendingMO != null) {
            Rlog.d(LOG_TAG, "Pending MO dropped before poll fg state:"
                    + mForegroundCall.getState());

            mDroppedDuringPoll.add(mPendingMO);
            mPendingMO = null;
            mHangupPendingMO = false;

            if (!isPhoneTypeGsm()) {
                if( mPendingCallInEcm) {
                    mPendingCallInEcm = false;
                }
                checkAndEnableDataCallAfterEmergencyCallDropped();
            }
        }

        if (newRinging != null) {
            mPhone.notifyNewRingingConnection(newRinging);
        }

        // clear the "local hangup" and "missed/rejected call"
        // cases from the "dropped during poll" list
        // These cases need no "last call fail" reason
        ArrayList<GsmCdmaConnection> locallyDisconnectedConnections = new ArrayList<>();
        for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) {
            GsmCdmaConnection conn = mDroppedDuringPoll.get(i);
            //CDMA
            boolean wasDisconnected = false;

            if (conn.isIncoming() && conn.getConnectTime() == 0) {
                // Missed or rejected call
                int cause;
                if (conn.mCause == DisconnectCause.LOCAL) {
                    cause = DisconnectCause.INCOMING_REJECTED;
                } else {
                    cause = DisconnectCause.INCOMING_MISSED;
                }

                if (Phone.DEBUG_PHONE) {
                    log("missed/rejected call, conn.cause=" + conn.mCause);
                    log("setting cause to " + cause);
                }
                mDroppedDuringPoll.remove(i);
                hasAnyCallDisconnected |= conn.onDisconnect(cause);
                wasDisconnected = true;
                locallyDisconnectedConnections.add(conn);
            } else if (conn.mCause == DisconnectCause.LOCAL
                    || conn.mCause == DisconnectCause.INVALID_NUMBER) {
                mDroppedDuringPoll.remove(i);
                hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause);
                wasDisconnected = true;
                locallyDisconnectedConnections.add(conn);
            }

            if (!isPhoneTypeGsm() && wasDisconnected && unknownConnectionAppeared
                    && conn == newUnknownConnectionCdma) {
                unknownConnectionAppeared = false;
                newUnknownConnectionCdma = null;
            }
        }
        if (locallyDisconnectedConnections.size() > 0) {
            mMetrics.writeRilCallList(mPhone.getPhoneId(), locallyDisconnectedConnections);
        }

        /* Disconnect any pending Handover connections */
        for (Iterator<Connection> it = mHandoverConnections.iterator();
                it.hasNext();) {
           .....
        }

        // Any non-local disconnects: determine cause
        if (mDroppedDuringPoll.size() > 0) {
            mCi.getLastCallFailCause(
                obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE));
        }

        if (needsPollDelay) {// 過一會在獲取最新狀態
            pollCallsAfterDelay();
        }

        .....

        if (VDBG) log("handlePollCalls calling updatePhoneState()");
        updatePhoneState();   // 更新 phone 的狀態

        if (unknownConnectionAppeared) {
         ......
        }

        if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) {
            mPhone.notifyPreciseCallStateChanged();   // 通知 GSMPhone 狀態更新
            updateMetrics(mConnections);
        }

       
    }

更新Phone狀態

    private void updatePhoneState() {
        PhoneConstants.State oldState = mState;
        if (mRingingCall.isRinging()) {
            mState = PhoneConstants.State.RINGING;
        } else if (mPendingMO != null ||
                !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) {
            mState = PhoneConstants.State.OFFHOOK;
        } else {
            Phone imsPhone = mPhone.getImsPhone();
            if ( mState == PhoneConstants.State.OFFHOOK && (imsPhone != null)){
                imsPhone.callEndCleanupHandOverCallIfAny();
            }
            mState = PhoneConstants.State.IDLE;
        }

        if (mState == PhoneConstants.State.IDLE && oldState != mState) {
            mVoiceCallEndedRegistrants.notifyRegistrants(
                new AsyncResult(null, null, null));
        } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {
            mVoiceCallStartedRegistrants.notifyRegistrants (
                    new AsyncResult(null, null, null));
        }
        if (Phone.DEBUG_PHONE) {
            log("update phone state, old=" + oldState + " new="+ mState);
        }
        if (mState != oldState) {
            mPhone.notifyPhoneStateChanged();// 更新 phone 的狀態
            mMetrics.writePhoneState(mPhone.getPhoneId(), mState);
        }
    }

mPhone.notifyPhoneStateChanged => mNotifier.notifyPhoneState , mNotifier 是一個 DefaultPhoneNotifier , notifyPhoneState 會通知 telephony.registry 更新,telephony.registry 會通知所有調用了TelephonyManager.listen 的監聽者, 實際上,這是提供給第三方APP監聽Phone狀態的一個接口,當前沒用!!。

DefaultPhoneNotifier.java

 @Override
    public void notifyPhoneState(Phone sender) {
        Call ringingCall = sender.getRingingCall();
        int subId = sender.getSubId();
        int phoneId = sender.getPhoneId();
        String incomingNumber = "";
        if (ringingCall != null && ringingCall.getEarliestConnection() != null) {
            incomingNumber = ringingCall.getEarliestConnection().getAddress();
        }
        try {
            if (mRegistry != null) {
             // mRegistry 是一個遠程接口 (TelephonyRegistry)
            // protected DefaultPhoneNotifier() {
            //mRegistry                      
              =ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
            //        "telephony.registry"));
                  mRegistry.notifyCallStateForPhoneId(phoneId, subId,
                        PhoneConstantConversions.convertCallState(
                            sender.getState()), incomingNumber);
            }
        } catch (RemoteException ex) {
            // system process is dead
        }
    }

mPhone.notifyPreciseCallStateChanged() ==> PhoneBase.notifyPreciseCallStateChangedP() 這裏會通知 TelephonyConnection 更新。 觸發 TelephonyConnection 的 updateState()

 

 

 

轉態更新後返回Message 回調mCi.dial(mPendingMO.getAddress(), clirMode, uusInfo, obtainCompleteMessage()),接下來流程來到了 mCi.dial, mCi 是一個 CommandsInterface 接口, 由 GsmCallTracker 的構造可知, GsmCallTracker 的 mCi 來自於 GSMPhone 的 mCi, 即 RIL。

RIL.java

進入RIL.java

@Override
    public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
        IRadio radioProxy = getRadioProxy(result);

        Log.v("liwangjiangDialerRil","Ril address = "+address);
        if (radioProxy != null) {
            RILRequest rr = obtainRequest(RIL_REQUEST_DIAL, result,
                    mRILDefaultWorkSource);

            Dial dialInfo = new Dial();
            dialInfo.address = convertNullToEmptyString(address);
            dialInfo.clir = clirMode;
            if (uusInfo != null) {
                UusInfo info = new UusInfo();
                info.uusType = uusInfo.getType();
                info.uusDcs = uusInfo.getDcs();
                info.uusData = new String(uusInfo.getUserData());
                dialInfo.uusInfo.add(info);
            }

            if (RILJ_LOGD) {
                // Do not log function arg for privacy
                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
            }

            try {
                radioProxy.dial(rr.mSerial, dialInfo);
            } catch (RemoteException | RuntimeException e) {
                handleRadioProxyExceptionForRR(rr, "dial", e);
            }
        }
    }

 

RIL與通話模塊底層通訊過程

RIL通訊主要由RILSender和RILReceiver構成,用於通訊傳輸的載體的有 RILRequest,
Registrant(RegistrantList)。

  1. RILSender
    是一個Handler, 通過 #send(RILRequest) 將請求發送到 mSenderThread線程中,handleMessage 將請求寫入mSocket中。
  2. RILReceiver
    在run內無限輪詢,一旦讀取到通話底層返回的數據,交給 #processResponse(Parcel)處理。
    其中應答分爲有請求的應答,無請求的應答(即狀態改變後的反饋)
    1.RIL_REQUEST_xxxx 有請求
    2.RIL_UNSOL_xxxx 無請求
  3. RILRequest
    成員變量:
    1.mSerial 請求序號,唯一,保證請求和反饋的一致。
    2.mRequest 請求類型 即 RIL_xxxx。
    3.mResult 用來發送請求的結果的句柄,由RIL請求的調用者提供。
  4. Registrant
           在 RIL內部維護着一系列的RegistrantList(Registrant的集合),每個集合代表一種狀態改變的類型,對於有請求的應答,RIL是通過對應的mResult來發送結果的,但是對於無請求的應答,RIL是將反饋通知告訴對應的RegistrantList的(#notifyRegistrants),RegistrantList會通知每一個Registrant。在RIL的直接父類定義了這些RegistrantList,並提供了註冊到RegistrantList的方法(eg.#registerForCallStateChanged,#unregisterForCallStateChanged)所以,如果想要監聽通話狀態的變化,那麼就需要註冊監聽到對應的RegistrantList(監聽着需要提供一個handler和一個int和object,handler用來發送數據,int區分監聽的事件,object是額外的信息)。

 

最後到Log分析了

Log需要打開才能看,當前沒有編過,敬請期待!!

 

如果寫的可以  給個 贊 吧謝謝!!

 

?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章