您的位置:首页 > 其它

(O)Telephony分析之通话流程分析(二)拨打电话流程分析(上)

2018-01-12 17:46 471 查看
拨打电话,是从通话的拨号盘开始的,而拨号盘的界面是在DialpadFragment,因此,需要从这个地方进行分析
一.拨号盘界面拨号流程

public void onClick(View view) {
......
if (resId == R.id.dialpad_floating_action_button) {
view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
// 处理Dial Button点击事件
handleDialButtonPressed();
}
......
}通过调用handleDialButtonPressed方法,处理点击拨号按钮后的流程
private void handleDialButtonPressed() {
// 没有输入号码的情况下,暂不讨论
if (isDigitsEmpty()) { // No number entered.
handleDialButtonClickWithEmptyDigits();
} else {
......
// uri加上tel前缀,并且type设置为INITIATION_DIALPAD,生成URI
final Intent intent = new CallIntentBuilder(number).setCallInitiationType(LogState.INITIATION_DIALPAD).build();
// Leo, 尝试去启动一个Activity,如果启动不了,会弹出一个 "Activity is not found" 的Toast框
DialerUtils.startActivityWithErrorToast(getActivity(), intent);
// Leo,隐藏拨号盘,并且清除号码盘上的号码
hideAndClearDialpad(false);
}
}可以看到此处通过初始化CallIntentBuilder,然后调用其build方法,生成一个Intent对象,并作为参数,调用DialerUtils的startActivityWithErrorToast方法
public static class CallIntentBuilder {
......
public CallIntentBuilder(Uri uri) {
mUri = uri;
}

public CallIntentBuilder(String number) {
this(CallUtil.getCallUri(number));
}

public CallIntentBuilder setCallInitiationType(int initiationType) {
mCallInitiationType = initiationType;
return this;
}

public Intent build() {
// Leo, 初始化一个Intent
return getCallIntent(
mUri,
mPhoneAccountHandle,
mIsVideoCall ? VideoProfile.STATE_BIDIRECTIONAL : VideoProfile.STATE_AUDIO_ONLY,
mCallInitiationType);
}
}
从这边来看,首先通过构造方法,将number生成一个Uri,然后通过build方法调用getCallIntent方法,返回一个Intent,而通过上述了解,

知道getCallIntent的参数分别为,

第一个参数为刚刚生成的Uri对象,其是以tel:为前缀的

第二个参数由于并未设置过,因此为null

第三个参数由于没有设置过视频通话,因此为VideoProfile.STATE_AUDIO_ONLY,语音通话

第四个参数为传入的LogState.INITIATION_DIALPAD
public static Intent getCallIntent(
Uri uri, PhoneAccountHandle accountHandle, int videoState, int callIntiationType) {
// 初始化一个Intent,action为CALL_ACTION,Data为uri
final Intent intent = new Intent(CALL_ACTION, uri);
// 目前应该是没有videoState的,即为VideoProfile.STATE_AUDIO_ONLY
intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);

final Bundle b = new Bundle();
// 设置其INITIATION_TYPE,此处第一次为LogState.INITIATION_DIALPAD
b.putInt(EXTRA_CALL_INITIATION_TYPE, callIntiationType);
// 设置其Extra
intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, b);

// 此前没有设置,应该为null
if (accountHandle != null) {
intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
}

return intent;
}确认Intent中所包含的数据

接下来会调用DialerUtils的startActivityWithErrorToast方法
public static void startActivityWithErrorToast(Context context, Intent intent) {
startActivityWithErrorToast(context, intent, R.string.activity_not_available);
}
public static void startActivityWithErrorToast(Context context, Intent intent, int msgId) {
try {
if ((IntentUtil.CALL_ACTION.equals(intent.getAction())
&& context instanceof Activity)) {
......
// 调用TelecomUtil的placeCall函数,传入的参数为intent
final boolean hasCallPermission = TelecomUtil.placeCall((Activity) 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);
}
}
......
} catch (ActivityNotFoundException e) {
Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show();
}
}
调用TelecomUtil的placeCall方法

