Android7.0 Phone應用源碼分析(一) phone撥號流程分析

1.1 dialer撥號

 

撥號盤點擊撥號DialpadFragment的onClick方法會被調用html

public void onClick(View view) {
        int resId = view.getId();
        if (resId == R.id.dialpad_floating_action_button) {
            view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
            handleDialButtonPressed();         } else if (resId == R.id.deleteButton) {
            keyPressed(KeyEvent.KEYCODE_DEL);
        } else if (resId == R.id.digits) {
            if (!isDigitsEmpty()) {
                mDigits.setCursorVisible(true);
            }
        } else if (resId == R.id.dialpad_overflow) {
            mOverflowPopupMenu.show();
        } else {
            Log.wtf(TAG, "Unexpected onClick() event from: " + view);
            return;
        }
}

handleDialButtonPressed方法處理具體的撥號事件android

private void handleDialButtonPressed() {
         ............ ............
         DialerUtils.startActivityWithErrorToast(getActivity(), intent);
         hideAndClearDialpad(false);
}

跟蹤DialerUtils的startActivityWithErrorToast方法,內部判斷了一些是否有撥號權限的判斷後,最後調用TelecomManagerCompat的placeCall事件git

public static void placeCall(@Nullable Activity activity,
            @Nullable TelecomManager telecomManager, @Nullable Intent intent) {
        if (activity == null || telecomManager == null || intent == null) {
            return;
        }
        if (CompatUtils.isMarshmallowCompatible()) {
            telecomManager.placeCall(intent.getData(), intent.getExtras());
            return;
        }
        activity.startActivityForResult(intent, 0);
    }

這裏根據當前系統版本若是是大於等於6.0,則調用TelecomManager的placeCall,不然直接調用startActivity呼出該intent併發

看看TelecomManager的placeCall方法app

android.telecom. TelecomManager
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);
            }
        }
}

經過aidl接口調用ITelecomService的placeCall方法框架

1.2 telecomService處理撥號事件

TelecomServiceImpl裏的mBinderImpl變量是ITelecomService的具體實現類socket

com.android.server.telecom. TelecomServiceImpl
private final ITelecomService.Stub mBinderImpl = new ITelecomService.Stub() {

  @Override
       public void placeCall(Uri handle, Bundle extras, String callingPackage) {
        ……………………………… ………………………………
         final UserHandle userHandle = Binder.getCallingUserHandle();
        long token = Binder.clearCallingIdentity();
        final Intent intent = new Intent(Intent.ACTION_CALL, handle);
        if (extras != null) {
             extras.setDefusable(true);
             intent.putExtras(extras);
             mUserCallIntentProcessorFactory.create(mContext, userHandle).processIntent
(intent, callingPackage, hasCallAppOp
&& hasCallPermission);
        }
}

這裏建立了一個UserCallIntentProcessor對象,並調用其processIntent事件處理,前面提到android 6.0如下用startActivity啓動撥號,三方應用撥號都是用這種方式,啓動的也是telecom裏的UserCallActivity類,一樣也是經過UserCallIntentProcessor處理相關事件ide

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

        String action = intent.getAction();

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

進入processOutgoingCallIntent方法函數

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

內部校驗一些是否能撥號權限以及其它操做限制看是否須要直接彈框拒絕,若是都經過了最後會調用sendBroadcastToReceiver方法發送廣播源碼分析

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

該廣播直接指定發給PrimaryCallReceiver處理

com.android.server.telecom.components. PrimaryCallReceiver
   public void onReceive(Context context, Intent intent) {
        Log.startSession("PCR.oR");
        synchronized (getTelecomSystem().getLock()) {
            getTelecomSystem().getCallIntentProcessor().processIntent(intent);
        }
        Log.endSession();
}

接着調用CallIntentProcessor. processIntent(intent)

com.android.server.telecom. CallIntentProcessor
public void processIntent(Intent intent) {
        final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false);
        Log.i(this, "onReceive - isUnknownCall: %s", isUnknownCall);

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

若是是未知號碼如空號由processUnknownCallIntent方法處理

不然調用processOutgoingCallIntent方法

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

        // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
        Call call = callsManager.startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser);
        if (call != null) {
              NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
              context, callsManager, call, intent, new PhoneNumberUtilsAdapterImpl(), isPrivilegedDialer);
            
                final int result = broadcaster.processIntent();
              final boolean success = result == DisconnectCause.NOT_DISCONNECTED;
              if (!success && call != null) {
                 disconnectCallAndShowErrorDialog(context, call, result);
              }
        }
}

