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\components\PrimaryCallReceiver.java 廣播,作爲撥號的單一入口, 其到 消息後,就轉發給 TelecomSystem 的
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
總結一下: system 進程的 InCallController 收到來自於 CallsManager 的消息後, 發送到 com.android.dialer 進程, 一步步地更新 撥號界面的狀態, 整個過程已經全部打通了,只需要調用CallsManager中的addCall方法就會更新狀態。