Phone撥號調起InCallUi流程(Phone 9.0 )(Phone調用InCallUi流程)

 

Phone調用InCallUi流程

流程圖

主要的類以及作用


Dialer
Dialer\com\android\dialer\app\dialpad\DialpadFragment.java  撥號鍵盤
Dialer\java\com\android\dialer\util\DialerUtils.java                    撥號工具
Dialer\java\com\android\dialer\telecom\TelecomUtil.java         通訊工具 
Dialer\java\com\android\incallui\InCallServiceImpl.java             InCallUi開啓服務,他繼承的是InCallService


framework/base/telecom
framework\base\telecomm\java\android\telecom\TelecomManager.java   向外部提供接口,有很多地方會調用這個接口進行撥號


service/telecomm
services\Telecomm\src\com\android\server\telecom\components\TelecomService.java  系統初始化的時候就會進行綁定,並不                                                                                                                                              是在打電話的時候才綁TelecomSystem                                                                                                                                              會被初始化,期間構建一些列必須源,                                                                                                                                              包括Phone和CI(和底層溝通的對象)                                                                                                                                               的構建
services\Telecomm\src\com\android\server\telecom\TelecomServiceImpl.java               提供Binder接口
services\Telecomm\src\com\android\server\telecom\components\UserCallIntentProcessor.java  用戶撥號處理,處理 內部校驗                                                                                                                                                            一些撥號的權限,以及其它作                                                                                                                                                              權限看看是不是需要彈框絕。                                                                                                                                                             比如 《只允許打緊急電話沒                                                                                                                                                               有電話許可,此應用程序不能                                                                                                                                                                發 出呼叫》
services\Telecomm\src\com\android\server\telecom\components\PrimaryCallReceiver.java   廣播,作爲撥號的單一入口, 其到                                                                                                                                                      消息後,就轉發給 TelecomSystem                                                                                                                                                     的
services\Telecomm\src\com\android\server\telecom\CallIntentProcessor.java          判斷Intent裏面的數據,比如  號碼是否爲空                                                                                                                                       等等....
services\Telecomm\src\com\android\server\telecom\CallsManager.java            官方聲明  :CallManager對象爲 Phone應用程序                                                                                                                                提供通用 Call API的方法。 SipPhone類實現                                                                                                                                    Phone接口,但大部分由虛擬方法填充,實際現                                                                                                                               使用的是 SipManager,訪問 SIP通信框架並控制                                                                                                                              SIP Call
services\Telecomm\src\com\android\server\telecom\InCallController.java             控制電話App的邏輯和UI 

 

流程圖:

 

 

接下來開看代碼邏輯 一個一個進程看

概述:撥號調用InCallUi模塊的過程,如果按照進程來分,可以分成3個模塊。

  1. com.android.dialer進程,電話APP模塊                路徑  : android/vendor/codeaurora/commonsys/packages/apps/Dialer 
  2. com.android.dialer.InCallUi  呼出呼入頁面         路徑  :要看廠商是否分離了,如果是高通代碼就放在Dialer App中
  3. System進程,系統框架層    Service/telecom層  路徑  :android/packages/service
  4. com.android.phone進程,    framework/  :       路徑  : android/framework

撥號過程由App開始(或第三方調用Telephony),進入接口System進程獲取接口,System接口會phone的一個Binder中的一個方法進行處理,進入Phone後做一些判斷(比如:號碼是否正確), 在啓動InCallUil設置狀態 ,狀態改變 是通過Call對象進行變更

在電話中java層有三個Call對象  所在位置是  

  1. 路徑:android/packages/services/Telecomm 
  2. 路徑:android/frameworks/base/telecomm
  3. 路徑:android/frameworks/opt/telephony

插入一個話題,InCallUi  就來電去電時展示,如果想實現一個InCallUi自己也是可以做到的,比如第三方APP  來電秀,來電視屏等等..都是繼承了一個服務(InCallService,給第三方擴展使用),屏蔽調InCallUi所繼承的服務

大致流程圖:

 

Phone進程是

 

開始代碼流程:

 

第一步進入Dialer App

Dialer\java\com\android\dialer\app\dialpad\DialpadFragment.java  屬於Dialer撥號盤

 private void handleDialButtonPressed() {
    if (isDigitsEmpty()) { // No number entered.
      // No real call made, so treat it as a click  沒有真正打過電話,所以把它當作一次點擊
      PerformanceReport.recordClick(UiAction.Type.PRESS_CALL_BUTTON_WITHOUT_CALLING);
      //號碼空白自動補全
      handleDialButtonClickWithEmptyDigits();
    } else {
      final String number = mDigits.getText().toString();

      // "persist.radio.otaspdial" is a temporary hack needed for one carrier's automated
      // test equipment.
      // TODO: clean it up.
      if (number != null
          && !TextUtils.isEmpty(mProhibitedPhoneNumberRegexp)
          && number.matches(mProhibitedPhoneNumberRegexp)) {
        PerformanceReport.recordClick(UiAction.Type.PRESS_CALL_BUTTON_WITHOUT_CALLING);
        LogUtil.i(
            "DialpadFragment.handleDialButtonPressed",
            "The phone number is prohibited explicitly by a rule.");
        //處理一些不能撥打電話的情況
        if (getActivity() != null) {
          DialogFragment dialogFragment =
              ErrorDialogFragment.newInstance(R.string.dialog_phone_call_prohibited_message);
          dialogFragment.show(getFragmentManager(), "phone_prohibited_dialog");
        }

        // Clear the digits just in case.
        clearDialpad();
      } else {
        final Intent intent =
            new CallIntentBuilder(number, CallInitiationType.Type.DIALPAD).build();
         DialerUtils.startActivityWithErrorToast(getActivity(), intent);  //撥打進入撥打電話位置。核心,主流程
        if(mSubscriptionManager.getActiveSubscriptionInfoCount() != 0) {
          hideAndClearDialpad(false);
        }
// 		hideAndClearDialpad(false);
      }
    }
  }