方法內部獲取一些撥號參數,好比是否視頻通話,調用者是不是默認撥號盤應用等,而後調用callsManager的startOutgoingCall方法獲得一個call對象,這是一個很重要的方法,來看看它的實現:

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

        // 建立一個call對象
        if (call == null) {
            call = new Call(getNextCallId(), mContext,
                    this,
                    mLock,
                    mConnectionServiceRepository,
                    mContactsAsyncHelper,
                    mCallerInfoAsyncQueryFactory,
                    handle,
                    null /* gatewayInfo */,
                    null /* connectionManagerPhoneAccount */,
                    null /* phoneAccountHandle */,
                    Call.CALL_DIRECTION_OUTGOING /* callDirection */,
                    false /* forceAttachToExistingConnection */,
                    false /* isConference */
            );
            call.setInitiatingUser(initiatingUser);

            call.initAnalytics();

            isReusedCall = false;
        }

         ...... ...... ...... ...... ...... ......
        List<PhoneAccountHandle> accounts = constructPossiblePhoneAccounts(handle, initiatingUser);       // 獲取當前激活的卡列表
         Log.v(this, "startOutgoingCall found accounts = " + accounts);

        if (phoneAccountHandle != null) {
            if (!accounts.contains(phoneAccountHandle)) {
                phoneAccountHandle = null;
            }
        }


       // 獲取當前應該使用哪張卡呼出      
         if (phoneAccountHandle == null && accounts.size() > 0 && !call.isEmergencyCall()) {
            // No preset account, check if default exists that supports the URI scheme for the
            // handle and verify it can be used.
            if(accounts.size() > 1) {
                  // 雙激活卡下取通話主卡帳戶,沒有通話主卡則爲空
                    PhoneAccountHandle defaultPhoneAccountHandle = mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(handle.getScheme(),
                                initiatingUser);
                if (defaultPhoneAccountHandle != null && accounts.contains(defaultPhoneAccountHandle)) {
                    phoneAccountHandle = defaultPhoneAccountHandle;
                }
            } else {
                // Use the only PhoneAccount that is available
                // 單激活卡直接取該卡帳戶)
                   phoneAccountHandle = accounts.get(0);
            }

        }

        call.setTargetPhoneAccount(phoneAccountHandle); // 設置當前通話帳戶
         boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle);

        // 檢查當前是否容許呼出該電話,好比當前已有一通電話在正在呼出,
        // 這時候不容許再呼出一路通話(緊急號碼除外)
        if (!isPotentialInCallMMICode && (!isReusedCall &&
                !makeRoomForOutgoingCall(call, call.isEmergencyCall()))) {
            // just cancel at this point.
            Log.i(this, "No remaining room for outgoing call: %s", call);
            if (mCalls.contains(call)) {
                // This call can already exist if it is a reused call,
                // See {@link #reuseOutgoingCall}.
                call.disconnect();
            }
            return null;
        }

       // 是否須要彈出雙卡選擇框(雙卡下沒有指定帳戶呼出非緊急號碼且當前無通話主卡)
        boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 && !call.isEmergencyCall();

        if (needsAccountSelection) {
          // 設置當前call狀態爲等待帳戶選擇
            // This is the state where the user is expected to select an account
            call.setState(CallState.SELECT_PHONE_ACCOUNT, "needs account selection");
            // Create our own instance to modify (since extras may be Bundle.EMPTY)
            extras = new Bundle(extras);
            extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts);
        } else {
           // 設置當前call狀態爲正在鏈接
            call.setState(CallState.CONNECTING, phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString());
        }

        setIntentExtrasAndStartTime(call, extras);

        // Do not add the call if it is a potential MMI code.
        if((isPotentialMMICode(handle)||isPotentialInCallMMICode)&& !needsAccountSelection){
            call.addListener(this);
         } else if (!mCalls.contains(call)) {
            // We check if mCalls already contains the call because we could potentially be reusing
            // a call which was previously added (See {@link #reuseOutgoingCall}).
             
              addCall(call);
            // 添加當前call到call列表
          }
        return call;
}

看看addCall的具體實現:

private void addCall(Call call) {
        Trace.beginSection("addCall");
        Log.v(this, "addCall(%s)", call);
        call.addListener(this);
        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());

        updateCallsManagerState();
        // onCallAdded for calls which immediately take the foreground (like the first call).
        for (CallsManagerListener listener : mListeners) {
            if (Log.SYSTRACE_DEBUG) {
                Trace.beginSection(listener.getClass().toString() + " addCall");
            }
            listener.onCallAdded(call);
            if (Log.SYSTRACE_DEBUG) {
                Trace.endSection();
            }
        }
        Trace.endSection();
    }

這裏會遍歷call狀態變化的觀察者並逐個回調通知,這裏的觀察者比較多,在callsManager建立的時候註冊監聽的

