Android Dialer源码分析之通话中主动显示IncallActivity

描述:当手机已经在通话中的时候,界面回到桌面,再打开Dialer app的时候,手机就会提示并让你确认到底你是想打开已经在通话的界面还是不管正在通话中的电话而重新拨打新的通话。
假如此刻你想打开正在通话的界面,android的源码不是直接在DialtactsActivitystartActivity,而是经过系统通话服务最终启动。
这个过程里,代码从Dialer走到Framework,然后从Framework走到Telecom,再从Telecom走到Framework,再从Framework走到Dialer。跨进程通信的过程比较值得回味。


当DialtactsActivity启动后,会去检查是否正在通话中,
如果在通话中,就会让Dialpadframgent显示一个Listview形式的DialpadChooser,当点击回到通话界面的选项时,会会回调到onItemClick

@Override
  public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
    DialpadChooserAdapter.ChoiceItem item =
        (DialpadChooserAdapter.ChoiceItem) parent.getItemAtPosition(position);
    int itemId = item.id;
    if (itemId == DialpadChooserAdapter.DIALPAD_CHOICE_USE_DTMF_DIALPAD) {
      returnToInCallScreen(true);
    } else if (itemId == DialpadChooserAdapter.DIALPAD_CHOICE_RETURN_TO_CALL) {
      returnToInCallScreen(false);
    } else if (itemId == DialpadChooserAdapter.DIALPAD_CHOICE_ADD_NEW_CALL) {
      showDialpadChooser(false);
    } else {
      LogUtil.w("DialpadFragment.onItemClick", "Unexpected itemId: " + itemId);
    }
  }

第二项DIALPAD_CHOICE_RETURN_TO_CALL
returnToInCallScreen(false)
这里销毁DialtactsActivity并调用TelecomUtil去处理显示InCallScreen;

  private void returnToInCallScreen(boolean showDialpad) {
    TelecomUtil.showInCallScreen(getActivity(), showDialpad);
    getActivity().finish();
  }

TelecomUtil

  public static void showInCallScreen(Context context, boolean showDialpad) {
    if (hasReadPhoneStatePermission(context)) {
      try {
        getTelecomManager(context).showInCallScreen(showDialpad);
      } catch (SecurityException e) {
        // Just in case
        LogUtil.w(TAG, "TelecomManager.showInCallScreen called without permission.");
      }
    }
  }

getTelecomManager获取TelecomManager

  private static TelecomManager getTelecomManager(Context context) {
    return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
  }

TelecomManager

	public void showInCallScreen(boolean showDialpad) {
        ITelecomService service = getTelecomService();
        if (service != null) {
            try {
                service.showInCallScreen(showDialpad, mContext.getOpPackageName());
            } catch (RemoteException e) {
                Log.e(TAG, "Error calling ITelecomService#showCallScreen", e);
            }
        }
    }

TelecomManager是属于Framework
\frameworks\base\telecomm\java\android\telecom\TelecomManager.java

getTelecomService()

    private ITelecomService getTelecomService() {
        if (mTelecomServiceOverride != null) {
            return mTelecomServiceOverride;
        }
        return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));
    }

要找到实现ITelecomService的实体,
发现是在TelecomServiceImpl里,
packages\services\Telecomm\src\com\android\server\telecom\TelecomServiceImpl.java

private final ITelecomService.Stub mBinderImpl = new ITelecomService.Stub()
内部类ITelecomService.Stub mBinderImplITelecomService的实体,实现了ITelecomService接口方法。

@Override
public void showInCallScreen(boolean showDialpad, String callingPackage) {
    try {
        Log.startSession("TSI.sICS");
        if (!canReadPhoneState(callingPackage, "showInCallScreen")) {
            return;
        }
        synchronized (mLock) {
            long token = Binder.clearCallingIdentity();
            try {
                mCallsManager.getInCallController().bringToForeground(showDialpad);
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }
    } finally {
        Log.endSession();
    }
}

检查token过后就用CallsManager获取InCallController,调用bringToForeground

    InCallController getInCallController() {
        return mInCallController;
    }

这个InCallController是在CallsManager构造方法里新建的

   mInCallController = new InCallController(
           context, mLock, this, systemStateProvider, defaultDialerCache, mTimeoutsAdapter,
           emergencyCallHelper);

InCallController:

    void bringToForeground(boolean showDialpad) {
        if (!mInCallServices.isEmpty()) {
            for (IInCallService inCallService : mInCallServices.values()) {
                try {
                    inCallService.bringToForeground(showDialpad);
                } catch (RemoteException ignored) {
                }
            }
        } else {
            Log.w(this, "Asking to bring unbound in-call UI to foreground.");
        }
    }

然后就是调用IInCallService这个远程接口来处理,
谁实现了这个远程接口呢?

答案在IncallService的内部类InCallServiceBinder里。
InCallService是在Framewrok里的,
\frameworks\base\telecomm\java\android\telecom\InCallService.java

InCallServiceBinder extends IInCallService.Stub
---
@Override
public void bringToForeground(boolean showDialpad) {
    mHandler.obtainMessage(MSG_BRING_TO_FOREGROUND, showDialpad ? 1 : 0, 0).sendToTarget();
}

通过Handler来发送message,

  case MSG_BRING_TO_FOREGROUND:
      mPhone.internalBringToForeground(msg.arg1 == 1);
      break;

然后这里的mPhone对象是new出来的。

mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj), callingPackage,
        getApplicationContext().getApplicationInfo().targetSdkVersion);
mPhone.addListener(mPhoneListener);

同时,mPhone还设置了监听器,
mPhoneListener也是一个内部类对象,

前面internalBringToForeground

    final void internalBringToForeground(boolean showDialpad) {
        fireBringToForeground(showDialpad);
    }
    ---------
    private void fireBringToForeground(boolean showDialpad) {
        for (Listener listener : mListeners) {
            listener.onBringToForeground(this, showDialpad);
        }
    }

就是去通知所有监听器调用onBringToForeground
也就是InCallService里的Phone.Listener

	@Override
	public void onBringToForeground(Phone phone, boolean showDialpad) {
		InCallService.this.onBringToForeground(showDialpad);
	}

可是InCallService是个抽象父类

    public void onBringToForeground(boolean showDialpad) {
    }

于是需要看子类实现。

InCallServiceImpl extends InCallService
packages\apps\Dialer\java\com\android\incallui\InCallServiceImpl.java
这里InCallServiceImpl是Dialer的UI部分,到了这一步是回到了Dialer包里,

  @Override
  public void onBringToForeground(boolean showDialpad) {
    InCallPresenter.getInstance().onBringToForeground(showDialpad);
  }

然后就回到了InCallPresenter

前面讲过,InCallPresenter里检测状态后才调用了showInCall打开IncallUI。

  public void showInCall(boolean showDialpad, boolean newOutgoingCall) {
    mContext.startActivity(
        InCallActivity.getIntent(
            mContext, showDialpad, newOutgoingCall, false /* forFullScreen */));
  }

这样整个流程结束。


在这个流程里看到了很多设计模式,
比如
TelecomManager 只是TelecomService的代理。真实的服务又是TelecomServiceImpl。代理模式很明显。
Phone保存了List<Listener>,再用for循环通知所有Listener,观察者模式很明显。

然后就是跨进程通信了,有ITelecomService IInCallService 这两个AIDL接口的使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值