二.Telecom阶段
public static boolean placeCall(Activity activity, Intent intent) {
if (hasCallPhonePermission(activity)) {
// 调用TelecomManagerCompat的placeCall函数,注意其传入的参数
// 第一个参数为DialpadFragment中的getActivity
// 第二个参数为TelecomManager对象
// 第三个参数中包含了拨号的所有参数
TelecomManagerCompat.placeCall(activity, getTelecomManager(activity), intent);
return true;
}
return false;
}
(一)getTelecomManager是什么

看下其代码
private static TelecomManager getTelecomManager(Context context) {
return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
}
此前,我们有对getSystemService进行过分析,是在SystemServiceRegistry中进行注册的,如下:
registerService(Context.TELECOM_SERVICE, TelecomManager.class,
new CachedServiceFetcher<TelecomManager>() {
@Override
public TelecomManager createService(ContextImpl ctx) {
return new TelecomManager(ctx.getOuterContext());
}});其返回的是新初始化的TelecomManager对象,再看下TelecomManager的构造方法
public TelecomManager(Context context) {
this(context, null);
}
public TelecomManager(Context context, ITelecomService telecomServiceImpl) {
Context appContext = context.getApplicationContext();
if (appContext != null) {
mContext = appContext;
} else {
mContext = context;
}
mTelecomServiceOverride = telecomServiceImpl;
android.telecom.Log.initMd5Sum();
}
因此,此处的TelecomManager对象中的telecomServiceImpl为null

(二)TelecomManagerCompat的placeCall方法分析
public static void placeCall(@Nullable Activity activity,
@Nullable TelecomManager telecomManager, @Nullable Intent intent) {
......
// Android M以上的版本
if (CompatUtils.isMarshmallowCompatible()) {
// 调用了TelecomManager对象的placeCall函数
// 第一个参数为包含号码的URI
telecomManager.placeCall(intent.getData(), intent.getExtras());
return;
}
activity.startActivityForResult(intent, 0);
}由于是O项目,高于M项目,因此调用的是telecomManager的placeCall方法,而telecomManager是刚刚看到的TelecomManager对象,因此
public void placeCall(Uri address, Bundle extras) {
ITelecomService service = getTelecomService();
if (service != null) {
......
try {
service.placeCall(address, extras == null ? new Bundle() : extras,
mContext.getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#placeCall", e);
}
}
}从此前的分析,TelecomManager的getTelecomService方法,在telecomServiceImpl为null的时候,返回的是TelecomManagerImpl的mBinderImpl

因此,调用了mBinderImpl的placeCall
public void placeCall(Uri handle, Bundle extras, String
4000
callingPackage) {
try {
......
// 判断是否拥有拨打电话的权限
final boolean hasCallPermission = mContext.checkCallingPermission(CALL_PHONE) ==
PackageManager.PERMISSION_GRANTED;

synchronized (mLock) {
final UserHandle userHandle = Binder.getCallingUserHandle();
long token = Binder.clearCallingIdentity();
try {
final Intent intent = new Intent(Intent.ACTION_CALL, handle);
if (extras != null) {
extras.setDefusable(true);
intent.putExtras(extras);
}
// mUserCallIntentProcessorFactory的create方法是在TelecomSystem的构造方法中重写的
// 返回的是UserCallIntentProcessor对象
mUserCallIntentProcessorFactory.create(mContext, userHandle)
.processIntent(
intent, callingPackage, isSelfManaged ||
(hasCallAppOp && hasCallPermission));
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
......
}调用mUserCallIntentProcessorFactory的create方法,并且调用返回对象的processIntent方法,那么mUserCallIntentProcessorFactory是什么呢?

查看其定义的地方
public TelecomServiceImpl(
Context context,
CallsManager callsManager,
PhoneAccountRegistrar phoneAccountRegistrar,
CallIntentProcessor.Adapter callIntentProcessorAdapter,
UserCallIntentProcessorFactory userCallIntentProcessorFactory,
DefaultDialerCache defaultDialerCache,
SubscriptionManagerAdapter subscriptionManagerAdapter,
TelecomSystem.SyncRoot lock) {
......
mUserCallIntentProcessorFactory = userCallIntentProcessorFactory;
......
}也就是说,这个是由TelecomServiceImpl对象在初始化的时候,传入的,那么TelecomServiceImpl是在什么地方初始化的呢?

还记得前篇中关于TelecomSystem的初始化么?在该类的构造方法中,可以看到
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);也就是说,UserCallIntentProcessorFactory的create方法中,初始化了一个UserCallIntentProcessor对象,因此

上述的TelecomManager的placeCall方法,最终是调用了UserCallIntentProcessor对象的processIntent方法
public void processIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency) {
......
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);
}
}由于此前传入的Intent的ACTION为Intent.ACTION_CALL,因此会调用processOutgoingCallIntent,此处我们需要注意的是,其传入的参数是什么