DialerUtils.java  組要調用  placeCallOrMakeToast 進入TelecomUtil.placeCall()

 public static void startActivityWithErrorToast(
      final Context context, final Intent intent, int msgId) {
    try {
      if ((Intent.ACTION_CALL.equals(intent.getAction()))) {
        // All dialer-initiated calls should pass the touch point to the InCallUI
        Point touchPoint = TouchPointManager.getInstance().getPoint();
        if (touchPoint.x != 0 || touchPoint.y != 0) {
          Bundle extras;
          // Make sure to not accidentally clobber any existing extras
          if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) {
            extras = intent.getParcelableExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
          } else {
            extras = new Bundle();
          }
          extras.putParcelable(TouchPointManager.TOUCH_POINT, touchPoint);
          intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
        }

        if (shouldWarnForOutgoingWps(context, intent.getData().getSchemeSpecificPart())) {
          LogUtil.i(
              "DialUtils.startActivityWithErrorToast",
              "showing outgoing WPS dialog before placing call");
          AlertDialog.Builder builder = new AlertDialog.Builder(context);
          builder.setMessage(R.string.outgoing_wps_warning);//彈出對話框 撥打 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);  //核心進入模塊
    if (!hasCallPermission) {
      // TODO: Make calling activity show request permission dialog and handle
      // callback results appropriately.
      Toast.makeText(context, "Cannot place call without Phone permission", Toast.LENGTH_SHORT)
          .show();
    }
  }

TelecomUtil.java  主要是調用TelecomManage.placeCall()方法

  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/telecom

TelecomManager.java

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

第三部 進入System進程 packages/services/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) {
            NotificationChannelManager notificationChannelManager =
                    new NotificationChannelManager();
            notificationChannelManager.createChannels(context);

            boolean shouldPauseBetweenRingtoneRepeat = context.getResources().getBoolean(
                    R.bool.should_pause_between_ringtone_repeats);
            // 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);
        }
        // number into the personal dialer.檢查DISALLOW_OUTGOING_CALLS限制。注意:我們在託管配置文件用戶中跳過此檢查,因爲總是可以通過複製和粘貼電話號碼到個人撥號器來繞過此檢查。
        if (!UserUtil.isManagedProfile(mContext, mUserHandle)) {
            // Only emergency calls are allowed for users with the DISALLOW_OUTGOING_CALLS
            // 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);
                    Log.w(this, "Rejecting non-emergency phone call due to DISALLOW_OUTGOING_CALLS "
                            + "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);
            Log.w(this, "Rejecting non-emergency phone call because "
                    + android.Manifest.permission.CALL_PHONE + " permission is not granted.");
            return;
        }

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

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

        // Save the user handle of current user before forwarding the intent to primary user.在將意圖轉發給主用戶之前,保存當前用戶的用戶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) {

            Log.i(this, "sendIntentToDestination: send intent to Telecom directly.");
            synchronized (TelecomSystem.getInstance().getLock()) {
              TelecomSystem.getInstance().getCallIntentProcessor().processIntent(intent);
            }
        } else {
            Log.i(this, "sendIntentToDestination: trampoline to Telecom.");
            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);
            // Show the toast to warn user that it is a personal call though initiated in work
            // profile.
            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");
        }

        Log.d(CallIntentProcessor.class, " processOutgoingCallIntent handle = " + handle
                + ", scheme = " + scheme + ", uriString = " + uriString
                + ", isSkipSchemaParsing = " + isSkipSchemaParsing
                + ", isAddParticipant = " + isAddParticipant);

        UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER);

        /**
         * description  帳號選擇
         */
        /**start*/
        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