mListeners.add(mInCallWakeLockController);
        mListeners.add(statusBarNotifier);
        mListeners.add(mCallLogManager);
        mListeners.add(mPhoneStateBroadcaster);
        mListeners.add(mInCallController);
        mListeners.add(mCallAudioManager);
        mListeners.add(missedCallNotifier);
        mListeners.add(mHeadsetMediaButton);
        mListeners.add(mProximitySensorManager);

這裏說一下mInCallController這個對象,是一個InCallController實例,內部封裝了與incallui服務的相關操做,實際上就是一個遠程服務代理類,當callsmanager添加一路call時, 回調InCallController的onCallAdded方法,以下:

public void onCallAdded(Call call) {
        if (!isBoundToServices()) {
            bindToServices(call);
        } else {
            adjustServiceBindingsForEmergency();

            Log.i(this, "onCallAdded: %s", System.identityHashCode(call));
            // Track the call if we don't already know about it.
            addCall(call);
         for (Map.Entry<ComponentName, IInCallService> entry : mInCallServices.entrySet()) {
                ComponentName componentName = entry.getKey();
                IInCallService inCallService = entry.getValue();
                ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(call, true /* includeVideoProvider */, mCallsManager.getPhoneAccountRegistrar());
                 try {
                       inCallService.addCall(parcelableCall);
                 } catch (RemoteException ignored) {
                 }
            }
        }
    }

最後調用inCallService的addCall方法告訴incallui當前添加了一路通話,incallui收到後會拉起界面,具體過程在此就不詳述了,OK回到前面startOutgoingCall的結尾

在成功返回一個call對象以後,新建一個NewOutgoingCallIntentBroadcaster對象,用processIntent方法處理請求

com.android.server.telecom. NewOutgoingCallIntentBroadcaster
public int processIntent() {
 ……………………………… ………………………………
         final boolean isPotentialEmergencyNumber = isPotentialEmergencyNumber(number);
        rewriteCallIntentAction(intent, isPotentialEmergencyNumber);
        action = intent.getAction();
        boolean callImmediately = false;

        if (Intent.ACTION_CALL.equals(action)) {
            if (isPotentialEmergencyNumber) {
                if (!mIsDefaultOrSystemPhoneApp) {
                    launchSystemDialer(intent.getData());
                    return DisconnectCause.OUTGOING_CANCELED;
                } else {
                    callImmediately = true;
                }
            }
        } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) {
            if (!isPotentialEmergencyNumber) {
                return DisconnectCause.OUTGOING_CANCELED;
            }
            callImmediately = true;
        } else {
            return DisconnectCause.INVALID_NUMBER;
        }

       if (callImmediately) {
            String scheme = isUriNumber ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL;
            boolean speakerphoneOn = mIntent.getBooleanExtra(
                    TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
            int videoState = mIntent.getIntExtra(
                    TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                    VideoProfile.STATE_AUDIO_ONLY);
            mCallsManager.placeOutgoingCall(mCall, Uri.fromParts(scheme, number, null), null, speakerphoneOn, videoState);
        }

        UserHandle targetUser = mCall.getInitiatingUser();
     broadcastIntent(intent, number, !callImmediately, targetUser);
        return DisconnectCause.NOT_DISCONNECTED;
}

該方法主要處理三種類型的call:

普通call Intent.ACTION_CALL

系統call Intent.ACTION_CALL_PRIVILEGED

緊急呼叫call Intent.ACTION_CALL_EMERGENCY

普通call任何應用均可以發起,第三方應用撥號都是使用該intent

系統call只有系統應用才能使用

緊急呼叫call 一樣只有系統應用才能使用,而且能夠在無卡狀態下呼出

對於一個Intent.ACTION_CALL_PRIVILEGED的撥號請求,會根據當前號碼是否爲緊急號碼來轉化該intent

private void rewriteCallIntentAction(Intent intent, boolean isPotentialEmergencyNumber) {
        String action = intent.getAction();
        if (Intent.ACTION_CALL_PRIVILEGED.equals(action)) {
            if (isPotentialEmergencyNumber) {

                action = Intent.ACTION_CALL_EMERGENCY;
            } else {
                action = Intent.ACTION_CALL;
            }
            intent.setAction(action);
       }
}

若是是緊急號碼則轉化爲Intent.ACTION_CALL_EMERGENCY

若是不是緊急號碼則轉化爲Intent.ACTION_CALL

因此實際上處理call只有兩種狀況Intent.ACTION_CALL和Intent.ACTION_CALL_EMERGENCY

1.對於Intent.ACTION_CALL的處理:

若是當前是緊急號碼,會校驗調用者是否爲系統默認撥號盤

若是是則置變量callImmediately爲true,後續直接呼出該電話

若是不是則拉起系統默認撥號盤,當前方法調用返回DisconnectCause.OUTGOING_CANCELED

2. 對於Intent.ACTION_CALL_EMERGENCY的處理:

直接設置變量callImmediately爲true,直接呼出該電話

綜上所述緊急撥號會直接調用CallsManager的placeOutgoingCall方法後再進入broadcastIntent方法,看看該方法實現

private void broadcastIntent(Intent originalCallIntent,  String number,
                        boolean receiverRequired, UserHandle targetUser) {
        Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
        if (number != null) {
            broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
        }
        broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        checkAndCopyProviderExtras(originalCallIntent, broadcastIntent);
        mContext.sendOrderedBroadcastAsUser(
                broadcastIntent,
                targetUser,
                android.Manifest.permission.PROCESS_OUTGOING_CALLS,
                AppOpsManager.OP_PROCESS_OUTGOING_CALLS,
                receiverRequired ? new NewOutgoingCallBroadcastIntentReceiver() : null,
                null,  // scheduler
                Activity.RESULT_OK,  // initialCode
                number,  // initialData: initial value for the result data (number to be modified)
                null);  // initialExtras
    }

發送一個Intent.ACTION_NEW_OUTGOING_CALL廣播,對於非緊急撥號,纔會生成一個

NewOutgoingCallBroadcastIntentReceive 實例來接收該廣播

NewOutgoingCallBroadcastIntentReceiver內部作了一些處理後最後仍是調用到CallsManager

的placeOutgoingCall方法,因此該方法是去電的關鍵方法

com.android.server.telecom. CallsManager
public void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo,
            boolean speakerphoneOn, int videoState) {
     ……………………………… ………………………………
        if (call.isEmergencyCall()) {
            // Emergency -- CreateConnectionProcessor will choose accounts automatically
            // 若是是緊急號碼,則取消已指定的通話卡帳戶
            call.setTargetPhoneAccount(null);
            new AsyncEmergencyContactNotifier(mContext).execute();
        }

        Final Boolean requireCallCapableAccountByHandle = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_requireCallCapableAccountForHandle);
        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.
            // 若是是緊急號碼或者已指定通話帳戶,則建立鏈接
            call.startCreateConnection(mPhoneAccountRegistrar);
        } else if (mPhoneAccountRegistrar.getCallCapablePhoneAccounts(
           requireCallCapableAccountByHandle ? call.getHandle().getScheme() : null, false, call.getInitiatingUser()).isEmpty()) {
          // 若是當前沒有激活的卡,則斷開此鏈接
            // If there are no call capable accounts, disconnect the call.
            markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.CANCELED,
                    "No registered PhoneAccounts"));
            markCallAsRemoved(call);
        }
}

該方法內部作了一些設置操做後,確承認以呼出則call的startCreateConnection方法

com.android.server.telecom.call
 void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
        if (mCreateConnectionProcessor != null) {
            return;
        }
        mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this, phoneAccountRegistrar, mContext);
        mCreateConnectionProcessor.process();
}

新建了一個CreateConnectionProcessor對象,處理鏈接請求

com.android.server.telecom. CreateConnectionProcessor
public void process() {
        Log.v(this, "process");
        clearTimeout();
        mAttemptRecords = new ArrayList<>();
        if (mCall.getTargetPhoneAccount() != null) {
            mAttemptRecords.add(new CallAttemptRecord(
                    mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));
        }
        adjustAttemptsForConnectionManager();
        adjustAttemptsForEmergency();
        mAttemptRecordIterator = mAttemptRecords.iterator();
        attemptNextPhoneAccount();
}

進入attemptNextPhoneAccount方法

private void attemptNextPhoneAccount() {
     ……………………………… ………………………………

        if (mCallResponse != null && attempt != null) {
         PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;
         // 獲取ConnectionServiceWrapper對象       
          mService = mRepository.getService(phoneAccount.getComponentName(),
                    phoneAccount.getUserHandle());
            if (mService == null) {
                 attemptNextPhoneAccount();
            } else {
               mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);
                mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
                mCall.setConnectionService(mService);
                setTimeoutIfNeeded(mService, attempt);
             // 已成功獲取ConnectionServiceWrapper對象,建立鏈接         
                 mService.createConnection(mCall, this );
            }
        } else {
            DisconnectCause disconnectCause = mLastErrorDisconnectCause != null ?
                    mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR);
            notifyCallConnectionFailure(disconnectCause);
        }
}

這裏的mService是ConnectionServiceWrapper實例,實際上就是一個包裝了綁定遠程服務的代理類,看看它的構造方法

看看ConnectionServiceWrapper的構造函數

