写在前面的话

本文主要分析Android 接电话的流程,研究的代码是Android 5.1的,现在我们只关注应用层,以CDMA为例,GSM同理。

一、显示来电的界面

android 接听拨打电话没有声音而其他正常_Phone


(如果图片看不清的话,可以右键选择在新标签中打开图片,或者把图片另存到自己电脑再查看。)


本文来自 ,转载请务必注明出处。


步骤1,2:在Framework层的最后,是由PhoneBase.java将来电通知传递到应用层的,如果想了解这段流程,请看《Android 5.1 Phone MT(来电)流程分析(Framework层) 》的步骤1~13。

步骤3,4,5: 好了,回到正题,本文主要讲的是应用层的流程。PstnIncomingCallNotifier.java在registerForNotifications()方法里注册监听了EVENT_NEW_RINGING_CONNECTION事件,因此它会接收到Framework层传递过来的来电通知。在PstnIncomingCallNotifier.java里mHandler的handleMessage()方法有EVENT_NEW_RINGING_CONNECTION相应的处理。

/**
    * Used to listen to events from {@link #mPhoneBase}.
    */
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch(msg.what) {
                case EVENT_NEW_RINGING_CONNECTION:
                    handleNewRingingConnection((AsyncResult) msg.obj);
                    break;
                ...
            }
        }
    };

   /**
     * Verifies the incoming call and triggers sending the incoming-call intent to Telecom.
     *
     * @param asyncResult The result object from the new ringing event.
     */
    private void handleNewRingingConnection(AsyncResult asyncResult) {
        Log.d(this, "handleNewRingingConnection");
        Connection connection = (Connection) asyncResult.result;
        if (connection != null) {
            Call call = connection.getCall();
            // Final verification of the ringing state before sending the intent to Telecom.
            if (call != null && call.getState().isRinging()) {
                Phone phone = call.getPhone();
                if (phone != null&& isBlockedByFirewall(connection.getAddress(), 
                phone.getPhoneId())) {
                PhoneUtils.hangupRingingCall(call);
                sendBlockRecordBroadcast(phone.getPhoneId(), connection.getAddress());
                return;
                }
                sendIncomingCallIntent(connection);
            }
        }
    }

   /**
     * Sends the incoming call intent to telecom.
     */
    private void sendIncomingCallIntent(Connection connection) {
      ...
      TelecomManager.from(mPhoneProxy.getContext()).addNewIncomingCall(
        TelecomAccountRegistry.makePstnPhoneAccountHandle(mPhoneProxy), extras);
    }
}

步骤6: TelecomManager.java的addNewIncomingCall()方法

/**
     * Registers a new incoming call. A {@link ConnectionService} should invoke this method when it
     * has an incoming call. The specified {@link PhoneAccountHandle} must have been registered
     * with {@link #registerPhoneAccount}. Once invoked, this method will cause the system to bind
     * to the {@link ConnectionService} associated with the {@link PhoneAccountHandle} and request
     * additional information about the call (See
     * {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming call UI.
     *
     * @param phoneAccount A {@link PhoneAccountHandle} registered with
     *            {@link #registerPhoneAccount}.
     * @param extras A bundle that will be passed through to
     *            {@link ConnectionService#onCreateIncomingConnection}.
     * @hide
     */
    @SystemApi
    public void addNewIncomingCall(PhoneAccountHandle phoneAccount, Bundle extras) {
        try {
            if (isServiceConnected()) {
                getTelecomService().addNewIncomingCall(
                        phoneAccount, extras == null ? new Bundle() : extras);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException adding a new incoming call: " + phoneAccount, e);
        }
    }

步骤7~10: TelecomService.java的addNewIncomingCall()方法

/**
         * @see android.telecom.TelecomManager#addNewIncomingCall
         */
        @Override
        public void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
            Log.i(this, "Adding new incoming call with phoneAccountHandle %s", phoneAccountHandle);
            if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null) {
                mAppOpsManager.checkPackage(
                        Binder.getCallingUid(), phoneAccountHandle.getComponentName().getPackageName());

                // Make sure it doesn't cross the UserHandle boundary
                enforceUserHandleMatchesCaller(phoneAccountHandle);

                Intent intent = new Intent(TelecomManager.ACTION_INCOMING_CALL);
                intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
                intent.putExtra(CallReceiver.KEY_IS_INCOMING_CALL, true);
                if (extras != null) {
                    intent.putExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras);
                }
                sendRequestAsync(MSG_NEW_INCOMING_CALL, 0, intent);
            } else {
                Log.w(this, "Null phoneAccountHandle. Ignoring request to add new incoming call");
            }
        }

    private MainThreadRequest sendRequestAsync(int command, int arg1, Object arg) {
        MainThreadRequest request = new MainThreadRequest();
        request.arg = arg;
        mMainThreadHandler.obtainMessage(command, arg1, 0, request).sendToTarget();
        return request;
    }