CallManager.java
 

    public Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras,
            UserHandle initiatingUser, Intent originalIntent) {
        boolean isReusedCall = true;
        Call call = reuseOutgoingCall(handle);

        PhoneAccount account =
                mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser);
        boolean isSelfManaged = account != null && account.isSelfManaged();

        if (call == null) {
            call = new Call(getNextCallId(), mContext,
                    this,
                    mLock,
                    mConnectionServiceRepository,
                    mContactsAsyncHelper,
                    mCallerInfoAsyncQueryFactory,
                    mPhoneNumberUtilsAdapter,
                    handle,
                    null /* gatewayInfo */,
                    null /* connectionManagerPhoneAccount */,
                    null /* phoneAccountHandle */,
                    Call.CALL_DIRECTION_OUTGOING /* callDirection */,
                    false /* forceAttachToExistingConnection */,
                    false, /* isConference */
                    mClockProxy);
            if ((extras != null) &&
                    extras.getBoolean(TelephonyProperties.EXTRA_DIAL_CONFERENCE_URI, false)) {
                //Reset PostDialDigits with empty string for ConfURI call.
                call.setPostDialDigits("");
            }
            call.initAnalytics();

            call.setIsSelfManaged(isSelfManaged);
            if (isSelfManaged) {
                // Self-managed calls will ALWAYS use voip audio mode.
                call.setIsVoipAudioMode(true);
            }
            call.setInitiatingUser(initiatingUser);
            isReusedCall = false;
        }

        int videoState = VideoProfile.STATE_AUDIO_ONLY;
        if (extras != null) {
            videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                    VideoProfile.STATE_AUDIO_ONLY);
            if (VideoProfile.isVideo(videoState)) {
                if (call.isEmergencyCall() && account != null &&
                        !account.hasCapabilities(PhoneAccount.CAPABILITY_EMERGENCY_VIDEO_CALLING)) {
                    Log.i(this, "startOutgoingCall - emergency video calls not supported; " +
                            "falling back to audio-only");
                    videoState = VideoProfile.STATE_AUDIO_ONLY;
                } else if (account != null &&
                        !account.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING)) {
                    videoState = VideoProfile.STATE_AUDIO_ONLY;
                }
            }

            call.setVideoState(videoState);
        }

        boolean isAddParticipant = ((extras != null) && (extras.getBoolean(
                TelephonyProperties.ADD_PARTICIPANT_KEY, false)));
        boolean isSkipSchemaOrConfUri = ((extras != null) && (extras.getBoolean(
                TelephonyProperties.EXTRA_SKIP_SCHEMA_PARSING, false) ||
                extras.getBoolean(TelephonyProperties.EXTRA_DIAL_CONFERENCE_URI, false)));

        if (isAddParticipant) {
            String number = handle.getSchemeSpecificPart();
            if (!isSkipSchemaOrConfUri) {
                number = PhoneNumberUtils.stripSeparators(number);
            }
            addParticipant(number);
            mInCallController.bringToForeground(false);
            return null;
        }
        // Force tel scheme for ims conf uri/skip schema calls to avoid selection of sip accounts
        String scheme = (isSkipSchemaOrConfUri? PhoneAccount.SCHEME_TEL: handle.getScheme());

        Log.d(this, "startOutgoingCall :: isAddParticipant=" + isAddParticipant
                + " isSkipSchemaOrConfUri=" + isSkipSchemaOrConfUri + " scheme=" + scheme);

        List<PhoneAccountHandle> potentialPhoneAccounts = findOutgoingCallPhoneAccount(
                phoneAccountHandle, handle, VideoProfile.isVideo(videoState),
                initiatingUser, scheme);
        if (potentialPhoneAccounts.size() == 1) {
            phoneAccountHandle = potentialPhoneAccounts.get(0);
        } else {
            phoneAccountHandle = null;
        }
        call.setTargetPhoneAccount(phoneAccountHandle);

        boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle) && !isSelfManaged;

        if (!isPotentialInCallMMICode && (!isReusedCall
                && !makeRoomForOutgoingCall(call, call.isEmergencyCall()))) {
            Call foregroundCall = getForegroundCall();
            Log.d(this, "No more room for outgoing call %s ", call);
            if (foregroundCall.isSelfManaged()) {
                //如果正在進行的調用是一個自管理的調用,則提示用戶詢問是否要斷開正在進行的調用並將傳出的調用放置。
                call.setOriginalCallIntent(originalIntent);
                startCallConfirmation(call);
            } else {
                // 如果正在進行的呼叫是託管呼叫,我們將阻止傳出呼叫撥號
                notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call);
            }
            return null;
        }

        // The outgoing call can be placed, go forward.

        boolean needsAccountSelection =
                phoneAccountHandle == null && potentialPhoneAccounts.size() > 1
                        && !call.isEmergencyCall() && !isSelfManaged;
        if (needsAccountSelection) {
    
            call.setState(CallState.SELECT_PHONE_ACCOUNT, "needs account selection");
            extras = new Bundle(extras);
            extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS,
                    potentialPhoneAccounts);
        } else {
            PhoneAccount accountToUse =
                    mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser);
            if (accountToUse != null && accountToUse.getExtras() != null) {
                if (accountToUse.getExtras()
                        .getBoolean(PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE)) {
                    Log.d(this, "startOutgoingCall: defaulting to voip mode for call %s",
                            call.getId());
                    call.setIsVoipAudioMode(true);
                }
            }

            call.setState(
                    CallState.CONNECTING,
                    phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString());

            boolean isVoicemail = (call.getHandle() != null)
                    && (PhoneAccount.SCHEME_VOICEMAIL.equals(call.getHandle().getScheme())
                    || (accountToUse != null && mPhoneAccountRegistrar.isVoiceMailNumber(
                    accountToUse.getAccountHandle(), call.getHandle().getSchemeSpecificPart())));

            if (!isVoicemail && (isRttSettingOn() || (extras != null
                    && extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)))) {
                if (accountToUse != null
                        && accountToUse.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
                    call.createRttStreams();
                }
                call.setRequestedToStartWithRtt();
            }
        }
        setIntentExtrasAndStartTime(call, extras);
        // 如果是MMI 號碼
        if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) {
            //讓CallsManager監聽call的行爲
            call.addListener(this);
        } else if (!mCalls.contains(call) && mPendingMOEmerCall == null) {
 確保Call不會重複添加(getNewOutgoingCall有重用機制).添加call, 然後callsManager通知監聽者,添加了一個call
            addCall(call);
        }

        return call;
    }

 

addCall添加call,然後callsManager通知監聽者,添加一個call

 public void addCall(Call call) {
        Trace.beginSection("addCall");
        Log.v(this, "addCall(%s)", call);
        call.addListener(this);// CallsManager 監聽 call
        mCalls.add(call); // 添加

        // Specifies the time telecom finished routing the call. This is used by the dialer for
        // analytics.
        Bundle extras = call.getIntentExtras();
        extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS,
                SystemClock.elapsedRealtime());

        updateCanAddCall();
        // onCallAdded for calls which immediately take the foreground (like the first call).onCallAdded用於立即佔據前臺的調用(如第一次調用)
        for (CallsManagerListener listener : mListeners) {// 告訴所有callsManager的監聽者
            if (LogUtils.SYSTRACE_DEBUG) {
                Trace.beginSection(listener.getClass().toString() + " addCall");
            }
            listener.onCallAdded(call); //進入監聽InCallControoler中
            if (LogUtils.SYSTRACE_DEBUG) {
                Trace.endSection();
            }
        }
        Trace.endSection();
    }

接下來需要查看mListeners添加了那些監聽,到底是通知了誰,需要查看callsManager中構造構造過程

