Phone调用InCallUi流程
流程图
主要的类以及作用
Dialer
类名 | 描述 |
Dialer\com\android\dialer\app\dialpad\DialpadFragment.java | 拨号键盘 |
Dialer\java\com\android\dialer\util\DialerUtils.java | 拨号工具 |
Dialer\java\com\android\dialer\telecom\TelecomUtil.java | 通讯工具 |
Dialer\java\com\android\incallui\InCallServiceImpl.java | InCallUi开启服务,他继承的是InCallService |
framework/base/telecom
类名 | 描述 |
framework\base\telecomm\java\android\telecom\TelecomManager.java | 向外部提供接口,有很多地方会调用这个接口进行拨号 |
service/telecomm
类名 | 描述 |
services\Telecomm\src\com\android\server\telecom\components\TelecomService.java | 系统初始化的时候就会进行绑定,并不 是在打电话的时候才绑TelecomSystem 会被初始化,期间构建一些列必须源,包括Phone和CI(和底层沟通的对象)的构建 |
services\Telecomm\src\com\android\server\telecom\TelecomServiceImpl.java | 提供Binder接口 |
services\Telecomm\src\com\android\server\telecom\components\UserCallIntentProcessor.java | 用户拨号处理,处理 内部校验一些拨号的权限,以及其它作权限看看是不是需要弹框绝。比如 《只允许打紧急电话没有电话许可,此应用程序不能 发 出呼叫》 |
services\Telecomm\src\com\android\server\telecom\CallIntentProcessor.java | 判断Intent里面的数据,比如 号码是否为空 等等.... |
services\Telecomm\src\com\android\server\telecom\CallsManager.java | 官方声明 :CallManager对象为 Phone应用程序提供通用 Call API的方法。 SipPhone类实现Phone接口,但大部分由虚拟方法填充,实际现 使用的是 SipManager,访问 SIP通信框架并控制 SIP Call |
services\Telecomm\src\com\android\server\telecom\InCallController.java | 控制电话App的逻辑和UI |
流程图:
接下来开看代码逻辑 一个一个进程看
概述:拨号调用InCallUi模块的过程,如果按照进程来分,可以分成3个模块。
- com.android.dialer进程,电话APP模块 路径 : android/vendor/codeaurora/commonsys/packages/apps/Dialer
- com.android.dialer.InCallUi 呼出呼入页面 路径 :要看厂商是否分离了,如果是高通代码就放在Dialer App中
- System进程,系统框架层 Service/telecom层 路径 :android/packages/service
- com.android.phone进程, framework/ : 路径 : android/framework
拨号过程由App开始(或第三方调用Telephony),进入接口System进程获取接口,System接口会phone的一个Binder中的一个方法进行处理,进入Phone后做一些判断(比如:号码是否正确), 在启动InCallUil设置状态 ,状态改变 是通过Call对象进行变更
在电话中java层有三个Call对象 所在位置是
- 路径:android/packages/services/Telecomm
- 路径:android/frameworks/base/telecomm
- 路径:android/frameworks/opt/telephony
插入一个话题,InCallUi 就来电去电时展示,如果想实现一个InCallUi自己也是可以做到的,比如第三方APP 来电秀,来电视屏等等..都是继承了一个服务(InCallService,给第三方扩展使用),屏蔽调InCallUi所继承的服务
大致流程图:
Phone进程是
开始代码流程:
第一步进入Dialer App
Dialer\java\com\android\dialer\app\dialpad\DialpadFragment.java 属于Dialer拨号盘
private void handleDialButtonPressed() {
if (isDigitsEmpty()) { // No number entered.
// No real call made, so treat it as a click 没有真正打过电话,所以把它当作一次点击
PerformanceReport.recordClick(UiAction.Type.PRESS_CALL_BUTTON_WITHOUT_CALLING);
//号码空白自动补全
handleDialButtonClickWithEmptyDigits();
} else {
final String number = mDigits.getText().toString();
// "persist.radio.otaspdial" is a temporary hack needed for one carrier's automated
// test equipment.
// TODO: clean it up.
if (number != null
&& !TextUtils.isEmpty(mProhibitedPhoneNumberRegexp)
&& number.matches(mProhibitedPhoneNumberRegexp)) {
PerformanceReport.recordClick(UiAction.Type.PRESS_CALL_BUTTON_WITHOUT_CALLING);
LogUtil.i(
"DialpadFragment.handleDialButtonPressed",
"The phone number is prohibited explicitly by a rule.");
//处理一些不能拨打电话的情况
if (getActivity() != null) {
DialogFragment dialogFragment =
ErrorDialogFragment.newInstance(R.string.dialog_phone_call_prohibited_message);
dialogFragment.show(getFragmentManager(), "phone_prohibited_dialog");
}
// Clear the digits just in case.
clearDialpad();
} else {
final Intent intent =
new CallIntentBuilder(number, CallInitiationType.Type.DIALPAD).build();
DialerUtils.startActivityWithErrorToast(getActivity(), intent); //拨打进入拨打电话位置。核心,主流程
if(mSubscriptionManager.getActiveSubscriptionInfoCount() != 0) {
hideAndClearDialpad(false);
}
// hideAndClearDialpad(false);
}
}
}
DialerUtils.java 组要调用 placeCallOrMakeToast 进入TelecomUtil.placeCall()
public static void startActivityWithErrorToast(
final Context context, final Intent intent, int msgId) {
try {
if ((Intent.ACTION_CALL.equals(intent.getAction()))) {
// All dialer-initiated calls should pass the touch point to the InCallUI
Point touchPoint = TouchPointManager.getInstance().getPoint();
if (touchPoint.x != 0 || touchPoint.y != 0) {
Bundle extras;
// Make sure to not accidentally clobber any existing extras
if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) {
extras = intent.getParcelableExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
} else {
extras = new Bundle();
}
extras.putParcelable(TouchPointManager.TOUCH_POINT, touchPoint);
intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
}
if (shouldWarnForOutgoingWps(context, intent.getData().getSchemeSpecificPart())) {
LogUtil.i(
"DialUtils.startActivityWithErrorToast",
"showing outgoing WPS dialog before placing call");
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage(R.string.outgoing_wps_warning);//弹出对话框 拨打 WPS 电话会中断现有通话。
builder.setPositiveButton(
R.string.dialog_continue,
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
placeCallOrMakeToast(context, intent);
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.create().show();
} else {
placeCallOrMakeToast(context, intent);//核心
}
} else {
context.startActivity(intent);
}
} catch (ActivityNotFoundException e) {
Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show();
}
}
private static void placeCallOrMakeToast(Context context, Intent intent) {
final boolean hasCallPermission = TelecomUtil.placeCall(context, intent); //核心进入模块
if (!hasCallPermission) {
// TODO: Make calling activity show request permission dialog and handle
// callback results appropriately.
Toast.makeText(context, "Cannot place call without Phone permission", Toast.LENGTH_SHORT)
.show();
}
}
TelecomUtil.java 主要是调用TelecomManage.placeCall()方法
public static boolean placeCall(Context context, Intent intent) {
if (hasCallPhonePermission(context)) {
// 核心,主过程 这个方法主要是调用TelecomManager.placeCall();//TelecomManager.java属于Framework/Telephony代码
getTelecomManager(context).placeCall(intent.getData(), intent.getExtras());
return true;
}
return false;
}
第二步,Phone进程 接口framework/base/telecom
TelecomManager.java
public void placeCall(Uri address, Bundle extras) {
ITelecomService service = getTelecomService();
if (service != null) {
if (address == null) {
Log.w(TAG, "Cannot place call to empty address.");
}
try {
service.placeCall(address, extras == null ? new Bundle() : extras,
mContext.getOpPackageName());//调用packager/service/telecom中的Binder
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#placeCall", e);
}
}
}
第三部 进入System进程 packages/services/Telecomm
进入System进程TelecomService是属于系统框架向外提供的服务,随着系统启动而启动,并由TelecomManager向外提供访问接。在TelecomSystem第一次绑定时(系统初始化的时候会进行绑定,并不是在打电话的时候才绑定),TelecomSystem会被初始化期间构建一些列必须资源,包括Phone和CI(和底层沟通的对象)的构建
TelecomService.java
@Override
public IBinder onBind(Intent intent) {
Log.d(this, "onBind");
initializeTelecomSystem(this);//系统初始化的时候就会进行绑定,并不是在打电话的时候才绑定),TelecomSystem 会被初始化,期间构建一些列必须资源,包括Phone和CI(和底层沟通的对象)的构建
synchronized (getTelecomSystem().getLock()) {
return getTelecomSystem().getTelecomServiceImpl().getBinder();
}
}
static void initializeTelecomSystem(Context context) {
if (TelecomSystem.getInstance() == null) {
NotificationChannelManager notificationChannelManager =
new NotificationChannelManager();
notificationChannelManager.createChannels(context);
boolean shouldPauseBetweenRingtoneRepeat = context.getResources().getBoolean(
R.bool.should_pause_between_ringtone_repeats);
// TelecomSystem被初始化 构造都是在打电话前执行的
TelecomSystem.setInstance(
new TelecomSystem(
context,
new MissedCallNotifierImpl.MissedCallNotifierImplFactory() {
@Override
public MissedCallNotifierImpl makeMissedCallNotifierImpl(
Context context,
PhoneAccountRegistrar phoneAccountRegistrar,
DefaultDialerCache defaultDialerCache) {
return new MissedCallNotifierImpl(context,
phoneAccountRegistrar, defaultDialerCache);
}
},
.....
.......
........
}
if (BluetoothAdapter.getDefaultAdapter() != null) {
context.startService(new Intent(context, BluetoothPhoneService.class));
}
}
TelecomSystem 系统进程中,通话模块的核心
TelecomSystem 构造函数
public TelecomSystem(
Context context,
....
.....
......) {
mContext = context.getApplicationContext();
......
.....
// 构建CallsManager
mCallsManager = new CallsManager(
mContext,
mLock,
mContactsAsyncHelper,
......
........
..........);
mIncomingCallNotifier = incomingCallNotifier;
incomingCallNotifier.setCallsManagerProxy(......)
mCallsManager.setIncomingCallNotifier(mIncomingCallNotifier);
mRespondViaSmsManager = new RespondViaSmsManager(mCallsManager, mLock);
mCallsManager.setRespondViaSmsManager(mRespondViaSmsManager);
//注册广播
mContext.registerReceiver(mUserSwitchedReceiver, USER_SWITCHED_FILTER);
mContext.registerReceiver(mUserStartingReceiver, USER_STARTING_FILTER);
mContext.registerReceiver(mBootCompletedReceiver, BOOT_COMPLETE_FILTER);
mBluetoothPhoneServiceImpl = bluetoothPhoneServiceImplFactory.makeBluetoothPhoneServiceImpl(
mContext, mLock, mCallsManager, mPhoneAccountRegistrar);
// 构建CallIntentProcessor, 执行Intent的请求
mCallIntentProcessor = new CallIntentProcessor(mContext, mCallsManager);
mTelecomBroadcastIntentProcessor = new TelecomBroadcastIntentProcessor(
mContext, mCallsManager);
// Register the receiver for the dialer secret codes, used to enable extended logging.
mDialerCodeReceiver = new DialerCodeReceiver(mCallsManager);
mContext.registerReceiver(mDialerCodeReceiver, DIALER_SECRET_CODE_FILTER,
Manifest.permission.CONTROL_INCALL_EXPERIENCE, null);
// 构建TelecomServiceImpl,这个是TelecomSystem的真正实现,返回的binder由这里提供,
// 外部对TelecomService的操作实际上都是在TelecomServiceImpl里面进行的。
mTelecomServiceImpl = new TelecomServiceImpl(
mContext, mCallsManager, mPhoneAccountRegistrar,
new CallIntentProcessor.AdapterImpl(),
new UserCallIntentProcessorFactory() {
@Override
public UserCallIntentProcessor create(Context context, UserHandle userHandle) {
return new UserCallIntentProcessor(context, userHandle);
}
},
defaultDialerCache,
new TelecomServiceImpl.SubscriptionManagerAdapterImpl(),
mLock);
Log.endSession();
}
上面的构造都是在打电话之前执行的,下面继续回到调起InCallUi流程
上面placeCall() 方法会进入到 TelecomServiceImpl.java中ITelecomService.Stub中的placeCall()方法,ITelecomService.Stub是一提供一个拨号入口。
TelecomServiceImpl.java
@Override
public void placeCall(Uri handle, Bundle extras, String callingPackage) {
try {
Log.startSession("TSI.pC");
enforceCallingPackage(callingPackage);//权限检查,传入对应的调用位置(调用位置org.codeaurora.dialer)
PhoneAccountHandle phoneAccountHandle = null;
if (extras != null) {
phoneAccountHandle = extras.getParcelable(
TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
if (extras.containsKey(TelecomManager.EXTRA_IS_HANDOVER)) {
// This extra is for Telecom use only so should never be passed in.
extras.remove(TelecomManager.EXTRA_IS_HANDOVER);
}
}
boolean isSelfManaged = phoneAccountHandle != null &&
isSelfManagedConnectionService(phoneAccountHandle);
if (isSelfManaged) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_OWN_CALLS,
"Self-managed ConnectionServices require MANAGE_OWN_CALLS permission.");
if (!callingPackage.equals(
phoneAccountHandle.getComponentName().getPackageName())
&& !canCallPhone(callingPackage,
"CALL_PHONE permission required to place calls.")) {
throw new SecurityException("Self-managed ConnectionServices can only "
+ "place calls through their own ConnectionService.");
}
} else if (!canCallPhone(callingPackage, "placeCall")) {
throw new SecurityException("Package " + callingPackage
+ " is not allowed to place phone calls");
}
final boolean hasCallAppOp = mAppOpsManager.noteOp(AppOpsManager.OP_CALL_PHONE,
Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED;
final boolean hasCallPermission = mContext.checkCallingPermission(CALL_PHONE) ==
PackageManager.PERMISSION_GRANTED;
final boolean hasCallPrivilegedPermission = mContext.checkCallingPermission(
CALL_PRIVILEGED) == PackageManager.PERMISSION_GRANTED;
synchronized (mLock) {
final UserHandle userHandle = Binder.getCallingUserHandle();
long token = Binder.clearCallingIdentity();
try {
final Intent intent = new Intent(hasCallPrivilegedPermission ?
Intent.ACTION_CALL_PRIVILEGED : Intent.ACTION_CALL, handle);
if (extras != null) {
extras.setDefusable(true);
intent.putExtras(extras);
}
// 核心,主过程
mUserCallIntentProcessorFactory.create(mContext, userHandle)
.processIntent(
intent, callingPackage, isSelfManaged ||
(hasCallAppOp && hasCallPermission),
true /* isLocalInvocation */);
} finally {
Binder.restoreCallingIdentity(token);
}
}
} finally {
Log.endSession();
}
}
接下来UserCallIntentProcessor.java主要处理一些 内部校验一些拨号的权限,以及其它操作权限看看是不是需要弹框拒绝。比如 《只允许打紧急电话,没有电话许可,此应用程序不能发出呼叫》
UserCallIntentProcessor.java
public void processIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency, boolean isLocalInvocation) {
// 确保无法调用的设备上没有处理调用意图。
if (!isVoiceCapable()) {
return;
}
String action = intent.getAction();
if (Intent.ACTION_CALL.equals(action) ||
Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
Intent.ACTION_CALL_EMERGENCY.equals(action)) {
processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency,
isLocalInvocation); //调用processOutgoingCallIntent()方法紧接着执行
}
}
// 核心,主过程 内部校验一些拨号的权限,以及其它操作权限看看是不是需要弹框拒绝。比如 《只允许打紧急电话,没有电话许可,此应用程序不能发出呼叫》
private void processOutgoingCallIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency, boolean isLocalInvocation) {
Uri handle = intent.getData();
String scheme = handle.getScheme();
String uriString = handle.getSchemeSpecificPart();
// 确保使用TEL方案拨打的sip uri转换为sip方案。
if (PhoneAccount.SCHEME_TEL.equals(scheme) && PhoneNumberUtils.isUriNumber(uriString)) {
handle = Uri.fromParts(PhoneAccount.SCHEME_SIP, uriString, null);
}
// number into the personal dialer.检查DISALLOW_OUTGOING_CALLS限制。注意:我们在托管配置文件用户中跳过此检查,因为总是可以通过复制和粘贴电话号码到个人拨号器来绕过此检查。
if (!UserUtil.isManagedProfile(mContext, mUserHandle)) {
// Only emergency calls are allowed for users with the DISALLOW_OUTGOING_CALLS
// restriction. 只有使用DISALLOW_OUTGOING_CALLS限制的用户才允许使用紧急呼叫。
if (!TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
final UserManager userManager = (UserManager) mContext.getSystemService(
Context.USER_SERVICE);
if (userManager.hasBaseUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
mUserHandle)) {
showErrorDialogForRestrictedOutgoingCall(mContext,
R.string.outgoing_call_not_allowed_user_restriction);
Log.w(this, "Rejecting non-emergency phone call due to DISALLOW_OUTGOING_CALLS "
+ "restriction");
return;
} else if (userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
mUserHandle)) {
final DevicePolicyManager dpm =
mContext.getSystemService(DevicePolicyManager.class);
if (dpm == null) {
return;
}
final Intent adminSupportIntent = dpm.createAdminSupportIntent(
UserManager.DISALLOW_OUTGOING_CALLS);
if (adminSupportIntent != null) {
mContext.startActivity(adminSupportIntent);
}
return;
}
}
}
if (!canCallNonEmergency && !TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
showErrorDialogForRestrictedOutgoingCall(mContext,
R.string.outgoing_call_not_allowed_no_permission);
Log.w(this, "Rejecting non-emergency phone call because "
+ android.Manifest.permission.CALL_PHONE + " permission is not granted.");
return;
}
int videoState = intent.getIntExtra(
TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
VideoProfile.STATE_AUDIO_ONLY);
Log.d(this, "processOutgoingCallIntent videoState = " + videoState);
intent.putExtra(CallIntentProcessor.KEY_IS_PRIVILEGED_DIALER,
isDefaultOrSystemDialer(callingPackageName));
// Save the user handle of current user before forwarding the intent to primary user.在将意图转发给主用户之前,保存当前用户的用户handle。
intent.putExtra(CallIntentProcessor.KEY_INITIATING_USER, mUserHandle);
// 核心,主过程 //检查一些权限后,执行那个下面代码发送广播
sendIntentToDestination(intent, isLocalInvocation);
}
/**
* rebroadcasting it. 潜在地将意图传递给仅作为主要用户运行的广播接收器。如果调用方是电信服务的本地调用方,我们将发送意图给电信,而不进行重播。
*/
private boolean sendIntentToDestination(Intent intent, boolean isLocalInvocation) {
intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);
intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.setClass(mContext, PrimaryCallReceiver.class); // //开启广播 最后进入到 PrimaryCallReceiver
if (isLocalInvocation) {
Log.i(this, "sendIntentToDestination: send intent to Telecom directly.");
synchronized (TelecomSystem.getInstance().getLock()) {
TelecomSystem.getInstance().getCallIntentProcessor().processIntent(intent);
}
} else {
Log.i(this, "sendIntentToDestination: trampoline to Telecom.");
mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM); //发送广播
}
return true;
}
接下来进入关播PrimaryCallReceiver中 ,PrimaryCallReceiver作为拨号的单一入口,其收到消息后就转给TelecomSystem中的CallIntentProcessor进行处理
PrimartCallReceiver.java
@Override
public void onReceive(Context context, Intent intent) {//作为拨号的单一入口, 其收到消息后,就转发给 TelecomSystem的
Log.startSession("PCR.oR");
synchronized (getTelecomSystem().getLock()) {
getTelecomSystem().getCallIntentProcessor().processIntent(intent);//调用所有呼出和呼入电话的单一入口
}
Log.endSession();
}
CallIntentProcessor.java
public void processIntent(Intent intent) {
final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false);
Log.i(this, "onReceive - isUnknownCall: %s", isUnknownCall);
Trace.beginSection("processNewCallCallIntent");
if (isUnknownCall) {//判断是否为空号码
processUnknownCallIntent(mCallsManager, intent);//如果是一个空号码执行这一步
} else {
processOutgoingCallIntent(mContext, mCallsManager, intent);//否则执行这一步
}
Trace.endSection();
}
static void processOutgoingCallIntent(
Context context,
CallsManager callsManager,
Intent intent) {
Uri handle = intent.getData();
String scheme = handle.getScheme();
String uriString = handle.getSchemeSpecificPart();
boolean isSkipSchemaParsing = intent.getBooleanExtra(
TelephonyProperties.EXTRA_SKIP_SCHEMA_PARSING, false);
Log.d(CallIntentProcessor.class, "isSkipSchemaParsing = " + isSkipSchemaParsing);
if (PhoneAccount.SCHEME_TEL.equals(scheme) && PhoneNumberUtils.isUriNumber(uriString) && !isSkipSchemaParsing) {
handle = Uri.fromParts(PhoneAccount.SCHEME_SIP, uriString, null);
}
PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
Bundle clientExtras = null;
if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) {
clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
}
if (clientExtras == null) {
clientExtras = new Bundle();
}
if (isSkipSchemaParsing) {
clientExtras.putBoolean(TelephonyProperties.EXTRA_SKIP_SCHEMA_PARSING,
isSkipSchemaParsing);
handle = Uri.fromParts(PhoneAccount.SCHEME_TEL, handle.toString(), null);
}
boolean isConferenceUri = intent.getBooleanExtra(
TelephonyProperties.EXTRA_DIAL_CONFERENCE_URI, false);
Log.d(CallIntentProcessor.class, "isConferenceUri = "+isConferenceUri);
if (isConferenceUri) {
clientExtras.putBoolean(TelephonyProperties.EXTRA_DIAL_CONFERENCE_URI, isConferenceUri);
}
boolean isAddParticipant = intent.getBooleanExtra(
TelephonyProperties.ADD_PARTICIPANT_KEY, false);
Log.d(CallIntentProcessor.class, "isAddparticipant = "+isAddParticipant);
if (isAddParticipant) {
clientExtras.putBoolean(TelephonyProperties.ADD_PARTICIPANT_KEY, isAddParticipant);
}
if (intent.hasExtra(TelecomManager.EXTRA_CALL_SUBJECT)) {
String callsubject = intent.getStringExtra(TelecomManager.EXTRA_CALL_SUBJECT);
clientExtras.putString(TelecomManager.EXTRA_CALL_SUBJECT, callsubject);
}
final int callDomain = intent.getIntExtra(
QtiCallConstants.EXTRA_CALL_DOMAIN, QtiCallConstants.DOMAIN_AUTOMATIC);
Log.d(CallIntentProcessor.class, "callDomain = " + callDomain);
clientExtras.putInt(QtiCallConstants.EXTRA_CALL_DOMAIN, callDomain);
final int videoState = intent.getIntExtra( TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
VideoProfile.STATE_AUDIO_ONLY);
clientExtras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
if (!callsManager.isSelfManaged(phoneAccountHandle,
(UserHandle) intent.getParcelableExtra(KEY_INITIATING_USER))) {
boolean fixedInitiatingUser = fixInitiatingUserIfNecessary(context, intent);
// Show the toast to warn user that it is a personal call though initiated in work
// profile.
if (fixedInitiatingUser) {
Toast.makeText(context, R.string.toast_personal_call_msg, Toast.LENGTH_LONG).show();
}
} else {
Log.i(CallIntentProcessor.class,
"processOutgoingCallIntent: skip initiating user check");
}
Log.d(CallIntentProcessor.class, " processOutgoingCallIntent handle = " + handle
+ ", scheme = " + scheme + ", uriString = " + uriString
+ ", isSkipSchemaParsing = " + isSkipSchemaParsing
+ ", isAddParticipant = " + isAddParticipant);
UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER);
/**
* description 帐号选择
*/
/**start*/
TelecomManager telecomManager =
(TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
List<PhoneAccountHandle> mSubscriptionAccountHandles = telecomManager.getCallCapablePhoneAccounts();
boolean isDualCard = (mSubscriptionAccountHandles != null && mSubscriptionAccountHandles.size() > 1);
if(isDualCard) {
String phoneNumber = intent.getData().getSchemeSpecificPart();
phoneNumber = PhoneNumberUtils.stripSeparators(phoneNumber);
int simGroup = SimSelectDialog.getCardByNumber(context, phoneNumber) - 1;
if (simGroup >= 0) {
if (phoneAccountHandle == null) {
phoneAccountHandle = telecomManager.getUserSelectedOutgoingPhoneAccount();
}
if (phoneAccountHandle != null) {
if (!phoneAccountHandle.equals(mSubscriptionAccountHandles.get(simGroup))) {
......
}
/**end*/
// 创建一个call.startOutgoingCall 新建一个(或者重用) call, 将CallsManager绑定监听该 call, 然后通知 callsManager 的监听者, 添加了一个 call
// Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
Call call = callsManager
.startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser,
intent);
//这是一个断点,因为下面将会详细地深入分析 callsManager.startOutgoingCall 的流程,下面分析 startOutgoingCall 完成后, 再从 NewOutgoingCallIntentBroadcaster.processIntent()往下分析
if (call != null) {
sendNewOutgoingCallIntent(context, call, callsManager, intent);//拿到上面创建的call, 进行下一步
}
}
这里重点看callsManager.startOutgoingCall(......);这里就是调起InCallUi的入口,startOutgoingCall新建一个(或者重用)call,将CallManager绑定监听该call,然后通知callsManager的监听,添加了一个call
CallManager.java
public Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras,
UserHandle initiatingUser, Intent originalIntent) {
boolean isReusedCall = true;
Call call = reuseOutgoingCall(handle);
PhoneAccount account =
mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser);
boolean isSelfManaged = account != null && account.isSelfManaged();
if (call == null) {
call = new Call(getNextCallId(), mContext,
this,
mLock,
mConnectionServiceRepository,
mContactsAsyncHelper,
mCallerInfoAsyncQueryFactory,
mPhoneNumberUtilsAdapter,
handle,
null /* gatewayInfo */,
null /* connectionManagerPhoneAccount */,
null /* phoneAccountHandle */,
Call.CALL_DIRECTION_OUTGOING /* callDirection */,
false /* forceAttachToExistingConnection */,
false, /* isConference */
mClockProxy);
if ((extras != null) &&
extras.getBoolean(TelephonyProperties.EXTRA_DIAL_CONFERENCE_URI, false)) {
//Reset PostDialDigits with empty string for ConfURI call.
call.setPostDialDigits("");
}
call.initAnalytics();
call.setIsSelfManaged(isSelfManaged);
if (isSelfManaged) {
// Self-managed calls will ALWAYS use voip audio mode.
call.setIsVoipAudioMode(true);
}
call.setInitiatingUser(initiatingUser);
isReusedCall = false;
}
int videoState = VideoProfile.STATE_AUDIO_ONLY;
if (extras != null) {
videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
VideoProfile.STATE_AUDIO_ONLY);
if (VideoProfile.isVideo(videoState)) {
if (call.isEmergencyCall() && account != null &&
!account.hasCapabilities(PhoneAccount.CAPABILITY_EMERGENCY_VIDEO_CALLING)) {
Log.i(this, "startOutgoingCall - emergency video calls not supported; " +
"falling back to audio-only");
videoState = VideoProfile.STATE_AUDIO_ONLY;
} else if (account != null &&
!account.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING)) {
videoState = VideoProfile.STATE_AUDIO_ONLY;
}
}
call.setVideoState(videoState);
}
boolean isAddParticipant = ((extras != null) && (extras.getBoolean(
TelephonyProperties.ADD_PARTICIPANT_KEY, false)));
boolean isSkipSchemaOrConfUri = ((extras != null) && (extras.getBoolean(
TelephonyProperties.EXTRA_SKIP_SCHEMA_PARSING, false) ||
extras.getBoolean(TelephonyProperties.EXTRA_DIAL_CONFERENCE_URI, false)));
if (isAddParticipant) {
String number = handle.getSchemeSpecificPart();
if (!isSkipSchemaOrConfUri) {
number = PhoneNumberUtils.stripSeparators(number);
}
addParticipant(number);
mInCallController.bringToForeground(false);
return null;
}
// Force tel scheme for ims conf uri/skip schema calls to avoid selection of sip accounts
String scheme = (isSkipSchemaOrConfUri? PhoneAccount.SCHEME_TEL: handle.getScheme());
Log.d(this, "startOutgoingCall :: isAddParticipant=" + isAddParticipant
+ " isSkipSchemaOrConfUri=" + isSkipSchemaOrConfUri + " scheme=" + scheme);
List<PhoneAccountHandle> potentialPhoneAccounts = findOutgoingCallPhoneAccount(
phoneAccountHandle, handle, VideoProfile.isVideo(videoState),
initiatingUser, scheme);
if (potentialPhoneAccounts.size() == 1) {
phoneAccountHandle = potentialPhoneAccounts.get(0);
} else {
phoneAccountHandle = null;
}
call.setTargetPhoneAccount(phoneAccountHandle);
boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle) && !isSelfManaged;
if (!isPotentialInCallMMICode && (!isReusedCall
&& !makeRoomForOutgoingCall(call, call.isEmergencyCall()))) {
Call foregroundCall = getForegroundCall();
Log.d(this, "No more room for outgoing call %s ", call);
if (foregroundCall.isSelfManaged()) {
//如果正在进行的调用是一个自管理的调用,则提示用户询问是否要断开正在进行的调用并将传出的调用放置。
call.setOriginalCallIntent(originalIntent);
startCallConfirmation(call);
} else {
// 如果正在进行的呼叫是托管呼叫,我们将阻止传出呼叫拨号
notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call);
}
return null;
}
// The outgoing call can be placed, go forward.
boolean needsAccountSelection =
phoneAccountHandle == null && potentialPhoneAccounts.size() > 1
&& !call.isEmergencyCall() && !isSelfManaged;
if (needsAccountSelection) {
call.setState(CallState.SELECT_PHONE_ACCOUNT, "needs account selection");
extras = new Bundle(extras);
extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS,
potentialPhoneAccounts);
} else {
PhoneAccount accountToUse =
mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser);
if (accountToUse != null && accountToUse.getExtras() != null) {
if (accountToUse.getExtras()
.getBoolean(PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE)) {
Log.d(this, "startOutgoingCall: defaulting to voip mode for call %s",
call.getId());
call.setIsVoipAudioMode(true);
}
}
call.setState(
CallState.CONNECTING,
phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString());
boolean isVoicemail = (call.getHandle() != null)
&& (PhoneAccount.SCHEME_VOICEMAIL.equals(call.getHandle().getScheme())
|| (accountToUse != null && mPhoneAccountRegistrar.isVoiceMailNumber(
accountToUse.getAccountHandle(), call.getHandle().getSchemeSpecificPart())));
if (!isVoicemail && (isRttSettingOn() || (extras != null
&& extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)))) {
if (accountToUse != null
&& accountToUse.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
call.createRttStreams();
}
call.setRequestedToStartWithRtt();
}
}
setIntentExtrasAndStartTime(call, extras);
// 如果是MMI 号码
if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) {
//让CallsManager监听call的行为
call.addListener(this);
} else if (!mCalls.contains(call) && mPendingMOEmerCall == null) {
确保Call不会重复添加(getNewOutgoingCall有重用机制).添加call, 然后callsManager通知监听者,添加了一个call
addCall(call);
}
return call;
}
addCall添加call,然后callsManager通知监听者,添加一个call
public void addCall(Call call) {
Trace.beginSection("addCall");
Log.v(this, "addCall(%s)", call);
call.addListener(this);// CallsManager 监听 call
mCalls.add(call); // 添加
// Specifies the time telecom finished routing the call. This is used by the dialer for
// analytics.
Bundle extras = call.getIntentExtras();
extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS,
SystemClock.elapsedRealtime());
updateCanAddCall();
// onCallAdded for calls which immediately take the foreground (like the first call).onCallAdded用于立即占据前台的调用(如第一次调用)
for (CallsManagerListener listener : mListeners) {// 告诉所有callsManager的监听者
if (LogUtils.SYSTRACE_DEBUG) {
Trace.beginSection(listener.getClass().toString() + " addCall");
}
listener.onCallAdded(call); //进入监听InCallControoler中
if (LogUtils.SYSTRACE_DEBUG) {
Trace.endSection();
}
}
Trace.endSection();
}
接下来需要查看mListeners添加了那些监听,到底是通知了谁,需要查看callsManager中构造构造过程
CallManager的构造函数
/**
* Initializes the required Telecom components.
*/
@VisibleForTesting
public CallsManager(
Context context,
TelecomSystem.SyncRoot lock,
.....
......) {
mContext = context;
mLock = lock;
.....
........
............
mDtmfLocalTonePlayer =
new DtmfLocalTonePlayer(new DtmfLocalTonePlayer.ToneGeneratorProxy());
CallAudioRouteStateMachine callAudioRouteStateMachine = new CallAudioRouteStateMachine(
context,
this,
bluetoothManager,
wiredHeadsetManager,
statusBarNotifier,
audioServiceFactory,
CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT
);
mConnectionSvrFocusMgr = connectionServiceFocusManagerFactory.create(
mRequester, Looper.getMainLooper());
mHeadsetMediaButton = headsetMediaButtonFactory.create(context, this, mLock);
mTtyManager = new TtyManager(context, mWiredHeadsetManager);
mProximitySensorManager = proximitySensorManagerFactory.create(context, this);
mPhoneStateBroadcaster = new PhoneStateBroadcaster(this);
mCallLogManager = new CallLogManager(context, phoneAccountRegistrar, mMissedCallNotifier);
mConnectionServiceRepository =
new ConnectionServiceRepository(mPhoneAccountRegistrar, mContext, mLock, this);
mInCallWakeLockController = inCallWakeLockControllerFactory.create(context, this);
mClockProxy = clockProxy;
// 在CallsManager构造的时候, 会给CallsManager添加一系列监听者, 当CallsManager变化时,会通知他们
mListeners.add(mInCallWakeLockController);
mListeners.add(statusBarNotifier);
mListeners.add(mCallLogManager);
mListeners.add(mPhoneStateBroadcaster);
//因为目前关注的是InCallController,因为InCallController监听CallsManager,收到来自CallsManager的消息后,
// 跨进程回到最初的com.android.dialer进程,通知UI发生变化,也就是说InCallController是system进程 主动沟通com.android.dialer进程的桥梁。
mListeners.add(mInCallController); // 重点关注 InCallController liwangjiang
mListeners.add(mCallAudioManager);
mListeners.add(mCallRecordingTonePlayer);
mListeners.add(missedCallNotifier);
mListeners.add(mHeadsetMediaButton);
mListeners.add(mProximitySensorManager);
// There is no USER_SWITCHED broadcast for user 0, handle it here explicitly.
final UserManager userManager = UserManager.get(mContext);
// Don't load missed call if it is run in split user model.
if (userManager.isPrimaryUser()) {
onUserSwitch(Process.myUserHandle());
}
// Register BroadcastReceiver to handle enhanced call blocking feature related event.
IntentFilter intentFilter = new IntentFilter(
CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
intentFilter.addAction(SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED);
context.registerReceiver(mReceiver, intentFilter);
QtiCarrierConfigHelper.getInstance().setup(mContext);
}
目前需要关注的是InCallController,因为InCallControll监听CallsManager,收到来自CallsManager的消息后,跨进程回到最初的com.android.dialer进程,通知UI发生变化,也就是说InCallContrller是system进程主动沟通com.android.dialer进程的桥梁,下面详细解释InCallContoller是如何沟通com.android.dialer进程的。
InCallController.java
我们首先看一下构造函数
InCallController几个内部内的意思:
CarSwappingInCallServiceConnection : 当这两种情况之一发生时,该类实例将接管连接。个版本,他在两个独立子类实例之间切换UI
EmergencyInCallServiceConnection : InCallServiceBindingConnection的一个版本,它将所有调用委托给一个辅助连接(CarSwappingInCallServiceConnection),直到它发现一个紧急调用,或者另一个连接终止。当这两种情况之一发生时,该类实例将接管连接。
InCallServiceBindingConnection 最后调用的类 他继承了 InCallServiceConnection
//构造器主要初始化是在TelecomSystem中进行初始化
public InCallController(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager,
SystemStateProvider systemStateProvider,
DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter,
EmergencyCallHelper emergencyCallHelper) {
mContext = context;
mLock = lock;
mCallsManager = callsManager;
mSystemStateProvider = systemStateProvider;
mTimeoutsAdapter = timeoutsAdapter;
mDefaultDialerCache = defaultDialerCache;
mEmergencyCallHelper = emergencyCallHelper;
Resources resources = mContext.getResources();
mSystemInCallComponentName = new ComponentName( // 这个服务组件时用来控制电话APP的UI的 pag包名 cls类名
resources.getString(R.string.ui_default_package),//pkg com.android.dialer
resources.getString(R.string.incall_default_class));//cls com.android.incallui.InCallServiceImpl
mSystemStateProvider.addListener(mSystemStateListener);
}
接下来就是监听的位置了 listener.onCallAdded(call); //进入监听InCallControoler中 InCallControoler是继承CallsManagerListenerBase对象 listener就是CallsManagerListenerBase接口
onCallAdded(call) 方法
if (!isBoundAndConnectedToServices()) {
Log.i(this, "onCallAdded: %s; not bound or connected.", call);
// 第一次触发时,服务没有绑定,所以需要先绑定服务
bindToServices(call);
} else {
.....
.......
}
}
bindToServices中主要看if中的 mInCallServiceConnection.connect(call)进入
public void bindToServices(Call call) {
if (mInCallServiceConnection == null) {
InCallServiceConnection dialerInCall = null;
InCallServiceInfo defaultDialerComponentInfo = getDefaultDialerComponent();
Log.i(this, "defaultDialer: " + defaultDialerComponentInfo);
if (defaultDialerComponentInfo != null &&
!defaultDialerComponentInfo.getComponentName().equals(
mSystemInCallComponentName)) {
dialerInCall = new InCallServiceBindingConnection(defaultDialerComponentInfo);
}
Log.i(this, "defaultDialer: " + dialerInCall);
InCallServiceInfo systemInCallInfo = getInCallServiceComponent(
mSystemInCallComponentName, IN_CALL_SERVICE_TYPE_SYSTEM_UI);
EmergencyInCallServiceConnection systemInCall =
new EmergencyInCallServiceConnection(systemInCallInfo, dialerInCall);
systemInCall.setHasEmergency(mCallsManager.hasEmergencyCall());
InCallServiceConnection carModeInCall = null;
InCallServiceInfo carModeComponentInfo = getCarModeComponent();
if (carModeComponentInfo != null &&
!carModeComponentInfo.getComponentName().equals(mSystemInCallComponentName)) {
carModeInCall = new InCallServiceBindingConnection(carModeComponentInfo);//初始化连接
}
mInCallServiceConnection =
new CarSwappingInCallServiceConnection(systemInCall, carModeInCall);
}
mInCallServiceConnection.setCarMode(shouldUseCarModeUI());
// 这个对象,他在两个独立的实例中切换UI
if (mInCallServiceConnection.connect(call) ==//mInCallServiceConnection.connect(call)开启调用InCallUi
InCallServiceConnection.CONNECTION_SUCCEEDED) {
//只有当我们实际连接到主UI InCallServices时,才连接到非UI InCallServices。
connectToNonUiInCallServices(call);
} else {
Log.i(this, "bindToServices: current UI doesn't support call; not binding.");
}
}
mInCallServiceConnection.connect(call)进入了CartSwapingInCallServiceConnection(内部类)对象中
private class CarSwappingInCallServiceConnection extends InCallServiceConnection {
@Override
public int connect(Call call) {
if (mIsConnected) {
Log.i(this, "already connected");
return CONNECTION_SUCCEEDED;
} else {
int result = mCurrentConnection.connect(call); //InCallUi调用 调到EmergencyInCallServiceConnection内部类中
if (result != CONNECTION_FAILED) {
mIsConnected = result == CONNECTION_SUCCEEDED;
return result;
}
}
return CONNECTION_FAILED;
}
}
mCurrentConnection.connect(call); //InCallUi调用 调到EmergencyInCallServiceConnection内部类中connect函数
/**
*InCallServiceBindingConnection的一个版本,它将所有调用委托给一个辅助连接,
*直到它发现一个紧急调用,或者另一个连接终止。当这两种情况之一发生时,该类实例将接管连接。
*/
private class EmergencyInCallServiceConnection extends InCallServiceBindingConnection {
@Override
public int connect(Call call) {
mIsConnected = true;
if (mIsProxying) {
int result = mSubConnection.connect(call);//InCallUi调用
mIsConnected = result == CONNECTION_SUCCEEDED;
if (result != CONNECTION_FAILED) {
return result;
}
// Could not connect to child, stop proxying.
mIsProxying = false;
}
mEmergencyCallHelper.maybeGrantTemporaryLocationPermission(call,
mCallsManager.getCurrentUserHandle());
if (call != null && call.isIncoming()
&& mEmergencyCallHelper.getLastEmergencyCallTimeMillis() > 0) {
// Add the last emergency call time to the call
Bundle extras = new Bundle();
extras.putLong(android.telecom.Call.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS,
mEmergencyCallHelper.getLastEmergencyCallTimeMillis());
call.putExtras(Call.SOURCE_CONNECTION_SERVICE, extras);
}
//调用这里,连接父接口中的connect()方法。
return super.connect(call);
}
}
EmergencyInCallServiceConnection继承了InCallServiceBindingConnection 所以调用super.connect就是调用了InCallServiceBindingConnection里面的 connect()方法
InCallServiceBindingConnection中的connect方法
private class InCallServiceBindingConnection extends InCallServiceConnection {
@Override
public int connect(Call call) {
if (mIsConnected) {
Log.addEvent(call, LogUtils.Events.INFO, "Already connected, ignoring request.");
return CONNECTION_SUCCEEDED;
}
if (call != null && call.isSelfManaged() &&
!mInCallServiceInfo.isSelfManagedCallsSupported()) {
Log.i(this, "Skipping binding to %s - doesn't support self-mgd calls",
mInCallServiceInfo);
mIsConnected = false;
return CONNECTION_NOT_SUPPORTED;
}
//开启InCallService
Intent intent = new Intent(InCallService.SERVICE_INTERFACE);
intent.setComponent(mInCallServiceInfo.getComponentName());
if (call != null && !call.isIncoming() && !call.isExternalCall()){
intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS,
call.getIntentExtras());
intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
call.getTargetPhoneAccount());
}
Log.i(this, "Attempting to bind to InCall %s, with %s", mInCallServiceInfo, intent);
mIsConnected = true;
if (!mContext.bindServiceAsUser(intent, mServiceConnection, //开启服务 调用InCallServiceImpl
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE |
Context.BIND_ABOVE_CLIENT,
UserHandle.CURRENT)) {
Log.w(this, "Failed to connect.");
mIsConnected = false;
}
if (call != null && mIsConnected) {
call.getAnalytics().addInCallService(
mInCallServiceInfo.getComponentName().flattenToShortString(),
mInCallServiceInfo.getType());
}
return mIsConnected ? CONNECTION_SUCCEEDED : CONNECTION_FAILED;
}
}
这里已经开启服务,开启了Dialer中InCallUi,在InCallUi中有一个服务InCallServiceImpl,这个服务就是开启dialer里面的InCallUi的入口
调用完毕后回调到InCallController中的InCallServiceBindingConnection中的ServiceConnection里面的onServiceConnected()方法 ,
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.startSession("ICSBC.oSC");
synchronized (mLock) {
try {
Log.d(this, "onServiceConnected: %s %b %b", name, mIsBound, mIsConnected);
mIsBound = true;
if (mIsConnected) {
// 成功后调用InCallServer ,连接成功往下走
onConnected(service);
}
} finally {
Log.endSession();
}
}
}
绑定与InCallUi数据传输方法
protected void onConnected(IBinder service) {
boolean shouldRemainConnected =
InCallController.this.onConnected(mInCallServiceInfo, service);//往下执行
if (!shouldRemainConnected) {
// Sometimes we can opt to disconnect for certain reasons, like if the
// InCallService rejected our initialization step, or the calls went away
// in the time it took us to bind to the InCallService. In such cases, we go
// ahead and disconnect ourselves.
disconnect();
}
}
private boolean onConnected(InCallServiceInfo info, IBinder service) {
Trace.beginSection("onConnected: " + info.getComponentName());
Log.i(this, "onConnected to %s", info.getComponentName());
IInCallService inCallService = IInCallService.Stub.asInterface(service);
mInCallServices.put(info, inCallService);
try {
// 让绑定的服务,可以通过 InCallAdapter 跨进程 访问 system 进程 (因为当前就是在系统进程)
// InCallAdapter 是一个 Binder。
// 需要对Android 通过 Binder实现跨进程调用有一定了解
// 还有对Android 的AIDL由一定了解
inCallService.setInCallAdapter(
new InCallAdapter(
mCallsManager,
mCallIdMapper,
mLock,
info.getComponentName().getPackageName()));
} catch (RemoteException e) {
Log.e(this, e, "Failed to set the in-call adapter.");
Trace.endSection();
return false;
}
// Upon successful connection, send the state of the world to the service.
List<Call> calls = orderCallsWithChildrenFirst(mCallsManager.getCalls());
Log.i(this, "Adding %s calls to InCallService after onConnected: %s, including external " +
"calls", calls.size(), info.getComponentName());
int numCallsSent = 0;
for (Call call : calls) {
try {
if ((call.isSelfManaged() && !info.isSelfManagedCallsSupported()) ||
(call.isExternalCall() && !info.isExternalCallsSupported())) {
continue;
}
// Only send the RTT call if it's a UI in-call service
boolean includeRttCall = info.equals(mInCallServiceConnection.getInfo());
// Track the call if we don't already know about it.
addCall(call);
numCallsSent += 1;
inCallService.addCall(ParcelableCallUtils.toParcelableCall(
call,
true /* includeVideoProvider */,
mCallsManager.getPhoneAccountRegistrar(),
info.isExternalCallsSupported(),
includeRttCall));
} catch (RemoteException ignored) {
}
}
try {
inCallService.onCallAudioStateChanged(mCallsManager.getAudioState());
inCallService.onCanAddCallChanged(mCallsManager.canAddCall());
} catch (RemoteException ignored) {
}
Log.i(this, "%s calls sent to InCallService.", numCallsSent);
Trace.endSection();
return true;
}
InCallController 内部类的逻辑 第一先是调用 CarSwappingInCallServiceConnection该内部类的作用是管理连接其他两个子类(EmergencyInCallServiceConnection ,InCallServiceBindingConnection)之间互相切换,
交互图:
com.android.dialer进程
InCallServiceImpl InCallService的实现类,负责和Telecom沟通,更新
InCallServiceImp的核心是在的他父类InCallService,InCallServiceImpl绑定时返回一个InCallServiceBinder,因此system进程拿到的接口就到的接口就是这个,绑定之前会初始化InCallPresenter,InCallPresenter是全局唯一(单例)的,他负责更新APP的UI
InCallServiceImp.java
@Override
public IBinder onBind(Intent intent) {
final Context context = getApplicationContext();
getResources().updateConfiguration(configuration,displayMetrics);
QtiCarrierConfigHelper.getInstance().setup(context);
final ContactInfoCache contactInfoCache = ContactInfoCache.getInstance(context);
InCallPresenter.getInstance()
.setUp(
context,
CallList.getInstance(),
new ExternalCallList(),
new StatusBarNotifier(context, contactInfoCache),
new ExternalCallNotifier(context, contactInfoCache),
contactInfoCache,
new ProximitySensor(
context, AudioModeProvider.getInstance(), new AccelerometerListener(context)),
new FilteredNumberAsyncQueryHandler(context));
InCallPresenter.getInstance().onServiceBind();
// 核心, 主过程
InCallPresenter.getInstance().maybeStartRevealAnimation(intent);
TelecomAdapter.getInstance().setInCallService(this);
InCallWindow.getInstance(context).initInCallWindow();
ImmersionInCallDialogMgr.getInstance().init(context, contactInfoCache);
// 返回一个 InCallServiceBinder
return super.onBind(intent);
}
InCallPresenter MVC模式中,控制逻辑和UI
InCallPreseter的maybeStartRevealAnimatin会启动InCallActivity,即拨号进入中的界面(或者是来电界面)
public void maybeStartRevealAnimation(Intent intent) {
if (intent == null || mInCallActivity != null) {
return;
}
final Bundle extras = intent.getBundleExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
if (extras == null) {
// Incoming call, just show the in-call UI directly.
return;
}
if (extras.containsKey(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS)) {
// Account selection dialog will show up so don't show the animation.
return;
}
final PhoneAccountHandle accountHandle =
intent.getParcelableExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
final Point touchPoint = extras.getParcelable(TouchPointManager.TOUCH_POINT);
InCallPresenter.getInstance().setBoundAndWaitingForOutgoingCall(true, accountHandle);
final Intent activityIntent =
InCallActivity.getIntent(mContext, false, true, false /* forFullScreen */);
activityIntent.putExtra(TouchPointManager.TOUCH_POINT, touchPoint);
mContext.startActivity(activityIntent);//启动InCallActivity
}
Dialer启动图:
调起InCallUi后就是更新InCallUi数据了。
回到刚刚开启服务的位置 mContext.bindServiceAsUser(intent, mServiceConnection,.....) 这里传入了mServiceConnection,InCallServiceImpl连接成功后回调到mServiceConnection,
InCallController.java
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.startSession("ICSBC.oSC");
synchronized (mLock) {
try {
Log.d(this, "onServiceConnected: %s %b %b", name, mIsBound, mIsConnected);
mIsBound = true;
if (mIsConnected) {//是否连接
// 只有当我们被认为是有联系的时候才继续。 成功后调用InCallServer
onConnected(service);
}
} finally {
Log.endSession();
}
}
}
}
protected void onConnected(IBinder service) {
boolean shouldRemainConnected =
InCallController.this.onConnected(mInCallServiceInfo, service);//往下执行
if (!shouldRemainConnected) {
disconnect();
}
}
onConnected这里面设置了很多监听,通过IInCallService调用到了framework/base/telecom InCallService中进行处理.
private boolean onConnected(InCallServiceInfo info, IBinder service) {
Trace.beginSection("onConnected: " + info.getComponentName());
Log.i(this, "onConnected to %s", info.getComponentName());
//进入Framework/base/Telecom
IInCallService inCallService = IInCallService.Stub.asInterface(service);
mInCallServices.put(info, inCallService);
try {
// 让绑定的服务,可以通过 InCallAdapter 跨进程 访问 system 进程 (因为当前就是在系统进程)
// InCallAdapter 是一个 Binder。
// 需要对Android 通过 Binder实现跨进程调用有一定了解
// 还有对Android 的AIDL由一定了解 调用到了Framework/base/telecom InCallServce
inCallService.setInCallAdapter(
new InCallAdapter(
mCallsManager,
mCallIdMapper,
mLock,
info.getComponentName().getPackageName()));
} catch (RemoteException e) {
Log.e(this, e, "Failed to set the in-call adapter.");
Trace.endSection();
return false;
}
// Upon successful connection, send the state of the world to the service.成功连接后,将 world 状态发送到服务。
List<Call> calls = orderCallsWithChildrenFirst(mCallsManager.getCalls());
Log.i(this, "Adding %s calls to InCallService after onConnected: %s, including external " +
"calls", calls.size(), info.getComponentName());
int numCallsSent = 0;
for (Call call : calls) {
try {
if ((call.isSelfManaged() && !info.isSelfManagedCallsSupported()) ||
(call.isExternalCall() && !info.isExternalCallsSupported())) {
continue;
}
// Only send the RTT call if it's a UI in-call service
boolean includeRttCall = info.equals(mInCallServiceConnection.getInfo());
// Track the call if we don't already know about it.
addCall(call);
numCallsSent += 1;
//添加调用
inCallService.addCall(ParcelableCallUtils.toParcelableCall(
call,
true /* includeVideoProvider */,
mCallsManager.getPhoneAccountRegistrar(),
info.isExternalCallsSupported(),
includeRttCall));
} catch (RemoteException ignored) {
}
}
try {
inCallService.onCallAudioStateChanged(mCallsManager.getAudioState());
inCallService.onCanAddCallChanged(mCallsManager.canAddCall());
} catch (RemoteException ignored) {
}
Log.i(this, "%s calls sent to InCallService.", numCallsSent);
Trace.endSection();
return true;
}
我们先看inCallService.setInCallAdapter方法 他进入的是framework/base/telecom InCallService.java ,
// 让绑定的服务,可以通过 InCallAdapter 跨进程 访问 system 进程 (因为当前就是在系统进程)
// InCallAdapter 是一个 Binder。
// 需要对Android 通过 Binder实现跨进程调用有一定了解
// 还有对Android 的AIDL由一定了解 调用到了Framework/base/telecom InCallServce
private final class InCallServiceBinder extends IInCallService.Stub {
@Override
public void setInCallAdapter(IInCallAdapter inCallAdapter) {
mHandler.obtainMessage(MSG_SET_IN_CALL_ADAPTER, inCallAdapter).sendToTarget();
}
@Override
public void addCall(ParcelableCall call) {
mHandler.obtainMessage(MSG_ADD_CALL, call).sendToTarget();
}
@Override
public void updateCall(ParcelableCall call) {
mHandler.obtainMessage(MSG_UPDATE_CALL, call).sendToTarget();
}
忽略......
}
进入Handler
/** Default Handler used to consolidate binder method calls onto a single thread. */
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
if (mPhone == null && msg.what != MSG_SET_IN_CALL_ADAPTER) {
return;
}
switch (msg.what) {
case MSG_SET_IN_CALL_ADAPTER:
String callingPackage = getApplicationContext().getOpPackageName();
// setInCallAdapter 触发创建 com.android.dialer 的唯一 的phone对象
// 也就是说, 在 com.android.dialer 进程的所有操作 都是经过 phone 里面的mInCallAdapter
// 发送到 system 进程的 (框架层)
mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj), callingPackage,
getApplicationContext().getApplicationInfo().targetSdkVersion);
// 核心, 让InCallService 监听 Phone 的状态变化
mPhone.addListener(mPhoneListener);
onPhoneCreated(mPhone);
break;
case MSG_ADD_CALL: //添加Call时调用
mPhone.internalAddCall((ParcelableCall) msg.obj);
break;
case MSG_UPDATE_CALL:
mPhone.internalUpdateCall((ParcelableCall) msg.obj);
break;
case MSG_SET_POST_DIAL_WAIT: {
SomeArgs args = (SomeArgs) msg.obj;
........
忽略大量消息类型
这里mPhone.addListener(mPhoneLintener) 他添加到Phone中等待响应
然后是调用 inCallService.addCall()方法
进入了Handler 中的 case MSG_ADD_CALL:
调用mPhone.InternalAddCall();进入了mPhone对象
Phone.java
final void internalAddCall(ParcelableCall parcelableCall) {
Call call = new Call(this, parcelableCall.getId(), mInCallAdapter,
parcelableCall.getState(), mCallingPackage, mTargetSdkVersion);
mCallByTelecomCallId.put(parcelableCall.getId(), call);
mCalls.add(call);
checkCallTree(parcelableCall);
call.internalUpdate(parcelableCall, mCallByTelecomCallId);
fireCallAdded(call);//进入回调
}
这里只是处理一些Call的处理
private void fireCallAdded(Call call) {
for (Listener listener : mListeners) {
listener.onCallAdded(this, call);
}
}
mListeners是mPhone.addListener(mPhoneLintener)添加进来的,调用onCallAdded() 方法,回调到 InCallService里面的private Phone.Listener mPhoneListener = new Phone.Listener(){ }中
private Phone.Listener mPhoneListener = new Phone.Listener() {
省略其他回调.....
/** ${inheritDoc} */
@Override
public void onCallAdded(Phone phone, Call call) {
InCallService.this.onCallAdded(call); //onCallAded本类里面没有操作,操作子类实现
}
省略其他回调.....
}
这里的InCallServie.this.onCallAdded()方法提供给子类实现 状态更新 传入Call对象
我们在来看一下他的子类 com.android.Dialer进程
InCallServiceImpl.java
@Override
public void onCallAdded(Call call) {
InCallPresenter.getInstance().onCallAdded(call);
}
这里就是更新状态入口
InCallPresenter的作用:从CallList获取更新,并将更改通知InCallActivity (UI)。负责启动新调用的活动,并在所有调用都断开时完成该活动。创建和管理调用内状态,并为希望在调用内状态更改时侦听的演示程序提供侦听器模式。
InCallPresenter.java
public void onCallAdded(final android.telecom.Call call) {
LatencyReport latencyReport = new LatencyReport(call);
if (shouldAttemptBlocking(call)) {
maybeBlockCall(call, latencyReport);
} else {
if (call.getDetails().hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL)) {
mExternalCallList.onCallAdded(call);
} else {
latencyReport.onCallBlockingDone();
mCallList.onCallAdded(mContext, call, latencyReport); //核心主过程
}
}
// Since a call has been added we are no longer waiting for Telecom to send us a call.
由于增加了一个呼叫,我们不再等待电信给我们发送一个呼叫
setBoundAndWaitingForOutgoingCall(false, null);
call.registerCallback(mCallCallback);
}
进入CallList进行call对象的数据解析和加载,
CallList.java
public void onCallAdded(
final Context context, final android.telecom.Call telecomCall, LatencyReport latencyReport) {
省略......
.........
if (call.getState() == DialerCall.State.INCOMING
|| call.getState() == DialerCall.State.CALL_WAITING) {
onIncoming(call); //主核心
} else {
dialerCallListener.onDialerCallUpdate();
}
if (call.getState() != State.INCOMING) {
// Only report outgoing calls
ShortcutUsageReporter.onOutgoingCallAdded(context, call.getNumber());
}
Trace.endSection();
}
private void onIncoming(DialerCall call) {
for (Listener listener : mListeners) {
listener.onIncomingCall(call);//回调进入InCallPresenter
}
}
这里的mListeners是InCallPresenter传入this,InCallPresenter实现了Listener
InCallPresenter.java
/** Called when there is a new incoming call. */
@Override
public void onIncomingCall(DialerCall call) {
InCallState newState = startOrFinishUi(InCallState.INCOMING);
InCallState oldState = mInCallState;
registerFoldAnswer();
mInCallState = newState;
for (IncomingCallListener listener : mIncomingCallListeners) {
listener.onIncomingCall(oldState, mInCallState, call);
}
if (mInCallActivity != null) {
// Re-evaluate which fragment is being shown.
mInCallActivity.onPrimaryCallStateChanged();//更新Activity 状态
}
}
流程图:
接下来我们分析一下Log是怎么调用InCallUi的
//Dialer拨号 Dialer进程
09-17 21:29:31.099 I/Dialer (28149): CallLogFragment - callBack()
09-17 21:29:31.103 I/Dialer (28149): PerformanceReport.stopRecording - enter
//phone进程 也就是package/service/telecom
UserCallIntentProcessor.java中准备跳转意图,跳转进入PrimaryCallReceiver
09-17 21:29:31.123 I/Telecom ( 1141): UserCallIntentProcessor: sendIntentToDestination: send intent to Telecom directly.: TSI.pC@A4U
通过PrimaryCallReceiver.java这个广播进入了CallIntentProcessor.java processIntent方法中,isUnknownCall: false: 是不是陌生电话
09-17 21:29:31.124 I/Telecom ( 1141): CallIntentProcessor: onReceive - isUnknownCall: false: TSI.pC@A4U
进入InCallController.java中的onCallAdded方法通过CallsManager进行调用 listener.onCallAdded(call);
09-17 21:29:31.149 I/Telecom ( 1141): InCallController: onCallAdded: [TC@3, CONNECTING, null, tel:***********, A, childs(0), has_parent(false), [Capabilities:], [Properties:]]; not bound or connected.: TSI.pC@A4U
进入InCallController.java bindToServices 方法
09-17 21:29:31.150 I/Telecom ( 1141): InCallController: defaultDialer: null: TSI.pC@A4U
09-17 21:29:31.151 I/Telecom ( 1141): InCallController: defaultDialer: null: TSI.pC@A4U
EmergencyInCallServiceConnection是InCallController 的内部类,这里开始连接InCallService,最终连接的是InCallServiceImpl
09-17 21:29:31.152 I/Telecom ( 1141): EmergencyInCallServiceConnection: Attempting to bind to InCall [ComponentInfo{org.codeaurora.dialer/com.android.incallui.InCallServiceImpl} supportsExternal? true supportsSelfMg?false], with Intent { act=android.telecom.InCallService cmp=org.codeaurora.dialer/com.android.incallui.InCallServiceImpl (has extras) }: TSI.pC@A4U
//Dialer进程
进入Dialer 里面的InCallServiceImpl.java 里面的onBind() 方法 oldLock 是否是老的InCallPresenter ,这里的log是开发自己增加的 ...
09-17 21:29:31.178 I/Dialer (28149): InCallServiceImpl.onBind - oldLock = 0
//phone进程
与InCallServiceImpl连接成功 info,返回的InCallSerivceInfo
09-17 21:29:31.281 I/Telecom ( 1141): InCallController: onConnected to Component Info{org.codeaurora.dialer/com.android.incallui.InCallServiceImpl}: ICSBC.oSC@A4Y
======================
这一步已经调起了InCallUi 只需要等待数据更新
======================
连接后会进入framework/base中InCallService中的内部的InCallServiceBinder内部类中进行处理,InCallServiceBinder他里面的方法就是给Handler发送信息
Handler调用的方法传输数据给子类进行处理。
//Dialer进程 InCallSerivce只有 Dialer进程中的InCallServiceImpl实现过 InCallServiceImpl实现了InCallService中的一些方法比如onCallAdded()、
加载页面
09-17 21:29:31.614 I/Dialer (28149): InCallFragment.setCallState - PrimaryCallState, state: 13, connectionLabel:
09-17 21:29:31.615 I/Dialer (28149): ContactGridManager - setCallState: 13
09-17 21:29:31.616 I/Dialer (28149): InCallFragment.setContactGridViewMarginTop - screenMode: 0 screenOrientation: 1
InCallActivity数据更新
09-17 21:29:31.616 I/Dialer (28149): InCallActivity.onPrimaryCallStateChanged
09-17 21:29:31.616 I/Dialer (28149): InCallActivity.showMainInCallFragment - shouldShowAnswerUi: false, shouldShowVideoUi: false, didShowAnswerScreen: false, didShowInCallScreen: true, didShowVideoCallScreen: false
09-17 21:29:31.616 I/Dialer (28149): InCallFragment.onInCallShowDialpad - isShown: false