ConnectionServiceWrapper(
            ComponentName componentName,
            ConnectionServiceRepository connectionServiceRepository,
            PhoneAccountRegistrar phoneAccountRegistrar,
            CallsManager callsManager,
            Context context,
            TelecomSystem.SyncRoot lock,
            UserHandle userHandle) {
        super(ConnectionService.SERVICE_INTERFACE, componentName, context, lock, userHandle);
        mConnectionServiceRepository = connectionServiceRepository;
        phoneAccountRegistrar.addListener(new PhoneAccountRegistrar.Listener() {
            // TODO -- Upon changes to PhoneAccountRegistrar, need to re-wire connections
            // To do this, we must proxy remote ConnectionService objects
        });
        mPhoneAccountRegistrar = phoneAccountRegistrar;
        mCallsManager = callsManager;
    }

這裏的ConnectionService.SERVICE_INTERFACE就是"android.telecom.ConnectionService"

也就是它所綁定的遠程服務的action

獲取該對象後調用createConnection方法

com.android.server.telecom. ConnectionServiceWrapper
    public void createConnection(final Call call, final CreateConnectionResponse response) {
        Log.d(this, "createConnection(%s) via %s.", call, getComponentName());
        BindCallback callback = new BindCallback() {
            @Override
            public void onSuccess() {
                ...... ...... ...... ...... ...... ...... ...... ...... try {
                    mServiceInterface.createConnection( call.getConnectionManagerPhoneAccount(), callId, new ConnectionRequest( call.getTargetPhoneAccount(), call.getHandle(), extras, call.getVideoState(), callId), call.shouldAttachToExistingConnection(), call.isUnknown());
                } 
                ...... ...... ...... ......

            @Override
            public void onFailure() {
                Log.e(this, new Exception(), "Failure to call %s", getComponentName());
                  response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR));
            }
        };

        mBinder.bind(callback, call);
    }

這裏的mBinder對象是ConnectionServiceWrapper的父類ServiceBinder裏的一個內部類

封裝了綁定遠程服務的一些操做,若當前還未綁定服務,則直接調用bindService獲取遠程服務的aidl接口,成功獲取到aidl接口後將其賦值給mServiceInterface,以下:

@Override
protected void setServiceInterface(IBinder binder) {
if (binder == null) {
// We have lost our service connection. Notify the world that this service is done.
// We must notify the adapter before CallsManager. The adapter will force any pending
// outgoing calls to try the next service. This needs to happen before CallsManager
// tries to clean up any calls still associated with this service.
            handleConnectionServiceDeath();
            mCallsManager.handleConnectionServiceDeath(this);
            mServiceInterface = null;
        } else {
            mServiceInterface = IConnectionService.Stub.asInterface(binder);
            addConnectionServiceAdapter(mAdapter);
        }
    }

最終不論是初次綁定仍是以前已綁定服務,調用 mBinder.bind(callback, call)成功後都會回到Callback的onSuccess方法,接着調用遠程服務的createConnection方法

mServiceInterface.createConnection(
                call.getConnectionManagerPhoneAccount(),
                callId,
                new ConnectionRequest(
                      call.getTargetPhoneAccount(),
                      call.getHandle(),
                      extras,
                      call.getVideoState(),
                      callId),
                      call.shouldAttachToExistingConnection(),
                      call.isUnknown());

接下來的流程就到了遠程服務的createConnection實現了

1.3 telecomFramework處理鏈接請求

查找IConnectionService的實現類,是ConnectionService的匿名內部類

android.telecom. ConnectionService
  private final IBinder mBinder = new IConnectionService.Stub() {

       @Override
        public void createConnection(
                PhoneAccountHandle connectionManagerPhoneAccount,
                String id,
                ConnectionRequest request,
                boolean isIncoming,
                boolean isUnknown) {
            SomeArgs args = SomeArgs.obtain();
            args.arg1 = connectionManagerPhoneAccount;
            args.arg2 = id;
            args.arg3 = request;
            args.argi1 = isIncoming ? 1 : 0;
            args.argi2 = isUnknown ? 1 : 0;
            mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget();
        }
}

Handle處理消息最後進入createConnection方法

