Android 5.1 Phone MT(来电)流程分析(应用层)

来源:互联网 发布:汽车美容快修软件 编辑:程序博客网 时间:2024/06/05 15:00

写在前面的话

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

一、显示来电的界面


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


http://blog.csdn.net/linyongan


步骤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界面,有两种方式,弹出一个小窗口显示来电,或者全屏显示来电。


二、接听电话

也没什么好说的,时序图已经写得比较明白,在步骤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层) 》。

2 0