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(), 同上:
网友评论