美文网首页
2020-07-09蓝牙电话apk的实现及源码分析(3)

2020-07-09蓝牙电话apk的实现及源码分析(3)

作者: fjasmin | 来源:发表于2020-07-09 22:46 被阅读0次


    Car 模块拨打电话

    Android 7.0 增加车载新特性,在Car模块中实现了蓝牙电话功能

    路径: android-8.0.0_r1\packages\apps\Car\Dialer\src\com\android\car\dialer

    code:

    DialerFragment.java:

    callButton.setOnClickListener((unusedView) -> {

    getUiCallManager().safePlaceCall(mNumber.toString(),false);

    });

    UiCallManager.java:

    publicvoidsafePlaceCall(String number,booleanbluetoothRequired){

        placeCall(number);

    }

    TelecomUiCallManager.java:

    @Override

    publicvoidplaceCall(String number){

    Uri uri = Uri.fromParts("tel", number,null);

    mTelecomManager.placeCall(uri,null);

    }

    TelecomManager.java:

    publicvoidplaceCall(Uri address, Bundle extras){

        ITelecomService service = getTelecomService();

    service.placeCall(address, extras ==null?newBundle() : extras,

                mContext.getOpPackageName());

    }

    privateITelecomServicegetTelecomService(){

    returnITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));

    }

    TelecomService.java:

    @Override

    publicIBinderonBind(Intent intent){

    synchronized(getTelecomSystem().getLock()) {

    returngetTelecomSystem().getTelecomServiceImpl().getBinder();

        }

    }

    TelecomServiceImpl.java:

    @Override

    publicvoidplaceCall(Uri handle, Bundle extras, String callingPackage){

    finalUserHandle userHandle = Binder.getCallingUserHandle();

    finalIntent intent =newIntent(Intent.ACTION_CALL, handle);

    if(extras !=null) {

    extras.setDefusable(true);

            intent.putExtras(extras);

        }

        mUserCallIntentProcessorFactory.create(mContext, userHandle)

                .processIntent(

                        intent, callingPackage, isSelfManaged ||

                                (hasCallAppOp && hasCallPermission));

    }

    UserCallIntentProcessor.java:

    publicvoidprocessIntent(Intent intent, String callingPackageName,

    booleancanCallNonEmergency){

        processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency);

    }

    privatevoidprocessOutgoingCallIntent(Intent intent, String callingPackageName,

    booleancanCallNonEmergency){

        intent.putExtra(CallIntentProcessor.KEY_INITIATING_USER, mUserHandle);

        sendBroadcastToReceiver(intent);

    }

    PrimaryCallReceiver.java:

    @Override

    publicvoidonReceive(Context context, Intent intent){

        getTelecomSystem().getCallIntentProcessor().processIntent(intent);

    }

    @Override

    publicTelecomSystemgetTelecomSystem(){

    returnTelecomSystem.getInstance();

    }

    CallIntentProcessor.java:

    publicvoidprocessIntent(Intent intent){

        processOutgoingCallIntent(mContext, mCallsManager, intent);

    }

    staticvoidprocessOutgoingCallIntent(

                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,

                        intent);

        sendNewOutgoingCallIntent(context, call, callsManager, intent);

    }

    staticvoidsendNewOutgoingCallIntent(Context context, Call call, CallsManager callsManager,

                Intent intent){

    // Asynchronous calls should not usually be made inside a BroadcastReceiver because once

    // onReceive is complete, the BroadcastReceiver's process runs the risk of getting

    // killed if memory is scarce. However, this is OK here because the entire Telecom

    // process will be running throughout the duration of the phone call and should never

    // be killed.

    NewOutgoingCallIntentBroadcaster broadcaster =newNewOutgoingCallIntentBroadcaster(

                context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(),

                isPrivilegedDialer);

    finalintresult = broadcaster.processIntent();

    }

    NewOutgoingCallBroadcastIntentReceiver.java:

    publicintprocessIntent(){

        Intent intent = mIntent;

        String action = intent.getAction();

    finalUri handle = intent.getData();

    if(handle ==null) {

    Log.w(this,"Empty handle obtained from the call intent.");

    returnDisconnectCause.INVALID_NUMBER;

        }

    booleanisVoicemailNumber = PhoneAccount.SCHEME_VOICEMAIL.equals(handle.getScheme());

    if(isVoicemailNumber) {

    if(Intent.ACTION_CALL.equals(action)

                    || Intent.ACTION_CALL_PRIVILEGED.equals(action)) {

    // Voicemail calls will be handled directly by the telephony connection manager

    Log.i(this,"Placing call immediately instead of waiting for "

    +" OutgoingCallBroadcastReceiver: %s", intent);

    // Since we are not going to go through "Outgoing call broadcast", make sure

    // we mark it as ready.

                mCall.setNewOutgoingCallIntentBroadcastIsDone();

    booleanspeakerphoneOn = mIntent.getBooleanExtra(

    TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE,false);

    mCallsManager.placeOutgoingCall(mCall, handle,null, speakerphoneOn,

                        VideoProfile.STATE_AUDIO_ONLY);

    returnDisconnectCause.NOT_DISCONNECTED;

    }else{

    Log.i(this,"Unhandled intent %s. Ignoring and not placing call.", intent);

    returnDisconnectCause.OUTGOING_CANCELED;

            }

        }

        String number = mPhoneNumberUtilsAdapter.getNumberFromIntent(intent, mContext);

    if(TextUtils.isEmpty(number)) {

    Log.w(this,"Empty number obtained from the call intent.");

    returnDisconnectCause.NO_PHONE_NUMBER_SUPPLIED;

        }

    booleanisUriNumber = mPhoneNumberUtilsAdapter.isUriNumber(number);

    if(!isUriNumber) {

            number = mPhoneNumberUtilsAdapter.convertKeypadLettersToDigits(number);

            number = mPhoneNumberUtilsAdapter.stripSeparators(number);

        }

    finalbooleanisPotentialEmergencyNumber = isPotentialEmergencyNumber(number);

    Log.v(this,"isPotentialEmergencyNumber = %s", isPotentialEmergencyNumber);

        rewriteCallIntentAction(intent, isPotentialEmergencyNumber);

        action = intent.getAction();

    // True for certain types of numbers that are not intended to be intercepted or modified

    // by third parties (e.g. emergency numbers).

    booleancallImmediately =false;

    if(Intent.ACTION_CALL.equals(action)) {

    if(isPotentialEmergencyNumber) {

    if(!mIsDefaultOrSystemPhoneApp) {

    Log.w(this,"Cannot call potential emergency number %s with CALL Intent %s "

    +"unless caller is system or default dialer.", number, intent);

                    launchSystemDialer(intent.getData());

    returnDisconnectCause.OUTGOING_CANCELED;

    }else{

    callImmediately =true;

                }

            }

    }elseif(Intent.ACTION_CALL_EMERGENCY.equals(action)) {

    if(!isPotentialEmergencyNumber) {

    Log.w(this,"Cannot call non-potential-emergency number %s with EMERGENCY_CALL "

    +"Intent %s.", number, intent);

    returnDisconnectCause.OUTGOING_CANCELED;

            }

    callImmediately =true;

    }else{

    Log.w(this,"Unhandled Intent %s. Ignoring and not placing call.", intent);

    returnDisconnectCause.INVALID_NUMBER;

        }

    if(callImmediately) {

    Log.i(this,"Placing call immediately instead of waiting for "

    +" OutgoingCallBroadcastReceiver: %s", intent);

            String scheme = isUriNumber ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL;

    booleanspeakerphoneOn = mIntent.getBooleanExtra(

    TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE,false);

    intvideoState = mIntent.getIntExtra(

                    TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,

                    VideoProfile.STATE_AUDIO_ONLY);

    mCallsManager.placeOutgoingCall(mCall, Uri.fromParts(scheme, number,null),null,

                    speakerphoneOn, videoState);

    // Don't return but instead continue and send the ACTION_NEW_OUTGOING_CALL broadcast

    // so that third parties can still inspect (but not intercept) the outgoing call. When

    // the broadcast finally reaches the OutgoingCallBroadcastReceiver, we'll know not to

    // initiate the call again because of the presence of the EXTRA_ALREADY_CALLED extra.

        }

        UserHandle targetUser = mCall.getInitiatingUser();

    Log.i(this,"Sending NewOutgoingCallBroadcast for %s to %s", mCall, targetUser);

        broadcastIntent(intent, number, !callImmediately, targetUser);

    returnDisconnectCause.NOT_DISCONNECTED;

    }

    CallsManager.java:

    CallstartOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras,

                UserHandle initiatingUser, Intent originalIntent){

    // Ensure new calls related to self-managed calls/connections are set as such.  This

    // will be overridden when the actual connection is returned in startCreateConnection,

    // however doing this now ensures the logs and any other logic will treat this call as

    // self-managed from the moment it is created.

    if(account !=null) {

            call.setIsSelfManaged(account.isSelfManaged());

    if(call.isSelfManaged()) {

    // Self-managed calls will ALWAYS use voip audio mode.

    call.setIsVoipAudioMode(true);

            }

        }

        call.setInitiatingUser(initiatingUser);

    isReusedCall =false;

    }

    Call.java:

    voidstartCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar){

    mCreateConnectionProcessor =newCreateConnectionProcessor(this, mRepository,this,

                phoneAccountRegistrar, mContext);

        mCreateConnectionProcessor.process();

    }

    CreateConnectionProcessor.java:

    publicvoidprocess(){

        attemptNextPhoneAccount();

    }

    privatevoidattemptNextPhoneAccount(){

    mService.createConnection(mCall,this);

    }

    最终还是调用 NativeInterface.dialNative(), 同上

    Car 模块接听电话

    android-8.0.0_r1\packages\apps\Car\Dialer\src\com\android\car\dialer\telecom\embedded

    TelecomUiCallManager.java:

    @Override

    publicvoidanswerCall(UiCall uiCall){

        Call telecomCall = mCallList.getTelecomCall(uiCall);

    if(telecomCall !=null) {

    telecomCall.answer(0);

        }

    }

    Call.java:

    publicvoidanswer(intvideoState){

    mConnectionService.answer(this, videoState);

    }

    ConnectionServiceWrapper.java:

    voidanswer(Call call,intvideoState){

    finalString callId = mCallIdMapper.getCallId(call);

    if(VideoProfile.isAudioOnly(videoState)) {

            mServiceInterface.answer(callId, Log.getExternalSession());

    }else{

            mServiceInterface.answerVideo(callId, videoState, Log.getExternalSession());

        }

    }

    ConnectionService.java:

    publicvoidanswer(String callId, Session.Info sessionInfo){

        SomeArgs args = SomeArgs.obtain();

        args.arg1 = callId;

        args.arg2 = Log.createSubsession();

        mHandler.obtainMessage(MSG_ANSWER, args).sendToTarget();

    }

    privatefinalHandler mHandler =newHandler(Looper.getMainLooper()) {

    @Override

    publicvoidhandleMessage(Message msg){

    switch(msg.what) {

    caseMSG_ANSWER: {

                    SomeArgs args = (SomeArgs) msg.obj;

                    answer((String) args.arg1);

    break;

                }

            }

        }

    };

    privatevoidanswer(String callId){

    findConnectionForAction(callId,"answer").onAnswer();

    }

    privateConnectionfindConnectionForAction(String callId, String action){

    if(mConnectionById.containsKey(callId)) {

    returnmConnectionById.get(callId);

        }

    returngetNullConnection();

    }

    InCallAdapter.java:

    // base\telecomm\java\android\telecom\InCallAdapter.java

    // Instructs Telecom to reject the specified call.

    publicvoidanswerCall(String callId,intvideoState){

        mAdapter.answerCall(callId, videoState);

    }

    InCallAdapter.java:

    // packages\services\Telecomm\src\com\android\server\telecom\InCallAdapter.java

    publicvoidanswerCall(String callId,intvideoState){

        Call call = mCallIdMapper.getCall(callId);

        mCallsManager.answerCall(call, videoState);

    }

    CallsManager.java:

    // Instructs Telecom to answer the specified call

    publicvoidanswerCall(Call call,intvideoState){

        call.answer(videoState);

    }

    Call.java:

    // packages\services\Telecomm\src\com\android\server\telecom\Call.java

    publicvoidanswer(intvideoState){

    // Check to verify that the call is still in the ringing state. A call can change states

    // between the time the user hits 'answer' and Telecom receives the command.

    if(isRinging("answer")) {

    if(!isVideoCallingSupported() && VideoProfile.isVideo(videoState)) {

                videoState = VideoProfile.STATE_AUDIO_ONLY;

            }

    // At this point, we are asking the connection service to answer but we don't assume

    // that it will work. Instead, we wait until confirmation from the connectino service

    // that the call is in a non-STATE_RINGING state before changing the UI. See

    // {@link ConnectionServiceAdapter#setActive} and other set* methods.

    mConnectionService.answer(this, videoState);

        }

    }

    最终还是调用 NativeInterface.handleCallActionNative(), 同上:

    相关文章

      网友评论

          本文标题:2020-07-09蓝牙电话apk的实现及源码分析(3)

          本文链接:https://www.haomeiwen.com/subject/yybecktx.html