CallManager的構造函數

 /**
     * Initializes the required Telecom components.
     */
    @VisibleForTesting
    public CallsManager(
            Context context,
            TelecomSystem.SyncRoot lock,
            .....
            ......) {
        mContext = context;
        mLock = lock;
          .....
          ........
          ............

        mDtmfLocalTonePlayer =
                new DtmfLocalTonePlayer(new DtmfLocalTonePlayer.ToneGeneratorProxy());
        CallAudioRouteStateMachine callAudioRouteStateMachine = new CallAudioRouteStateMachine(
                context,
                this,
                bluetoothManager,
                wiredHeadsetManager,
                statusBarNotifier,
                audioServiceFactory,
                CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT
        );

        mConnectionSvrFocusMgr = connectionServiceFocusManagerFactory.create(
                mRequester, Looper.getMainLooper());
        mHeadsetMediaButton = headsetMediaButtonFactory.create(context, this, mLock);
        mTtyManager = new TtyManager(context, mWiredHeadsetManager);
        mProximitySensorManager = proximitySensorManagerFactory.create(context, this);
        mPhoneStateBroadcaster = new PhoneStateBroadcaster(this);
        mCallLogManager = new CallLogManager(context, phoneAccountRegistrar, mMissedCallNotifier);
        mConnectionServiceRepository =
                new ConnectionServiceRepository(mPhoneAccountRegistrar, mContext, mLock, this);
        mInCallWakeLockController = inCallWakeLockControllerFactory.create(context, this);
        mClockProxy = clockProxy;
        // 在CallsManager構造的時候, 會給CallsManager添加一系列監聽者, 當CallsManager變化時,會通知他們
        mListeners.add(mInCallWakeLockController);
        mListeners.add(statusBarNotifier);
        mListeners.add(mCallLogManager);
        mListeners.add(mPhoneStateBroadcaster);
        //因爲目前關注的是InCallController,因爲InCallController監聽CallsManager,收到來自CallsManager的消息後,
        // 跨進程回到最初的com.android.dialer進程,通知UI發生變化,也就是說InCallController是system進程 主動溝通com.android.dialer進程的橋樑。
        mListeners.add(mInCallController);  // 重點關注 InCallController  liwangjiang
        mListeners.add(mCallAudioManager);
        mListeners.add(mCallRecordingTonePlayer);
        mListeners.add(missedCallNotifier);
        mListeners.add(mHeadsetMediaButton);
        mListeners.add(mProximitySensorManager);

        // There is no USER_SWITCHED broadcast for user 0, handle it here explicitly.
        final UserManager userManager = UserManager.get(mContext);
        // Don't load missed call if it is run in split user model.
        if (userManager.isPrimaryUser()) {
            onUserSwitch(Process.myUserHandle());
        }
        // Register BroadcastReceiver to handle enhanced call blocking feature related event.
        IntentFilter intentFilter = new IntentFilter(
                CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
        intentFilter.addAction(SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED);
        context.registerReceiver(mReceiver, intentFilter);
        QtiCarrierConfigHelper.getInstance().setup(mContext);
    }

目前需要關注的是InCallController,因爲InCallControll監聽CallsManager,收到來自CallsManager的消息後,跨進程回到最初的com.android.dialer進程,通知UI發生變化,也就是說InCallContrller是system進程主動溝通com.android.dialer進程的橋樑,下面詳細解釋InCallContoller是如何溝通com.android.dialer進程的。

InCallController.java

我們首先看一下構造函數
InCallController幾個內部內的意思:

CarSwappingInCallServiceConnection :  當這兩種情況之一發生時,該類實例將接管連接。個版本,他在兩個獨立子類實例之間切換UI

EmergencyInCallServiceConnection  :  InCallServiceBindingConnection的一個版本,它將所有調用委託給一個輔助連接(CarSwappingInCallServiceConnection),直到它發現一個緊急調用,或者另一個連接終止。當這兩種情況之一發生時,該類實例將接管連接。

InCallServiceBindingConnection  最後調用的類 他繼承了  InCallServiceConnection
 

 //構造器主要初始化是在TelecomSystem中進行初始化
    public InCallController(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager,
            SystemStateProvider systemStateProvider,
            DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter,
            EmergencyCallHelper emergencyCallHelper) {
        mContext = context;
        mLock = lock;
        mCallsManager = callsManager;
        mSystemStateProvider = systemStateProvider;
        mTimeoutsAdapter = timeoutsAdapter;
        mDefaultDialerCache = defaultDialerCache;
        mEmergencyCallHelper = emergencyCallHelper;

        Resources resources = mContext.getResources();
        mSystemInCallComponentName = new ComponentName( // 這個服務組件時用來控制電話APP的UI的 pag包名  cls類名
                resources.getString(R.string.ui_default_package),//pkg  com.android.dialer
                resources.getString(R.string.incall_default_class));//cls com.android.incallui.InCallServiceImpl

        mSystemStateProvider.addListener(mSystemStateListener);
    }

接下來就是監聽的位置了 listener.onCallAdded(call); //進入監聽InCallControoler中  InCallControoler是繼承CallsManagerListenerBase對象  listener就是CallsManagerListenerBase接口

onCallAdded(call) 方法
 

if (!isBoundAndConnectedToServices()) {
            Log.i(this, "onCallAdded: %s; not bound or connected.", call);
            // 第一次觸發時,服務沒有綁定,所以需要先綁定服務
            bindToServices(call);
        } else {
           .....
           .......    
        }
    }

bindToServices中主要看if中的 mInCallServiceConnection.connect(call)進入

  public void bindToServices(Call call) {
        if (mInCallServiceConnection == null) {
            InCallServiceConnection dialerInCall = null;
            InCallServiceInfo defaultDialerComponentInfo = getDefaultDialerComponent();
            Log.i(this, "defaultDialer: " + defaultDialerComponentInfo);
            if (defaultDialerComponentInfo != null &&
                    !defaultDialerComponentInfo.getComponentName().equals(
                            mSystemInCallComponentName)) {
                dialerInCall = new InCallServiceBindingConnection(defaultDialerComponentInfo);
            }
            Log.i(this, "defaultDialer: " + dialerInCall);

            InCallServiceInfo systemInCallInfo = getInCallServiceComponent(
                    mSystemInCallComponentName, IN_CALL_SERVICE_TYPE_SYSTEM_UI);
            EmergencyInCallServiceConnection systemInCall =
                    new EmergencyInCallServiceConnection(systemInCallInfo, dialerInCall);
            systemInCall.setHasEmergency(mCallsManager.hasEmergencyCall());

            InCallServiceConnection carModeInCall = null;
            InCallServiceInfo carModeComponentInfo = getCarModeComponent();
            if (carModeComponentInfo != null &&
                    !carModeComponentInfo.getComponentName().equals(mSystemInCallComponentName)) {
                carModeInCall = new InCallServiceBindingConnection(carModeComponentInfo);//初始化連接
            }

            mInCallServiceConnection =
                    new CarSwappingInCallServiceConnection(systemInCall, carModeInCall);
        }

        mInCallServiceConnection.setCarMode(shouldUseCarModeUI());

        // 這個對象,他在兩個獨立的實例中切換UI
        if (mInCallServiceConnection.connect(call) ==//mInCallServiceConnection.connect(call)開啓調用InCallUi
                InCallServiceConnection.CONNECTION_SUCCEEDED) {
            //只有當我們實際連接到主UI InCallServices時,才連接到非UI InCallServices。
            connectToNonUiInCallServices(call);
        } else {
            Log.i(this, "bindToServices: current UI doesn't support call; not binding.");
        }
    }

 

mInCallServiceConnection.connect(call)進入了CartSwapingInCallServiceConnection(內部類)對象中