第一个参数为带有数据的Intent,这个无需多言

第二个参数为callingPackageName,是在TelecomManager中使用mContext.getOpPackageName()得到的,而mContext是由DialpadFragment中使用getActivity得到的,因此,它的值就一目了然了

第三个参数为canCallNonEmergency,是在TelecomServiceImpl中定义的,这个无需多言了

接下来,继续分析代码processOutgoingCallIntent
private void processOutgoingCallIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency) {
Uri handle = intent.getData(); // tel:号码
String scheme = handle.getScheme(); // tel
String uriString = handle.getSchemeSpecificPart();

......

// 通话状态,默认是语音通话
int videoState = intent.getIntExtra(
TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
VideoProfile.STATE_AUDIO_ONLY);
Log.d(this, "processOutgoingCallIntent videoState = " + videoState);

// Leo, 传入的package是否为系统默认的Dialer应用
intent.putExtra(CallIntentProcessor.KEY_IS_PRIVILEGED_DIALER,
isDefaultOrSystemDialer(callingPackageName));

// Save the user handle of current user before forwarding the intent to primary user.
intent.putExtra(CallIntentProcessor.KEY_INITIATING_USER, mUserHandle);

sendBroadcastToReceiver(intent);
}这个mUserHandle,追述到上面,可以看到是在TelecomServiceImpl中进行定义的,具体的代码为

final UserHandle userHandle = Binder.getCallingUserHandle();

暂时不提
private boolean sendBroadcastToReceiver(Intent intent) {
// 非incoming call
intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);
intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
// 设置接受广播的类
intent.setClass(mContext, PrimaryCallReceiver.class);
Log.d(this, "Sending broadcast as user to CallReceiver");
// 系统广播
mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
return true;
}
可以看到,其实是将通话的基本信息发送给PrimaryCallReceiver进行处理
public void onReceive(Context context, Intent intent) {
......
synchronized (getTelecomSystem().getLock()) {
// 接收到消息后处理,调用TelecomSystem的getCallIntentPRocessor方法,获取CallIntentProcessor对象
// 并且调用起的processIntent方法
getTelecomSystem().getCallIntentProcessor().processIntent(intent);
}
......
}
public TelecomSystem getTelecomSystem() {
return TelecomSystem.getInstance();
}
这个TelecomSystem的getInstance方法,返回了一个TelecomSystem对象,就是我们前篇所了解到的TelecomSystem对象,那么
public CallIntentProcessor getCallIntentProcessor() {
return mCallIntentProcessor;
}
TelecomSystem的构造函数中
mCallIntentProcessor = new CallIntentProcessor(mContext, mCallsManager);
初始化CallIntentProcessor对象,并调用其processIntent方法
public void processIntent(Intent intent) {
// 默认false
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();
}
调用processOutgoingCallIntent方法,注意其参数mCallsManager是在TelecomSystem中初始化CallIntentProcessor时传入的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Telephony 拨号流程