可以看到,在aosp的packages\services包下,有两个service,分别为Telecomm和Telephony,Telecomm在android5.0引入,是Android的一个系统服务,运行在system系统进程,位于Dialer,和TeleService之间,起到桥梁作用。Telephony,TeleService服务,运行于自身的com.android.phone进程。
路径 | 统称 | 进程 |
---|---|---|
packages/app/Dialer | Dialer | com.android.dialer |
packages/service/telecomm | telecom | system_service |
packages/service/telephony | TeleService | TeleService |
Telecom服务
Telecom是一个更一般性的通讯框架,运行在system进程中。
1.点击拨号
/android/packages/apps/Dialer/java/com/android/dialer/dialpadview/DialpadFragment.java
@Override
public void onClick(View view) {
int resId = view.getId();
//点击拨号
if (resId == R.id.dialpad_floating_action_button) {
view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
handleDialButtonPressed();
}
在handleDialButtonPressed中做一些基本的判断,判断号码是否为空
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 = digits.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(prohibitedPhoneNumberRegexp)
&& number.matches(prohibitedPhoneNumberRegexp)) {
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 {
PreCall.start(getContext(), new CallIntentBuilder(number, CallInitiationType.Type.DIALPAD));
hideAndClearDialpad();
}
}
}
/android/packages/apps/Dialer/java/com/android/dialer/precall/PreCall.java
static void start(Context context, CallIntentBuilder builder) {
//调用DialerUtils.startActivityWithErrorToast方法
DialerUtils.startActivityWithErrorToast(context, getIntent(context, builder));
}
/android/packages/apps/Dialer/java/com/android/dialer/util/DialerUtils.java
//开启一个activity,或者显示错误弹窗
public static void startActivityWithErrorToast(
final Context context, final Intent intent, int msgId) {
try {
//如果activity是拨打电话
if ((Intent.ACTION_CALL.equals(intent.getAction()))) {
...
if (shouldWarnForOutgoingWps(context, intent.getData().getSchemeSpecificPart())) {
...
} else {
//拨打电话,或者显示错误弹窗
placeCallOrMakeToast(context, intent);
}
} else { //不是直接开启activity
context.startActivity(intent);
}
} catch (ActivityNotFoundException e) {
//如果开启activity异常则,显示错误弹窗
Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show();
}
}
/packages/apps/Dialer/java/com/android/dialer/telecom/TelecomUtil.java
//尝试使用TelecomManager去拨打一个电话
//拨打一个电话成功,返回true
//如果由于权限检查,拨打电话失败返回false
public static boolean placeCall(Context context, Intent intent) {
if (hasCallPhonePermission(context)) {
getTelecomManager(context).placeCall(intent.getData(), intent.getExtras());
return true;
}
return false;
}
/frameworks/base/telecomm/java/android/telecom/TelecomManager.java
//使用telecom service和其他的附加服务,向提供的地址拨打电话
@RequiresPermission(anyOf = {android.Manifest.permission.CALL_PHONE,
android.Manifest.permission.MANAGE_OWN_CALLS})
public void placeCall(Uri address, Bundle extras) {
//获取TelecomService
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(), mContext.getAttributionTag());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#placeCall", e);
}
}
}
通过ITelecomService 远程调用Telecom系统服务,接下来就进入到system_process进程
/packages/services/Telecomm/src/com/android/server/telecom/TelecomServiceImpl.java
/**
* @see android.telecom.TelecomManager#placeCall
*/
@Override
public void placeCall(Uri handle, Bundle extras, String callingPackage,
String callingFeatureId) {
try {
Log.startSession("TSI.pC");
enforceCallingPackage(callingPackage);
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, callingFeatureId,
"CALL_PHONE permission required to place calls.")) {
// The caller is not allowed to place calls, so we want to ensure that it
// can only place calls through itself.
throw new SecurityException("Self-managed ConnectionServices can only "
+ "place calls through their own ConnectionService.");
}
} else if (!canCallPhone(callingPackage, callingFeatureId, "placeCall")) {
throw new SecurityException("Package " + callingPackage
+ " is not allowed to place phone calls");
}
// Note: we can still get here for the default/system dialer, even if the Phone
// permission is turned off. This is because the default/system dialer is always
// allowed to attempt to place a call (regardless of permission state), in case
// it turns out to be an emergency call. If the permission is denied and the
// call is being made to a non-emergency number, the call will be denied later on
// by {@link UserCallIntentProcessor}.
final boolean hasCallAppOp = mAppOpsManager.noteOp(AppOpsManager.OP_CALL_PHONE,
Binder.getCallingUid(), callingPackage, callingFeatureId, null)
== AppOpsManager.MODE_ALLOWED;
final boolean hasCallPermission = mContext.checkCallingPermission(CALL_PHONE) ==
PackageManager.PERMISSION_GRANTED;
// The Emergency Dialer has call privileged permission and uses this to place
// emergency calls. We ensure permission checks in
// NewOutgoingCallIntentBroadcaster#process pass by sending this to
// Telecom as an ACTION_CALL_PRIVILEGED intent (which makes sense since the
// com.android.phone process has that permission).
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();
}
}
/packages/services/Telecomm/src/com/android/server/telecom/components/UserCallIntentProcessor.java
//处理发送到活动的意图
public void processIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency, boolean isLocalInvocation) {
// Ensure call intents are not processed on devices that are not capable of calling.
//确保不在无法呼叫的设备的拨打电话
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);
}
}
//处理向外呼叫的intent
private void processOutgoingCallIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency, boolean isLocalInvocation) {
...
sendIntentToDestination(intent, isLocalInvocation, callingPackageName);
}
private boolean sendIntentToDestination(Intent intent, boolean isLocalInvocation,
String callingPackage) {
intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);
intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
if (isLocalInvocation) {
// We are invoking this from TelecomServiceImpl, so TelecomSystem is available. Don't
// bother trampolining the intent, just sent it directly to the call intent processor.
// TODO: We should not be using an intent here; this whole flows needs cleanup.
Log.i(this, "sendIntentToDestination: send intent to Telecom directly.");
synchronized (TelecomSystem.getInstance().getLock()) {
TelecomSystem.getInstance().getCallIntentProcessor().processIntent(intent,
callingPackage);
}
} else {
// We're calling from the UserCallActivity, so the TelecomSystem is not in the same
// process; we need to trampoline to TelecomSystem in the system server process.
Log.i(this, "sendIntentToDestination: trampoline to Telecom.");
TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
tm.handleCallIntent(intent, callingPackage);
}
return true;
}
/frameworks/base/telecomm/java/android/telecom/TelecomManager.java
public void handleCallIntent(Intent intent, String callingPackageProxy) {
ITelecomService service = getTelecomService();
if (service != null) {
try {
//使用通信服务
service.handleCallIntent(intent, callingPackageProxy);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException handleCallIntent: " + e);
}
}
}
/packages/services/Telecomm/src/com/android/server/telecom/TelecomServiceImpl.java
public void handleCallIntent(Intent intent, String callingPackage) {
try {
Log.startSession("TSI.hCI");
synchronized (mLock) {
mContext.enforceCallingOrSelfPermission(PERMISSION_HANDLE_CALL_INTENT,
"handleCallIntent is for internal use only.");
long token = Binder.clearCallingIdentity();
try {
Log.i(this, "handleCallIntent: handling call intent");
mCallIntentProcessorAdapter.processOutgoingCallIntent(mContext,
mCallsManager, intent, callingPackage);
} finally {
Binder.restoreCallingIdentity(token);
}
}
} finally {
Log.endSession();
}
}
可以看到,调用了 mCallIntentProcessorAdapter.processOutgoingCallIntent,而mCallIntentProcessorAdapter是在TelecomServiceImpl的构造函数中初始化的,mCallIntentProcessorAdapter = callIntentProcessorAdapter;而,TelecomServiceImpl是在TelecomSystem的构造函数中创建的,至于TelecomSystem的创建和TelecomService服务的启动可以看博客https://blog.csdn.net/jason_wzn/article/details/58164251的分析
mTelecomServiceImpl = new TelecomServiceImpl(
mContext, mCallsManager, mPhoneAccountRegistrar,
new CallIntentProcessor.AdapterImpl(defaultDialerCache),
new UserCallIntentProcessorFactory() {
@Override
public UserCallIntentProcessor create(Context context,
UserHandle userHandle) {
return new UserCallIntentProcessor(context, userHandle);
}
},
defaultDialerCache,
new TelecomServiceImpl.SubscriptionManagerAdapterImpl(),
new TelecomServiceImpl.SettingsSecureAdapterImpl(),
mLock);
最终调用/packages/services/Telecomm/src/com/android/server/telecom/CallIntentProcessor#AdapterImpl.processOutgoingCallIntent方法
public void processOutgoingCallIntent(Context context, CallsManager callsManager,
Intent intent, String callingPackage) {
CallIntentProcessor.processOutgoingCallIntent(context, callsManager, intent,
callingPackage, mDefaultDialerCache);
}
调用CallIntentProcessor.processOutgoingCallIntent
static void processOutgoingCallIntent(
Context context,
CallsManager callsManager,
Intent intent,
String callingPackage,
DefaultDialerCache defaultDialerCache) {
...
// Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
//创建call对象
CompletableFuture<Call> callFuture = callsManager
.startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser,
intent, callingPackage);
...
}
/packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java
CompletableFuture<Call> startOutgoingCall(Uri handle,
PhoneAccountHandle requestedAccountHandle,
Bundle extras, UserHandle initiatingUser, Intent originalIntent,
String callingPackage) {
final List<Uri> callee = new ArrayList<>();
callee.add(handle);
return startOutgoingCall(callee, requestedAccountHandle, extras, initiatingUser,
originalIntent, callingPackage, false);
}
private CompletableFuture<Call> startOutgoingCall(List<Uri> participants,
PhoneAccountHandle requestedAccountHandle,
Bundle extras, UserHandle initiatingUser, Intent originalIntent,
String callingPackage, boolean isConference) {
...
if (call == null) {
call = new Call(getNextCallId(), mContext,
this,
mLock,
mConnectionServiceRepository,
mPhoneNumberUtilsAdapter,
handle,
isConference ? participants : null,
null /* gatewayInfo */,
null /* connectionManagerPhoneAccount */,
null /* requestedAccountHandle */,
Call.CALL_DIRECTION_OUTGOING /* callDirection */,
false /* forceAttachToExistingConnection */,
isConference, /* isConference */
mClockProxy,
mToastFactory);
addCall(callToPlace);
...
}
//将指定的通话添加到实时通话列表中
public void addCall(Call call) {
...
call.addListener(this);
mCalls.add(call);
...
for (CallsManagerListener listener : mListeners) {
...
listener.onCallAdded(call);
...
}
...
}
/packages/services/Telecomm/src/com/android/server/telecom/InCallController.java
public void onCallAdded(Call call) {
if (!isBoundAndConnectedToServices()) {
// We are not bound, or we're not connected.
bindToServices(call);
} else {
...
addCall(call);
...
List<ComponentName> componentsUpdated = new ArrayList<>();
for (Map.Entry<InCallServiceInfo, IInCallService> entry : mInCallServices.entrySet()) {
...
inCallService.addCall(sanitizeParcelableCallForService(info, parcelableCall));
...
}
}
这里调用inCallService.addCall远程调用Dialer进程的InCallService服务,并将Call对象传回。
/frameworks/base/telecomm/java/android/telecom/InCallService.InCallServiceBinder
public void addCall(ParcelableCall call) {
mHandler.obtainMessage(MSG_ADD_CALL, call).sendToTarget();
}
InCallServiceBinder.mHandler
case MSG_ADD_CALL:
mPhone.internalAddCall((ParcelableCall) msg.obj);
break;
/frameworks/base/telecomm/java/android/telecom/Phone.java
final void internalUpdateCall(ParcelableCall parcelableCall) {
...
call.internalUpdate(parcelableCall, mCallByTelecomCallId);
...
}
/frameworks/base/telecomm/java/android/telecom/Call.java
final void internalUpdate(ParcelableCall parcelableCall, Map<String, Call> callIdMap) {
...
// Now we fire updates, ensuring that any client who listens to any of these notifications
// gets the most up-to-date state.
if (stateChanged) {
fireStateChanged(mState);
}
if (detailsChanged) {
fireDetailsChanged(mDetails);
}
...
}
private void fireStateChanged(final int newState) {
for (CallbackRecord<Callback> record : mCallbackRecords) {
final Call call = this;
final Callback callback = record.getCallback();
record.getHandler().post(new Runnable() {
@Override
public void run() {
callback.onStateChanged(call, newState);
}
});
}
}
/packages/apps/Dialer/java/com/android/incallui/call/DialerCall#telecomCallCallback
public void onStateChanged(Call call, int newState) {
LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call + " newState=" + newState);
update();
}
private void update() {
...
for (DialerCallListener listener : listeners) {
listener.onDialerCallUpdate();
}
...
}
/packages/apps/Dialer/java/com/android/incallui/call/CallList.java#DialerCallListenerImpl
@Override
public void onDialerCallUpdate() {
Trace.beginSection("CallList.onDialerCallUpdate");
onUpdateCall(call);
notifyGenericListeners();
Trace.endSection();
}
CallList.java
private void notifyGenericListeners() {
...
for (Listener listener : listeners) {
listener.onCallListChange(this);
}
...
}
CallList的listeners在incallui/InCallPresenter.java的setUp,中赋值this.callList = callList;
/packages/apps/Dialer/java/com/android/incallui/InCallPresenter.java
public void onCallListChange(CallList callList) {
...
newState = startOrFinishUi(newState);
...
}
private InCallState startOrFinishUi(InCallState newState) {
...
if ((showCallUi || showAccountPicker) && !shouldStartInBubbleMode()) {
LogUtil.i("InCallPresenter.startOrFinishUi", "Start in call UI");
//开始显示UI
showInCall(false /* showDialpad */, !showAccountPicker /* newOutgoingCall */);
} else if (newState == InCallState.NO_CALLS) {
// The new state is the no calls state. Tear everything down.
inCallState = newState;
attemptFinishActivity();
attemptCleanup();
}
...
return newState;
}
开启InCallActivity
public void showInCall(boolean showDialpad, boolean newOutgoingCall) {
LogUtil.i("InCallPresenter.showInCall", "Showing InCallActivity");
context.startActivity(
InCallActivity.getIntent(context, showDialpad, newOutgoingCall, false /* forFullScreen */));
}