private class CarSwappingInCallServiceConnection extends InCallServiceConnection {
      @Override
        public int connect(Call call) {
            if (mIsConnected) {
                Log.i(this, "already connected");
                return CONNECTION_SUCCEEDED;
            } else {
                int result = mCurrentConnection.connect(call);  //InCallUi調用  調到EmergencyInCallServiceConnection內部類中
                if (result != CONNECTION_FAILED) {
                    mIsConnected = result == CONNECTION_SUCCEEDED;
                    return result;
                }
            }

            return CONNECTION_FAILED;
        }

}

mCurrentConnection.connect(call);  //InCallUi調用  調到EmergencyInCallServiceConnection內部類中connect函數

/**
*InCallServiceBindingConnection的一個版本,它將所有調用委託給一個輔助連接,
*直到它發現一個緊急調用,或者另一個連接終止。當這兩種情況之一發生時,該類實例將接管連接。
*/
private class EmergencyInCallServiceConnection extends InCallServiceBindingConnection {
        @Override
        public int connect(Call call) {
            mIsConnected = true;
            if (mIsProxying) {
                int result = mSubConnection.connect(call);//InCallUi調用
                mIsConnected = result == CONNECTION_SUCCEEDED;
                if (result != CONNECTION_FAILED) {
                    return result;
                }
                // Could not connect to child, stop proxying.
                mIsProxying = false;
            }

            mEmergencyCallHelper.maybeGrantTemporaryLocationPermission(call,
                mCallsManager.getCurrentUserHandle());

            if (call != null && call.isIncoming()
                && mEmergencyCallHelper.getLastEmergencyCallTimeMillis() > 0) {
              // Add the last emergency call time to the call
              Bundle extras = new Bundle();
              extras.putLong(android.telecom.Call.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS,
                      mEmergencyCallHelper.getLastEmergencyCallTimeMillis());
              call.putExtras(Call.SOURCE_CONNECTION_SERVICE, extras);
            }

            //調用這裏,連接父接口中的connect()方法。
            return super.connect(call);
        }
}

EmergencyInCallServiceConnection繼承了InCallServiceBindingConnection 所以調用super.connect就是調用了InCallServiceBindingConnection裏面的 connect()方法

InCallServiceBindingConnection中的connect方法

private class InCallServiceBindingConnection extends InCallServiceConnection {
@Override
        public int connect(Call call) {
            if (mIsConnected) {
                Log.addEvent(call, LogUtils.Events.INFO, "Already connected, ignoring request.");
                return CONNECTION_SUCCEEDED;
            }

            if (call != null && call.isSelfManaged() &&
                    !mInCallServiceInfo.isSelfManagedCallsSupported()) {
                Log.i(this, "Skipping binding to %s - doesn't support self-mgd calls",
                        mInCallServiceInfo);
                mIsConnected = false;
                return CONNECTION_NOT_SUPPORTED;
            }
            //開啓InCallService
            Intent intent = new Intent(InCallService.SERVICE_INTERFACE);
            intent.setComponent(mInCallServiceInfo.getComponentName());
            if (call != null && !call.isIncoming() && !call.isExternalCall()){
                intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS,
                        call.getIntentExtras());
                intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
                        call.getTargetPhoneAccount());
            }

            Log.i(this, "Attempting to bind to InCall %s, with %s", mInCallServiceInfo, intent);
            mIsConnected = true;
            if (!mContext.bindServiceAsUser(intent, mServiceConnection,   //開啓服務  調用InCallServiceImpl
                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE |
                        Context.BIND_ABOVE_CLIENT,
                        UserHandle.CURRENT)) {
                Log.w(this, "Failed to connect.");
                mIsConnected = false;
            }

            if (call != null && mIsConnected) {
                call.getAnalytics().addInCallService(
                        mInCallServiceInfo.getComponentName().flattenToShortString(),
                        mInCallServiceInfo.getType());
            }

            return mIsConnected ? CONNECTION_SUCCEEDED : CONNECTION_FAILED;
        }
}

這裏已經開啓服務,開啓了Dialer中InCallUi,在InCallUi中有一個服務InCallServiceImpl,這個服務就是開啓dialer裏面的InCallUi的入口
調用完畢後回調到InCallController中的InCallServiceBindingConnection中的ServiceConnection裏面的onServiceConnected()方法  ,

  @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.startSession("ICSBC.oSC");
                synchronized (mLock) {
                    try {
                        Log.d(this, "onServiceConnected: %s %b %b", name, mIsBound, mIsConnected);
                        mIsBound = true;
                        if (mIsConnected) {
                            //  成功後調用InCallServer ,連接成功往下走
                            onConnected(service);
                        }
                    } finally {
                        Log.endSession();
                    }
                }
            }

綁定與InCallUi數據傳輸方法

  protected void onConnected(IBinder service) {
            boolean shouldRemainConnected =
                    InCallController.this.onConnected(mInCallServiceInfo, service);//往下執行
            if (!shouldRemainConnected) {
                // Sometimes we can opt to disconnect for certain reasons, like if the
                // InCallService rejected our initialization step, or the calls went away
                // in the time it took us to bind to the InCallService. In such cases, we go
                // ahead and disconnect ourselves.
                disconnect();
            }
        }


private boolean onConnected(InCallServiceInfo info, IBinder service) {
        Trace.beginSection("onConnected: " + info.getComponentName());
        Log.i(this, "onConnected to %s", info.getComponentName());

        IInCallService inCallService = IInCallService.Stub.asInterface(service);
        mInCallServices.put(info, inCallService);

        try {
            // 讓綁定的服務,可以通過 InCallAdapter 跨進程 訪問 system 進程 (因爲當前就是在系統進程)
            // InCallAdapter 是一個 Binder。
            // 需要對Android 通過 Binder實現跨進程調用有一定了解
            // 還有對Android 的AIDL由一定了解
            inCallService.setInCallAdapter(
                    new InCallAdapter(
                            mCallsManager,
                            mCallIdMapper,
                            mLock,
                            info.getComponentName().getPackageName()));
        } catch (RemoteException e) {
            Log.e(this, e, "Failed to set the in-call adapter.");
            Trace.endSection();
            return false;
        }

        // Upon successful connection, send the state of the world to the service.
        List<Call> calls = orderCallsWithChildrenFirst(mCallsManager.getCalls());
        Log.i(this, "Adding %s calls to InCallService after onConnected: %s, including external " +
                "calls", calls.size(), info.getComponentName());
        int numCallsSent = 0;
        for (Call call : calls) {
            try {
                if ((call.isSelfManaged() && !info.isSelfManagedCallsSupported()) ||
                        (call.isExternalCall() && !info.isExternalCallsSupported())) {
                    continue;
                }

                // Only send the RTT call if it's a UI in-call service
                boolean includeRttCall = info.equals(mInCallServiceConnection.getInfo());

                // Track the call if we don't already know about it.
                addCall(call);
                numCallsSent += 1;
                inCallService.addCall(ParcelableCallUtils.toParcelableCall(
                        call,
                        true /* includeVideoProvider */,
                        mCallsManager.getPhoneAccountRegistrar(),
                        info.isExternalCallsSupported(),
                        includeRttCall));
            } catch (RemoteException ignored) {
            }
        }
        try {
            inCallService.onCallAudioStateChanged(mCallsManager.getAudioState());
            inCallService.onCanAddCallChanged(mCallsManager.canAddCall());
        } catch (RemoteException ignored) {
        }
        Log.i(this, "%s calls sent to InCallService.", numCallsSent);
        Trace.endSection();
        return true;
    }