(在这里跟Android5.0是不一样的)在sendRequestAsync()方法里调用obtainMessage()方法创建了一个消息类型为MSG_NEW_INCOMING_CALL的Message,并且通过sendToTarget()发送出去。

步骤11,12: TelecomService.java里MainThreadHandler的handleMessage()方法有对MSG_NEW_INCOMING_CALL的处理。

case MSG_NEW_INCOMING_CALL:
     if (request.arg == null || !(request.arg instanceof Intent)) {
         Log.w(this, "Invalid new incoming call request");
         break;
     }
     CallReceiver.processIncomingCallIntent((Intent) request.arg);
     break;

调用了CallReceiver.java的processIncomingCallIntent(),进而又调用CallsManager.java的processIncomingCallIntent()方法。

步骤13: CallsManager.java的processIncomingCallIntent()方法

/**
     * Starts the process to attach the call to a connection service.
     *
     * @param phoneAccountHandle The phone account which contains the component name of the
     *        connection service to use for this call.
     * @param extras The optional extras Bundle passed with the intent used for the incoming call.
     */
    void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
        Log.d(this, "processIncomingCallIntent");
        Uri handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER);
        Call call = new Call(
                mContext,
                mConnectionServiceRepository,
                handle,
                null /* gatewayInfo */,
                null /* connectionManagerPhoneAccount */,
                phoneAccountHandle,
                true /* isIncoming */,
                false /* isConference */);

        call.setExtras(extras);
        // TODO: Move this to be a part of addCall()
        call.addListener(this);
        call.startCreateConnection(mPhoneAccountRegistrar);
    }

在这里创建了一个Call对象,并且把需要的参数传递进来,并且调用Call的startCreateConnection方法。

步骤14,15,16: Call.java的startCreateConnection()方法,这个Call.java是在packages\services\telecomm\src\com\android\server\telecom目录下的。

/**
     * Starts the create connection sequence. Upon completion, there should exist an active
     * connection through a connection service (or the call will have failed).
     *
     * @param phoneAccountRegistrar The phone account registrar.
     */
    void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
        Preconditions.checkState(mCreateConnectionProcessor == null);
        mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
                phoneAccountRegistrar, mContext);
        mCreateConnectionProcessor.process();
    }

创建了一个CreateConnectionProcessor对象,先调用它的process()方法,再调用attemptNextPhoneAccount方法,最后调用了ConnectionServiceWrapper.java的createConnection()方法,步骤13中创建的Call对象也就被传递到了这里。

步骤17: ConnectionServiceWrapper.java的createConnection()方法。

/**
     * Creates a new connection for a new outgoing call or to attach to an existing incoming call.
     */
    void createConnection(final Call call, final CreateConnectionResponse response) {
        Log.d(this, "createConnection(%s) via %s.", call, getComponentName());
        BindCallback callback = new BindCallback() {
            @Override
            public void onSuccess() {
                String callId = mCallIdMapper.getCallId(call);
                mPendingResponses.put(callId, response);

                GatewayInfo gatewayInfo = call.getGatewayInfo();
                Bundle extras = call.getExtras();
                if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null &&
                        gatewayInfo.getOriginalAddress() != null) {
                    extras = (Bundle) extras.clone();
                    extras.putString(
                            TelecomManager.GATEWAY_PROVIDER_PACKAGE,
                            gatewayInfo.getGatewayProviderPackageName());
                    extras.putParcelable(
                            TelecomManager.GATEWAY_ORIGINAL_ADDRESS,
                            gatewayInfo.getOriginalAddress());
                }

                try {
                    mServiceInterface.createConnection(
                            call.getConnectionManagerPhoneAccount(),
                            callId,
                            new ConnectionRequest(
                                    call.getTargetPhoneAccount(),
                                    call.getHandle(),
                                    extras,
                                    call.getVideoState()),
                            call.isIncoming(),
                            call.isUnknown());
                } catch (RemoteException e) {
                    Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
                    mPendingResponses.remove(callId).handleCreateConnectionFailure(
                            new DisconnectCause(DisconnectCause.ERROR, e.toString()));
                }
            }

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

        mBinder.bind(callback);
    }

