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

2020-07-09  本文已影响0人  fjasmin


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(), 同上:

上一篇下一篇

猜你喜欢

热点阅读