InCallController 內部類的邏輯   第一先是調用 CarSwappingInCallServiceConnection該內部類的作用是管理連接其他兩個子類(EmergencyInCallServiceConnection   ,InCallServiceBindingConnection)之間互相切換,

交互圖:

 

 

com.android.dialer進程

InCallServiceImpl InCallService的實現類,負責和Telecom溝通,更新

InCallServiceImp的核心是在的他父類InCallService,InCallServiceImpl綁定時返回一個InCallServiceBinder,因此system進程拿到的接口就到的接口就是這個,綁定之前會初始化InCallPresenter,InCallPresenter是全局唯一(單例)的,他負責更新APP的UI

InCallServiceImp.java

@Override
  public IBinder onBind(Intent intent) {
    final Context context = getApplicationContext();
	
    getResources().updateConfiguration(configuration,displayMetrics);


    QtiCarrierConfigHelper.getInstance().setup(context);
    final ContactInfoCache contactInfoCache = ContactInfoCache.getInstance(context);

    InCallPresenter.getInstance()
        .setUp(
            context,
            CallList.getInstance(),
            new ExternalCallList(),
            new StatusBarNotifier(context, contactInfoCache),
            new ExternalCallNotifier(context, contactInfoCache),
            contactInfoCache,
            new ProximitySensor(
                context, AudioModeProvider.getInstance(), new AccelerometerListener(context)),
            new FilteredNumberAsyncQueryHandler(context));
    InCallPresenter.getInstance().onServiceBind();
    // 核心, 主過程
    InCallPresenter.getInstance().maybeStartRevealAnimation(intent);
    TelecomAdapter.getInstance().setInCallService(this);


    InCallWindow.getInstance(context).initInCallWindow();
    ImmersionInCallDialogMgr.getInstance().init(context, contactInfoCache);

    // 返回一個 InCallServiceBinder
    return super.onBind(intent);
  }

InCallPresenter MVC模式中,控制邏輯和UI

InCallPreseter的maybeStartRevealAnimatin會啓動InCallActivity,即撥號進入中的界面(或者是來電界面)

public void maybeStartRevealAnimation(Intent intent) {
    if (intent == null || mInCallActivity != null) {
      return;
    }
    final Bundle extras = intent.getBundleExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
    if (extras == null) {
      // Incoming call, just show the in-call UI directly.
      return;
    }

    if (extras.containsKey(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS)) {
      // Account selection dialog will show up so don't show the animation.
      return;
    }

    final PhoneAccountHandle accountHandle =
        intent.getParcelableExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
    final Point touchPoint = extras.getParcelable(TouchPointManager.TOUCH_POINT);

    InCallPresenter.getInstance().setBoundAndWaitingForOutgoingCall(true, accountHandle);

    final Intent activityIntent =
        InCallActivity.getIntent(mContext, false, true, false /* forFullScreen */);
    activityIntent.putExtra(TouchPointManager.TOUCH_POINT, touchPoint);
    mContext.startActivity(activityIntent);//啓動InCallActivity
  }

Dialer啓動圖:

 

調起InCallUi後就是更新InCallUi數據了。

回到剛剛開啓服務的位置 mContext.bindServiceAsUser(intent, mServiceConnection,.....) 這裏傳入了mServiceConnection,InCallServiceImpl連接成功後回調到mServiceConnection, 

InCallController.java

private final ServiceConnection mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.startSession("ICSBC.oSC");
                synchronized (mLock) {
                    try {
                        Log.d(this, "onServiceConnected: %s %b %b", name, mIsBound, mIsConnected);
                        mIsBound = true;
                        if (mIsConnected) {//是否連接
                            // 只有當我們被認爲是有聯繫的時候才繼續。 成功後調用InCallServer
                            onConnected(service);
                        }
                    } finally {
                        Log.endSession();
                    }
                }
            }
}
protected void onConnected(IBinder service) {
            boolean shouldRemainConnected =
                    InCallController.this.onConnected(mInCallServiceInfo, service);//往下執行
            if (!shouldRemainConnected) {
                disconnect();
            }
        }