步骤18,19:这里调用了ConnectionServiceWrapper的父类ServiceBinder的bind()方法,先new一个ServiceConnection对象,然后绑定一个远程服务端服务。如果绑定成功的话,在ServiceBinder的内部类ServiceBinderConnection的onServiceConnected()方法就被调用。
在这里做了两件事:
1、步骤20,21:通过setBinder()方法,回调ConnectionServiceWrapper的setServiceInterface()方法,通过mServiceInterface = IConnectionService.Stub.asInterface(binder);
这行代码获取一个远程服务端的对象mServiceInterface 。
2、步骤22,23:再通过调用handleSuccessfulConnection()方法回调callback 的onSuccess()方法,也就又回到ConnectionServiceWrapper的createConnection()方法里。
步骤24,25:最后通过这一行mServiceInterface.createConnection();
,调用ConnectionService.java里mBinder的createConnection()方法。

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

步骤26,27:在这里通过obtainMessage()方法创建了一个消息类型为MSG_CREATE_CONNECTION的Message,再把传进来的参数封装到Message里再发送出去,然后在ConnectionService.java里mHandler的handleMessage()方法里处理这个Message,最后就调用了ConnectionService.java的createConnection()方法。

步骤28: ConnectionService.java的createConnection()方法。

/**
     * This can be used by telecom to either create a new outgoing call or attach to an existing
     * incoming call. In either case, telecom will cycle through a set of services and call
     * createConnection util a connection service cancels the process or completes it successfully.
     */
    private void createConnection(
            final PhoneAccountHandle callManagerAccount,
            final String callId,
            final ConnectionRequest request,
            boolean isIncoming,
            boolean isUnknown) {
        Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
                "isIncoming: %b, isUnknown: %b", callManagerAccount, callId, request, isIncoming,
                isUnknown);

        Connection connection = 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));
        }

        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: 0x%x",
                Connection.toLogSafePhoneNumber(number),
                Connection.stateToString(connection.getState()),
                Connection.capabilitiesToString(connection.getConnectionCapabilities()),
                connection.getCallProperties());

        Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);
        mAdapter.handleCreateConnectionComplete(
                callId,
                request,
                new ParcelableConnection(
                        getAccountHandle(request, connection),
                        connection.getState(),
                        connection.getConnectionCapabilities(),
                        connection.getCallProperties(),
                        connection.getAddress(),
                        connection.getAddressPresentation(),
                        connection.getCallerDisplayName(),
                        connection.getCallerDisplayNamePresentation(),
                        connection.getVideoProvider() == null ?
                                null : connection.getVideoProvider().getInterface(),
                        connection.getVideoState(),
                        connection.isRingbackRequested(),
                        connection.getAudioModeIsVoip(),
                        connection.getStatusHints(),
                        connection.getDisconnectCause(),
                        createIdList(connection.getConferenceables()),
                        connection.getCallSubstate()));
        if (isUnknown) {
            triggerConferenceRecalculate();
        }
    }

步骤29: onCreateIncomingConnection()方法会被调用到,这个方法被TelephonyConnectionService重写,TelephonyConnectionService是ConnectionService的实例,所以会调用TelephonyConnectionService.java的onCreateIncomingConnection()方法来创建一个CDMAConnection对象。

步骤30:创建CDMAConnection对象之后,就调用ConnectionServiceAdapter.java的handleCreateConnectionComplete()来处理之后的事情,比如启动UI界面之类。

步骤31~39:流程一直走,这一段也没什么好说的了。

步骤40: CallsManager.java的onSuccessfulIncomingCall()方法。
在这里做了两件事:
1、把Call的状态从NEW改成RINGING
2、把Call对象添加到Call的集合里。(步骤41)

步骤42~63: 启动UI界面,有两种方式,弹出一个小窗口显示来电,或者全屏显示来电。


二、接听电话

android 接听拨打电话没有声音而其他正常_Phone_02

也没什么好说的,时序图已经写得比较明白,在步骤18之后,就紧接着《Android 5.1 Phone MT(来电)流程分析(Framework层) 》的步骤21。等真正接通电话之后,就把Call的状态从RINGING改成ACTIVE,Call的状态改变之后,就会停止响铃,对应《Android 5.1 Phone MT(来电)流程分析(Framework层) 》的步骤20。


如果想继续了解Framework层的流程,请看《Android 5.1 Phone MT(来电)流程分析(Framework层) 》。