android 6.0拨打电话流程,源码(1)

时间:2022-11-10 09:51:14

水平有限,有错误之处请指正, 基于MTK的37平台。

(一)输入号码,拨出电话
package com.android.dialer.dialpad
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.dialpad_floating_action_button:
mHaptic.vibrate();
handleDialButtonPressed();
break;
}

R.id.dialpad_floating_action_button 拨号按键
a:mHaptic.vibrate
拨号键按下的振动(触觉反馈)
b:handleDialButtonPressed
处理按键事件

handleDialButtonPressed函数:

packages/apps/Dialer/src/com/android/dialer/dialpad/DialpadFragment.java

  private void handleDialButtonPressed(int type) {
if (isDigitsEmpty()) { // No number entered.
handleDialButtonClickWithEmptyDigits();
} else {
final Intent intent;
/** M: [IP Dial] check the type of call @{ */
if (type != Constants.DIAL_NUMBER_INTENT_NORMAL) {
intent = IntentUtil.getCallIntent(IntentUtil.getCallUri(number),
(getActivity() instanceof DialtactsActivity ?
((DialtactsActivity) getActivity()).getCallOrigin() : null), type);
} else {
intent = IntentUtil.getCallIntent(number,
(getActivity() instanceof DialtactsActivity ?
((DialtactsActivity) getActivity()).getCallOrigin() : null));
}

DialerUtils.startActivityWithErrorToast(getActivity(), intent);
......
}

DialerUtils.startActivityWithErrorToast(getActivity(), intent)这个intent有意思了,他经过了两次处理才真正成型。
1、intent的成型
(1)check the type of call 检查呼叫类型
如果呼叫类型是Constants.DIAL_NUMBER_INTENT_NORMAL,就直接使用number,如果不是经过IntentUtil.getCallUri(number)处理,往下没看,有需要再找。
(2)IntentUtil.getCallIntent()函数。
packages/apps/Dialer/src/com/android/dialer/util/IntentUtil.java

    public static Intent getCallIntent(
Uri uri, String callOrigin, PhoneAccountHandle accountHandle, int videoState) {
final Intent intent = new Intent(CALL_ACTION, uri);
intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
if (callOrigin != null) {
intent.putExtra(PhoneConstants.EXTRA_CALL_ORIGIN, callOrigin);
}
if (accountHandle != null) {
intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
}

return intent;
}

Intent intent = new Intent(CALL_ACTION, uri); 也就这个可以,别的我也不知道是啥,先记在本本上,往下看。
2、DialerUtils.startActivityWithErrorToast()
packages/apps/Dialer/src/com/android/dialer/util/DialerUtils.java

  public static void startActivityWithErrorToast(Context context, Intent intent, int msgId) {
try {
if ((IntentUtil.CALL_ACTION.equals(intent.getAction())
&& context instanceof Activity)) {

// 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 = new Bundle();
extras.putParcelable(TouchPointManager.TOUCH_POINT, touchPoint);
intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
}
final TelecomManager tm =
(TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
tm.placeCall(intent.getData(), intent.getExtras());
} else {
context.startActivity(intent);
}
} catch (ActivityNotFoundException e) {
Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show();
}
}

过滤IntentUtil.CALL_ACTION的intent,这个函数不只是用于拨打电话吧。

获取点击位置传递给InCallUI
// All dialer-initiated calls should pass the touch point to the InCallUI
Point touchPoint = TouchPointManager.getInstance().getPoint();

TelecomManager提供对有关呼叫注册/呼叫管理功能的信息的访问
final TelecomManager tm = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
tm.placeCall(intent.getData(), intent.getExtras());

frameworks/base/telecomm/java/android/telecom/TelecomManager.java

/*Places a new outgoing call

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());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#placeCall", e);
}
}
}

getTelecomService 这个具体的事例在那,这个找了半天没找到,最后网上搜了一下,TelecomServiceImpl,这个以后得找找,记在本本上。

packages/services/Telecomm/src/com/android/server/telecom/TelecomServiceImpl.java

 @Override
public void placeCall(Uri handle, Bundle extras, String callingPackage) {
enforceCallingPackage(callingPackage);
......//此处check权限,和设备的支持状态。

synchronized (mLock) {
final UserHandle userHandle = Binder.getCallingUserHandle();
long token = Binder.clearCallingIdentity();
try {
final Intent intent = new Intent(Intent.ACTION_CALL, handle);
intent.putExtras(extras);
new UserCallIntentProcessor(mContext, userHandle).processIntent(intent,
callingPackage, hasCallAppOp && hasCallPermission);
} finally {
Binder.restoreCallingIdentity(token);
}
}
}

这里呢前面是检查了设备是否支持电话功能,还有权限check。
后来又重新 new Intent,厉害了我的哥,不过好像没变啥。

packages/services/Telecomm/src/com/android/server/telecom/components/UserCallIntentProcessor.java

这里写代码片
   public void processIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency) {
// 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);
}
}

private void processOutgoingCallIntent(Intent intent, String callingPackageName,
boolean canCallNonEmergency) {
Uri handle = intent.getData();
String scheme = ();
String uriString = handle.getSchemeSpecificPart();

/// M: Do noting for CDMA empty flash at present
if (intent.getBooleanExtra(EXTRA_SEND_EMPTY_FLASH, false)) {
Log.w(this, "Empty flash obtained from the call intent.");
return;
}

if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
handle = Uri.fromParts(PhoneNumberUtils.isUriNumber(uriString) ?
PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL, uriString, null);
}

final UserManager userManager =
(UserManager) mContext.getSystemService(Context.USER_SERVICE);
if (userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, mUserHandle)
&& !TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
// Only emergency calls are allowed for users with the DISALLOW_OUTGOING_CALLS
// restriction.
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;
}

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);

if (VideoProfile.isVideo(videoState)
&& TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
Log.d(this, "Emergency call...Converting video call to voice...");
videoState = VideoProfile.STATE_AUDIO_ONLY;
intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
videoState);
}

if (VideoProfile.isVideo(videoState) && isTtyModeEnabled()) {
Toast.makeText(mContext, mContext.getResources().getString(R.string.
video_call_not_allowed_if_tty_enabled), Toast.LENGTH_SHORT).show();
Log.d(this, "Rejecting video calls as tty is enabled");
return;
}

intent.putExtra(CallIntentProcessor.KEY_IS_PRIVILEGED_DIALER,
isDefaultOrSystemDialer(callingPackageName));
sendBroadcastToReceiver(intent);
}
......
private boolean sendBroadcastToReceiver(Intent intent) {
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.OWNER);
return true;
}
```
processIntent也是去check。
processOutgoingCallIntent 处理Intent
sendBroadcastToReceiver 发送广播
(intent.setClass(mContext, PrimaryCallReceiver.class) 小白一个,广播都没见过这么用的)记本本上。


packages/services/Telecomm/src/com/android/server/telecom/components/PrimaryCallReceiver.java

public class PrimaryCallReceiver extends BroadcastReceiver implements TelecomSystem.Component {

@Override
public void onReceive(Context context, Intent intent) {
synchronized (getTelecomSystem().getLock()) {
getTelecomSystem().getCallIntentProcessor().processIntent(intent);
}
}

@Override
public TelecomSystem getTelecomSystem() {
return TelecomSystem.getInstance();
}
}

PrimaryCallReceiver这个东西把进来我下了一跳,代码这么少还跳一下,这是什么鬼??
好吧跳就跳吧,获取一个TelecomSystem的实例

packages/services/Telecomm/src/com/android/server/telecom/TelecomSystem.java

public CallIntentProcessor getCallIntentProcessor() {
return mCallIntentProcessor;
}
然后processIntent(intent)。

packages/services/Telecomm/src/com/android/server/telecom/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();
}

Trace这玩意瞅了半天。在http://android.xsoftlab.net/tools/debugging/systrace.html是看到了介绍,是性能分析器。记本本上。

   static void processOutgoingCallIntent(
Context context,
CallsManager callsManager,
Intent intent) {
Uri handle = intent.getData();
String scheme = handle.getScheme();
String uriString = handle.getSchemeSpecificPart();

PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
......
Bundle clientExtras = null;
if (clientExtras == null) {
clientExtras = new Bundle();
}
......
final boolean isPrivilegedDialer = intent.getBooleanExtra(KEY_IS_PRIVILEGED_DIALER, false);
......
// Here we handle all error case for VoLTE.
boolean isImsCallRequest = TelecomVolteUtils.isImsCallOnlyRequest(intent);
boolean isConferenceDialRequest = TelecomVolteUtils.isConferenceDialRequest(intent);
...... //对于VoLTE的处理。
// Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
Call call = callsManager.startOutgoingCall(handle, phoneAccountHandle, clientExtras);

if (call != null) {
/// M: ip dial. ip prefix already add, here need to change intent @{
if (call.isIpCall()) {
intent.setData(call.getHandle());
}
/// @}

/// M: For VoLTE - Conference Dial @{
// For Con dial, skip NewOutgoingCallIntentBroadcaster. createConnection() directly.
if (call.isConferenceDial()) {
call.startCreateConnection(TelecomSystem.getInstance().getPhoneAccountRegistrar());
return;
}
/// @}

// Asynchronous calls should not usually be made inside a BroadcastReceiver
// because once
// onReceive is complete, the BroadcastReceiver's process runs the risk of getting
// killed if memory is scarce. However, this is OK here because the entire Telecom
// process will be running throughout the duration of the phone call and should never
// be killed.
NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
context, callsManager, call, intent, isPrivilegedDialer);

final int result = broadcaster.processIntent();
final boolean success = result == DisconnectCause.NOT_DISCONNECTED;

if (!success && call != null) {
disconnectCallAndShowErrorDialog(context, call, result);
}
}
}

调用CallsManager.java的startOutgoingCall()方法创建一个Call实例
Call call = callsManager.startOutgoingCall(handle, phoneAccountHandle, clientExtras);
然后传递给NewOutgoingCallIntentBroadcaster。