onConnected這裏面設置了很多監聽,通過IInCallService調用到了framework/base/telecom  InCallService中進行處理.

  private boolean onConnected(InCallServiceInfo info, IBinder service) {
        Trace.beginSection("onConnected: " + info.getComponentName());
        Log.i(this, "onConnected to %s", info.getComponentName());
        //進入Framework/base/Telecom
        IInCallService inCallService = IInCallService.Stub.asInterface(service);
        mInCallServices.put(info, inCallService);

        try {
            // 讓綁定的服務,可以通過 InCallAdapter 跨進程 訪問 system 進程 (因爲當前就是在系統進程)
            // InCallAdapter 是一個 Binder。
            // 需要對Android 通過 Binder實現跨進程調用有一定了解
            // 還有對Android 的AIDL由一定了解  調用到了Framework/base/telecom  InCallServce
            inCallService.setInCallAdapter(
                    new InCallAdapter(
                            mCallsManager,
                            mCallIdMapper,
                            mLock,
                            info.getComponentName().getPackageName()));
        } catch (RemoteException e) {
            Log.e(this, e, "Failed to set the in-call adapter.");
            Trace.endSection();
            return false;
        }

        // Upon successful connection, send the state of the world to the service.成功連接後,將 world 狀態發送到服務。
        List<Call> calls = orderCallsWithChildrenFirst(mCallsManager.getCalls());
        Log.i(this, "Adding %s calls to InCallService after onConnected: %s, including external " +
                "calls", calls.size(), info.getComponentName());
        int numCallsSent = 0;
        for (Call call : calls) {
            try {
                if ((call.isSelfManaged() && !info.isSelfManagedCallsSupported()) ||
                        (call.isExternalCall() && !info.isExternalCallsSupported())) {
                    continue;
                }

                // Only send the RTT call if it's a UI in-call service
                boolean includeRttCall = info.equals(mInCallServiceConnection.getInfo());

                // Track the call if we don't already know about it.
                addCall(call);
                numCallsSent += 1;
                //添加調用
                inCallService.addCall(ParcelableCallUtils.toParcelableCall(
                        call,
                        true /* includeVideoProvider */,
                        mCallsManager.getPhoneAccountRegistrar(),
                        info.isExternalCallsSupported(),
                        includeRttCall));
            } catch (RemoteException ignored) {
            }
        }
        try {
            inCallService.onCallAudioStateChanged(mCallsManager.getAudioState());
            inCallService.onCanAddCallChanged(mCallsManager.canAddCall());
        } catch (RemoteException ignored) {
        }
        Log.i(this, "%s calls sent to InCallService.", numCallsSent);
        Trace.endSection();
        return true;
    }

我們先看inCallService.setInCallAdapter方法   他進入的是framework/base/telecom InCallService.java , 

// 讓綁定的服務,可以通過 InCallAdapter 跨進程 訪問 system 進程 (因爲當前就是在系統進程)
// InCallAdapter 是一個 Binder。
// 需要對Android 通過 Binder實現跨進程調用有一定了解
// 還有對Android 的AIDL由一定了解  調用到了Framework/base/telecom  InCallServce
 private final class InCallServiceBinder extends IInCallService.Stub {
        @Override
        public void setInCallAdapter(IInCallAdapter inCallAdapter) {
            mHandler.obtainMessage(MSG_SET_IN_CALL_ADAPTER, inCallAdapter).sendToTarget();
        }

        @Override
        public void addCall(ParcelableCall call) {
            mHandler.obtainMessage(MSG_ADD_CALL, call).sendToTarget();
        }

        @Override
        public void updateCall(ParcelableCall call) {
            mHandler.obtainMessage(MSG_UPDATE_CALL, call).sendToTarget();
        }
        忽略......
 }

進入Handler

  /** Default Handler used to consolidate binder method calls onto a single thread. */
    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            if (mPhone == null && msg.what != MSG_SET_IN_CALL_ADAPTER) {
                return;
            }

            switch (msg.what) {
                case MSG_SET_IN_CALL_ADAPTER:
                    String callingPackage = getApplicationContext().getOpPackageName();
                    // setInCallAdapter 觸發創建 com.android.dialer 的唯一 的phone對象
                    // 也就是說, 在 com.android.dialer 進程的所有操作 都是經過 phone 裏面的mInCallAdapter
                    // 發送到 system 進程的 (框架層)
                    mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj), callingPackage,
                            getApplicationContext().getApplicationInfo().targetSdkVersion);
                    // 核心, 讓InCallService 監聽 Phone 的狀態變化
                    mPhone.addListener(mPhoneListener);
                    onPhoneCreated(mPhone);
                    break;
                case MSG_ADD_CALL:  //添加Call時調用
                    mPhone.internalAddCall((ParcelableCall) msg.obj);
                    break;
                case MSG_UPDATE_CALL:
                    mPhone.internalUpdateCall((ParcelableCall) msg.obj);
                    break;
                case MSG_SET_POST_DIAL_WAIT: {
                    SomeArgs args = (SomeArgs) msg.obj;
                ........
                忽略大量消息類型

這裏mPhone.addListener(mPhoneLintener)  他添加到Phone中等待響應

然後是調用 inCallService.addCall()方法

進入了Handler 中的  case MSG_ADD_CALL:  

調用mPhone.InternalAddCall();進入了mPhone對象

Phone.java   

 final void internalAddCall(ParcelableCall parcelableCall) {
        Call call = new Call(this, parcelableCall.getId(), mInCallAdapter,
                parcelableCall.getState(), mCallingPackage, mTargetSdkVersion);
        mCallByTelecomCallId.put(parcelableCall.getId(), call);
        mCalls.add(call);
        checkCallTree(parcelableCall);
        call.internalUpdate(parcelableCall, mCallByTelecomCallId);
        fireCallAdded(call);//進入回調
     }

這裏只是處理一些Call的處理

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

mListeners是mPhone.addListener(mPhoneLintener)添加進來的,調用onCallAdded() 方法,回調到 InCallService裏面的private Phone.Listener mPhoneListener = new Phone.Listener(){ }中

  private Phone.Listener mPhoneListener = new Phone.Listener() {
   省略其他回調.....
      /** ${inheritDoc} */
        @Override
        public void onCallAdded(Phone phone, Call call) {
            InCallService.this.onCallAdded(call); //onCallAded本類裏面沒有操作,操作子類實現
        }
     省略其他回調.....     
  }

這裏的InCallServie.this.onCallAdded()方法提供給子類實現  狀態更新  傳入Call對象

我們在來看一下他的子類  com.android.Dialer進程

InCallServiceImpl.java

  @Override
  public void onCallAdded(Call call) {
    InCallPresenter.getInstance().onCallAdded(call);
  }

這裏就是更新狀態入口

InCallPresenter的作用:從CallList獲取更新,並將更改通知InCallActivity (UI)。負責啓動新調用的活動,並在所有調用都斷開時完成該活動。創建和管理調用內狀態,併爲希望在調用內狀態更改時偵聽的演示程序提供偵聽器模式。

InCallPresenter.java

public void onCallAdded(final android.telecom.Call call) {
    LatencyReport latencyReport = new LatencyReport(call);

    if (shouldAttemptBlocking(call)) {
      maybeBlockCall(call, latencyReport);
    } else {
      if (call.getDetails().hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL)) {
        mExternalCallList.onCallAdded(call);
      } else {
        latencyReport.onCallBlockingDone();
        mCallList.onCallAdded(mContext, call, latencyReport);  //核心主過程
      }
    }

    // Since a call has been added we are no longer waiting for Telecom to send us a call. 
   由於增加了一個呼叫,我們不再等待電信給我們發送一個呼叫
    setBoundAndWaitingForOutgoingCall(false, null);
    call.registerCallback(mCallCallback);
  }