private void createConnection(final PhoneAccountHandle callManagerAccount,
            final String callId, final ConnectionRequest request, boolean isIncoming,  boolean isUnknown) {

        Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request): isIncoming ?
            onCreateIncomingConnection(callManagerAccount, request) : onCreateOutgoingConnection(callManagerAccount, request); //去電事件在這裏
         Log.d(this, "createConnection, connection: %s", connection);

        if (connection == null) { //建立鏈接失敗
            connection = Connection.createFailedConnection(
                    new DisconnectCause(DisconnectCause.ERROR));
        }

        connection.setTelecomCallId(callId);
        if (connection.getState() != Connection.STATE_DISCONNECTED) {
            addConnection(callId, connection); //建立鏈接成功,添加到隊列
         }

        Uri address = connection.getAddress();
        String number = address == null ? "null" : address.getSchemeSpecificPart();
        Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s, properties: %s",
                Connection.toLogSafePhoneNumber(number),
                Connection.stateToString(connection.getState()),
                Connection.capabilitiesToString(connection.getConnectionCapabilities()),
                Connection.propertiesToString(connection.getConnectionProperties()));

        Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);
        mAdapter.handleCreateConnectionComplete(
                callId,
                request,
                new ParcelableConnection(
                        request.getAccountHandle(),
                        connection.getState(),
                        connection.getConnectionCapabilities(),
                        connection.getConnectionProperties(),
                        connection.getAddress(),
                        connection.getAddressPresentation(),
                        connection.getCallerDisplayName(),
                        connection.getCallerDisplayNamePresentation(),
                        connection.getVideoProvider() == null ?
                                null : connection.getVideoProvider().getInterface(),
                        connection.getVideoState(),
                        connection.isRingbackRequested(),
                        connection.getAudioModeIsVoip(),
                        connection.getConnectTimeMillis(),
                        connection.getStatusHints(),
                        connection.getDisconnectCause(),
                        createIdList(connection.getConferenceables()),
                        connection.getExtras(),
                        connection.getUserData()));//MOTO Calling Code - IKPIM-1774 (ftr 33860)
        if (isUnknown) {
            triggerConferenceRecalculate();
        }
}

這裏根據來電或去掉建立不一樣的Connection對象,去電走onCreateOutgoingConnection流程,該方法返回null,因此具體是由其子類實現的,也就是TelephonyConnectionService

1.4 telephonyService建立呼出鏈接

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

        ............. ............. ............ .............
        // 這裏會有不少狀況會直接返回鏈接錯誤,好比未指定卡帳戶撥打語音信息,
         // 或者撥打的號碼爲空,指定卡被禁用(非緊急撥號)等

        final TelephonyConnection connection =
        createConnectionFor(phone, null, true /* isOutgoing */, request.getAccountHandle(),
               request.getTelecomCallId(), request.getAddress());
              ............ .............
             placeOutgoingConnection(connection, phone, request);
        }

        return connection;
    }

方法內部做了不少預處理,若是失敗則直接返回錯誤鏈接對象failedConnection,若是成功則建立一個TelephonyConnection對象,看看該對象是如何建立的:

private TelephonyConnection createConnectionFor(
            Phone phone, com.android.internal.telephony.Connection originalConnection,
            boolean isOutgoing,PhoneAccountHandle phoneAccountHandle,
            String telecomCallId, Uri address) {
        TelephonyConnection returnConnection = null;
        int phoneType = phone.getPhoneType();
        if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
            returnConnection = new GsmConnection(originalConnection, telecomCallId);
        } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
            boolean allowsMute = allowsMute(phone);
            returnConnection = new CdmaConnection(originalConnection, mEmergencyTonePlayer, allowsMute, isOutgoing, telecomCallId);
        }
        if (returnConnection != null) {
            // Listen to Telephony specific callbacks from the connection
            returnConnection.addTelephonyConnectionListener(mTelephonyConnectionListener);
            returnConnection.setVideoPauseSupported(
                    TelecomAccountRegistry.getInstance(this).isVideoPauseSupported(
                            phoneAccountHandle));
            boolean isEmergencyCall = (address != null && PhoneNumberUtils.isEmergencyNumber(
                    address.getSchemeSpecificPart()));
            returnConnection.setConferenceSupported(!isEmergencyCall
                    && TelecomAccountRegistry.getInstance(this).isMergeCallSupported(
                            phoneAccountHandle));
        }
        return returnConnection;

若是是GSM,則建立GsmConnection對象,若是是CDMA,則建立CdmaConnection對象,獲取到對象connection對象後,最後調用placeOutgoingConnection(connection, phone, request);進入呼出流程

private void placeOutgoingConnection(
            TelephonyConnection connection, Phone phone, ConnectionRequest request) {
        String number = connection.getAddress().getSchemeSpecificPart();

        com.android.internal.telephony.Connection originalConnection;
        try {
            originalConnection 
                       = phone.dial(number, null , request.getVideoState(), request.getExtras());
        } catch (CallStateException e) {
            Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e);
            int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
            if (e.getError() == CallStateException.ERROR_DISCONNECTED) {
                cause = android.telephony.DisconnectCause.OUT_OF_SERVICE;
            }
            connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
                    cause, e.getMessage()));
            return;
        }
          ……………………………… ………………………………

}

這裏的originalConnection = phone.dial(number, null, request.getVideoState(), request.getExtras());就是實際的phone呼出入口,由此進入telephonyFramework模塊

1.5 telepnonyFramework處理phone呼出

在android N上,cdma和gsm都由GsmCdmaPhone對象統一處理,看看dial的實現:

com.android.internal.telephony.GsmCdmaPhone
public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
            throws CallStateException {
            ……………………………… ………………………………

        if ((imsUseEnabled && (!isUt || useImsForUt)) || useImsForEmergency) {
            try {
                if (DBG) logd("Trying IMS PS call");
                return imsPhone.dial(dialString, uusInfo, videoState, intentExtras);
            } catch (CallStateException e) {
                if (DBG) logd("IMS PS call exception " + e +
                        "imsUseEnabled =" + imsUseEnabled + ", imsPhone =" + imsPhone);
                if (!Phone.CS_FALLBACK.equals(e.getMessage())) {
                    CallStateException ce = new CallStateException(e.getMessage());
                    ce.setStackTrace(e.getStackTrace());
                    throw ce;
                }
            }
        }

        if (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");
        }
        if (DBG) logd("Trying (non-IMS) CS call");

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

這裏還涉及到一個ImsPhone對象,它也是Phone的子類是專門用於處理volte通話的,這裏先不討論它,最後進入dialInternal方法

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

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

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

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

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

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

這裏將號碼進行格式化,去除無效字符,而後調用mCT的dial方法,這裏的mCT就是GsmCdmaCallTracker對象

com.android.internal.telephony. GsmCdmaCallTracker
public synchronized Connection dial(String dialString, int clirMode, UUSInfo uusInfo,
                                        Bundle intentExtras)
            throws CallStateException {
        // note that this triggers call state changed notif
        clearDisconnected();
       ……………………………… ………………………………
         mCi.dial(mPendingMO.getAddress(), clirMode, uusInfo, obtainCompleteMessage());
        if (mNumberConverted) {
            mPendingMO.setConverted(origNumber);
            mNumberConverted = false;
        }
        updatePhoneState();   //更新phone狀態
         mPhone.notifyPreciseCallStateChanged(); //發起phone狀態變化通知

        return mPendingMO; //返回通話鏈接

}

這裏的mCi是CommandsInterface接口,執行dial呼叫操做,追溯mCi的源頭,發現是在建立GsmCdmaCallTracker時從GsmCdmaPhone裏獲取的,而GsmCdmaPhone對象又是由PhoneFactory建立,看看代碼片斷:

com.android.internal.telephony.PhoneFactory
public static void makeDefaultPhone(Context context) {
 ……………………………… ………………………………
      for (int i = 0; i < numPhones; i++) {
          try {
            networkModes[i]  = TelephonyManager.getIntAtIndex(
                  context.getContentResolver(),
                    Settings.Global.PREFERRED_NETWORK_MODE, i);
           } catch (SettingNotFoundException snfe) {
               Rlog.d(LOG_TAG, "Network Mode is not available for " + i);
                  networkModes[i] = RILConstants.PREFERRED_NETWORK_MODE;
           }
       Rlog.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkModes[i]));
      sCommandsInterfaces[i] = new RIL(context, networkModes[i], cdmaSubscription, i);
     }
   ………………………………
    for (int i = 0; i < numPhones; i++) {
         Phone phone = null;
         int phoneType = TelephonyManager.getPhoneType(networkModes[i]);
         if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
          phone = TelephonyPluginDelegate.getInstance().makeGsmCdmaPhone(context,
 sCommandsInterfaces[i], sPhoneNotifier, i, PhoneConstants.PHONE_TYPE_GSM, TelephonyComponentFactory.getInstance());
          } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
            phone = TelephonyPluginDelegate.getInstance().makeGsmCdmaPhone(context,
 sCommandsInterfaces[i], sPhoneNotifier, i, PhoneConstants.PHONE_TYPE_CDMA_LTE, TelephonyComponentFactory.getInstance()); } Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i); sPhones[i] = phone; }
}

sCommandsInterfaces的建立的是RIL實例,因此呼叫操做實際上試執行RIL的dial方法:

com.android.internal.telephony.RIL
    dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
        RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);

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

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

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

        mEventLog.writeRilDial(rr.mSerial, clirMode, uusInfo);

        send(rr);
    }

這裏建立了一個RILRequest對象,將本身包裝到Message裏而後發送到RILSender的消息隊列裏,

send(RILRequest rr) {
        Message msg;

        if (mSocket == null) {
            rr.onError(RADIO_NOT_AVAILABLE, null);
            rr.release();
            return;
        }

        msg = mSender.obtainMessage(EVENT_SEND, rr);
 acquireWakeLock(rr, FOR_WAKELOCK); msg.sendToTarget(); }

RILSender接收處理:

class RILSender extends Handler implements Runnable {

