android 8.0 MO 通话

本文详细解析了Android 8.0系统中电话拨打的流程,从Dialer应用层开始,经过Telecomm服务层,再到Telephony服务层,深入探讨了Intent处理、Call对象创建及连接建立的过程,涉及DialpadFragment、TelecomManager、CallsManager等多个关键组件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

本文是作为本人学习总结。

首先挂一下流程图。

 

 

通话相关界面的显示和控制从/packages/apps/Dialer目录下。

一   /packages/apps/Dialer 层

  dial 从DialpadFragment 开始,DialpadFragment 显示一个12键拨号键盘。

这里说明一下, DialpadFragment 是由DialtactsActivity 控制。

DialtactsActivity 控制了DialpadFragment (拨号盘界面),RegularSearchFragment(联系人搜索界面),SmartDialSearchFragment(拨号搜索界面),ListsFragment(最近通话记录,联系人列表显示)。

在DialpadFragment 的handleDialButtonPressed 方法中, 检查号码是否是被禁止的号码, 然后进一步处理。

  

  public void onClick(View view) {
    int resId = view.getId();
    if (resId == R.id.dialpad_floating_action_button) {
      view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
      handleDialButtonPressed();
    // 点击delete 键
    } else if (resId == R.id.deleteButton) {
      keyPressed(KeyEvent.KEYCODE_DEL);
    //点击数字键
    } else if (resId == R.id.digits) {
      if (!isDigitsEmpty()) {
        mDigits.setCursorVisible(true);
      }
    } else if (resId == R.id.dialpad_overflow) {
      mOverflowPopupMenu.show();
    } else {
      LogUtil.w("DialpadFragment.onClick", "Unexpected event from: " + view);
      return;
    }
  }
    private void handleDialButtonPressed() {
    if (isDigitsEmpty()) { // No number entered.
        // 拨打最后通话的号码
        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)) {
        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);
        hideAndClearDialpad(false); //隐藏拨号键盘
      }
    }
  }

看一下 call intent 的结构。

uri 是由String number 包装得到。

  public Intent build() {
    Intent intent = new Intent(Intent.ACTION_CALL, uri);
    // 是否视频通话
    intent.putExtra(
        TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
        isVideoCall ? VideoProfile.STATE_BIDIRECTIONAL : VideoProfile.STATE_AUDIO_ONLY);

    Bundle extras = new Bundle();
    extras.putLong(Constants.EXTRA_CALL_CREATED_TIME_MILLIS, SystemClock.elapsedRealtime());
    CallIntentParser.putCallSpecificAppData(extras, callSpecificAppData);
    intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);

    if (phoneAccountHandle != null) {
      intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
    }

    if (!TextUtils.isEmpty(callSubject)) {
      intent.putExtra(TelecomManager.EXTRA_CALL_SUBJECT, callSubject);
    }

    return intent;
  }

 

DialerUtils.java

    startActivityWithErrorToast 方法主要是将键盘的触点位置传入intent.

 

    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
	// 加入用户在键盘上的触点,后续启动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);
        }

	// wps 网络是美国的高优先级的网络,使得紧急通话不会被无线通话网络阻塞。
        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);
          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();
    }
  }

 

    public static boolean placeCall(Context context, Intent intent) {
    if (hasCallPhonePermission(context)) {
      getTelecomManager(context).placeCall(intent.getData(), intent.getExtras());
      return true;
    }
    return false;
  }

二 /packages/services/Telecomm 层

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

 

 

ITelecomService 在TelecomServiceImpl.java 中实现

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

 public class TelecomServiceImpl {

    private static final int DEFAULT_VIDEO_STATE = -1;

    private final ITelecomService.Stub mBinderImpl = new ITelecomService.Stub() {
	
	......
	
	        /**
         * @see android.telecom.TelecomManager#placeCall
         */
        @Override
        public void placeCall(Uri handle, Bundle extras, String callingPackage) {
            try {
		//权限检查和包检查
                ......
                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(mContext, userHandle)
                                .processIntent(
                                        intent, callingPackage, isSelfManaged ||
                                                (hasCallAppOp && hasCallPermission));
                    } finally {
                        Binder.restoreCallingIdentity(token);
                    }
                }
            } finally {
                Log.endSession();
            }
        }

这里包含了/packages/services/Telecomm 暴露给外层的接口,比如acceptRingingCall,addNewIncomingCall,silenceRinger等方法。

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

processOutgoingCallIntent 将Intent 保存的data,Scheme 等信息重新取出并重新包装发送给PrimaryCallReceiver。

	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_EMERGEN
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值