進入CallList進行call對象的數據解析和加載,
CallList.java

  public void onCallAdded(
      final Context context, final android.telecom.Call telecomCall, LatencyReport latencyReport) {
    省略......
    .........
    if (call.getState() == DialerCall.State.INCOMING
        || call.getState() == DialerCall.State.CALL_WAITING) {
      onIncoming(call);  //主核心
    } else {
      dialerCallListener.onDialerCallUpdate();
    }

    if (call.getState() != State.INCOMING) {
      // Only report outgoing calls
      ShortcutUsageReporter.onOutgoingCallAdded(context, call.getNumber());
    }

    Trace.endSection();
  }
  private void onIncoming(DialerCall call) {

    for (Listener listener : mListeners) {
      listener.onIncomingCall(call);//回調進入InCallPresenter
    }
  }

這裏的mListeners是InCallPresenter傳入this,InCallPresenter實現了Listener 

InCallPresenter.java

 /** Called when there is a new incoming call. */
  @Override
  public void onIncomingCall(DialerCall call) {

    InCallState newState = startOrFinishUi(InCallState.INCOMING);
    InCallState oldState = mInCallState;
    registerFoldAnswer();
    mInCallState = newState;
    for (IncomingCallListener listener : mIncomingCallListeners) {
      listener.onIncomingCall(oldState, mInCallState, call);
    }
    if (mInCallActivity != null) {
      // Re-evaluate which fragment is being shown.
      mInCallActivity.onPrimaryCallStateChanged();//更新Activity 狀態
    }
  }

流程圖:

 

接下來我們分析一下Log是怎麼調用InCallUi的

//Dialer撥號  Dialer進程
09-17 21:29:31.099 I/Dialer  (28149): CallLogFragment - callBack()
09-17 21:29:31.103 I/Dialer  (28149): PerformanceReport.stopRecording - enter

//phone進程  也就是package/service/telecom
UserCallIntentProcessor.java中準備跳轉意圖,跳轉進入PrimaryCallReceiver
09-17 21:29:31.123 I/Telecom ( 1141): UserCallIntentProcessor: sendIntentToDestination: send intent to Telecom directly.: TSI.pC@A4U

通過PrimaryCallReceiver.java這個廣播進入了CallIntentProcessor.java  processIntent方法中,isUnknownCall: false: 是不是陌生電話
09-17 21:29:31.124 I/Telecom ( 1141): CallIntentProcessor: onReceive - isUnknownCall: false: TSI.pC@A4U

進入InCallController.java中的onCallAdded方法通過CallsManager進行調用 listener.onCallAdded(call);
09-17 21:29:31.149 I/Telecom ( 1141): InCallController: onCallAdded: [TC@3, CONNECTING, null, tel:***********, A, childs(0), has_parent(false), [Capabilities:], [Properties:]]; not bound or connected.: TSI.pC@A4U

進入InCallController.java bindToServices 方法
09-17 21:29:31.150 I/Telecom ( 1141): InCallController: defaultDialer: null: TSI.pC@A4U
09-17 21:29:31.151 I/Telecom ( 1141): InCallController: defaultDialer: null: TSI.pC@A4U

EmergencyInCallServiceConnection是InCallController 的內部類,這裏開始連接InCallService,最終連接的是InCallServiceImpl
09-17 21:29:31.152 I/Telecom ( 1141): EmergencyInCallServiceConnection: Attempting to bind to InCall [ComponentInfo{org.codeaurora.dialer/com.android.incallui.InCallServiceImpl} supportsExternal? true supportsSelfMg?false], with Intent { act=android.telecom.InCallService cmp=org.codeaurora.dialer/com.android.incallui.InCallServiceImpl (has extras) }: TSI.pC@A4U


//Dialer進程
進入Dialer 裏面的InCallServiceImpl.java 裏面的onBind() 方法  oldLock 是否是老的InCallPresenter   ,這裏的log是開發自己增加的 ...
09-17 21:29:31.178 I/Dialer  (28149): InCallServiceImpl.onBind - oldLock = 0


//phone進程
與InCallServiceImpl連接成功   info,返回的InCallSerivceInfo
09-17 21:29:31.281 I/Telecom ( 1141): InCallController: onConnected to Component Info{org.codeaurora.dialer/com.android.incallui.InCallServiceImpl}: ICSBC.oSC@A4Y

======================
這一步已經調起了InCallUi  只需要等待數據更新
======================
連接後會進入framework/base中InCallService中的內部的InCallServiceBinder內部類中進行處理,InCallServiceBinder他裏面的方法就是給Handler發送信息
Handler調用的方法傳輸數據給子類進行處理。

//Dialer進程  InCallSerivce只有 Dialer進程中的InCallServiceImpl實現過  InCallServiceImpl實現了InCallService中的一些方法比如onCallAdded()、
加載頁面
09-17 21:29:31.614 I/Dialer  (28149): InCallFragment.setCallState - PrimaryCallState, state: 13, connectionLabel: 
09-17 21:29:31.615 I/Dialer  (28149): ContactGridManager - setCallState: 13
09-17 21:29:31.616 I/Dialer  (28149): InCallFragment.setContactGridViewMarginTop - screenMode: 0  screenOrientation: 1
InCallActivity數據更新
09-17 21:29:31.616 I/Dialer  (28149): InCallActivity.onPrimaryCallStateChanged
09-17 21:29:31.616 I/Dialer  (28149): InCallActivity.showMainInCallFragment - shouldShowAnswerUi: false, shouldShowVideoUi: false, didShowAnswerScreen: false, didShowInCallScreen: true, didShowVideoCallScreen: false
09-17 21:29:31.616 I/Dialer  (28149): InCallFragment.onInCallShowDialpad - isShown: false

總結一下: system 進程的 InCallController 收到來自於 CallsManager 的消息後, 發送到 com.android.dialer 進程, 一步步地更新 撥號界面的狀態, 整個過程已經全部打通了,只需要調用CallsManager中的addCall方法就會更新狀態。

 

結語:我語文沒及格過,湊活着看吧。代碼挺長,如果有錯誤請提出做 更正,如有內容在加,謝謝觀看 ,如果滿意點個贊吧 。

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