  handleMessage(Message msg) {
            RILRequest rr = (RILRequest)(msg.obj);
            RILRequest req = null;

            switch (msg.what) {
            case EVENT_SEND:
case EVENT_SEND_ACK:
 LocalSocket s; s = mSocket;  ……………………………… s.getOutputStream().write(dataLength); s.getOutputStream().write(data);  ……………………………… break;  ……………………………… }  ……………………………… ……………………………… }

這裏就涉及到framework跟RIL進程的Socket通訊,交互框架圖以下:

image001

RILSender負責發送請求,RILReceiver負責接收消息,RILReceiver接收到消息後執行

processResponse方法

processResponse (Parcel p) {
      int type;
      type = p.readInt();
       if (type == RESPONSE_UNSOLICITED || type == RESPONSE_UNSOLICITED_ACK_EXP) {
            processUnsolicited (p, type);
       } else if (type == RESPONSE_SOLICITED || type == RESPONSE_SOLICITED_ACK_EXP) {
           RILRequest rr = processSolicited (p, type);
 if (rr != null) { if (type == RESPONSE_SOLICITED) { decrementWakeLock(rr); } rr.release(); return; } } else if (type == RESPONSE_SOLICITED_ACK) { } }

對於RILReceiver收到底層上報上來的消息分爲兩種

RESPONSE_UNSOLICITED表明modem主動上報的事件如來電,對方主動掛斷等

RESPONSE_SOLICITED表明modem迴應上層下發的事件如撥號,接聽電話等

因此剛剛的撥號請求應該是由processSolicited處理

processSolicited (Parcel p, int type) {
        int serial, error;
        boolean found = false;

        serial = p.readInt();
        error = p.readInt();

        RILRequest rr;
        rr = findAndRemoveRequestFromList(serial);
  ………………………………  switch (rr.mRequest){  case RIL_REQUEST_DIAL: ret =  responseVoid(p); break;
              ………………………………
           }
              ………………………………
          if (rr.mResult != null) {
                AsyncResult.forMessage(rr.mResult, ret, null);
                rr.mResult.sendToTarget();
        }
        mEventLog.writeOnRilSolicitedResponse(rr.mSerial, error, rr.mRequest, ret);
        return rr;
}

findAndRemoveRequestFromList查找對應的發送請求對象RILRequest,取出Message併發送通知,這裏Message就是發給GsmCdmaCallTracker,處理消息爲EVENT_OPERATION_COMPLETE

進入operationComplete

private void operationComplete() {
        mPendingOperations--;
        if (DBG_POLL) log("operationComplete: pendingOperations=" +
                mPendingOperations + ", needsPoll=" + mNeedsPoll);
        if (mPendingOperations == 0 && mNeedsPoll) {
            mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
 mCi.getCurrentCalls(mLastRelevantPoll); } else if (mPendingOperations < 0) { // this should never happen
            Rlog.e(LOG_TAG,"GsmCdmaCallTracker.pendingOperations < 0"); mPendingOperations = 0; } }

這裏又封裝了一個Message對象,調用RIL的getCurrentCalls方法主動獲取一次call狀態,接下來的流程又進入與RIL層的socket通訊,過程與前面同樣,最後GsmCdmaCallTracker收到

@Override
    protected synchronized void handlePollCalls(AsyncResult ar) {
        List polledCalls;
          ……………………………… ………………………………

       updatePhoneState();

        if (unknownConnectionAppeared) {
            if (isPhoneTypeGsm()) {
                for (Connection c : newUnknownConnectionsGsm) {
                    log("Notify unknown for " + c);
                    mPhone.notifyUnknownConnection(c);
                }
            } else {
                mPhone.notifyUnknownConnection(newUnknownConnectionCdma);
            }
        }

        if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) {
            mPhone.notifyPreciseCallStateChanged();
        }
}

最後更新phone狀態,註冊了該phone電話狀態監聽的對象將會收到通知

後續modem會主動上報一些電話狀態的變化:RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED

processUnsolicited (Parcel p, int type) {

           ………………………………

        try {switch(response) {     
             case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
                if (RILJ_LOGD) unsljLog(response);

                 mCallStateRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
              break;
            ………………………………
        }
}

mCallStateRegistrants發出通知(RegistrantList消息處理機制)

GsmCdmaCallTracker在建立的時候註冊了該通知

mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);

具體處理由它的父類CallTracker實現

com.android.internal.telephony. pollCallsWhenSafe
    protected void pollCallsWhenSafe() {
        mNeedsPoll = true;
        if (checkNoOperationsPending()) {
            mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
            mCi.getCurrentCalls(mLastRelevantPoll);
        }
    }

與前面主動獲取call狀態的處理同樣,只不過如今是收到modem主動上報的call狀態變化消息而後處理並更新phone狀態

1.6 小結

至此,一個撥號流程就完成了,大體流程如

Dialer→TeleComService→TeleComFramework→TeleponyService→TelephonyFramework→RIL

下一章節:Android7.0 Phone應用源碼分析(二) phone來電流程分析

相關文章
相關標籤/搜索