描述:当手机已经在通话中的时候,界面回到桌面,再打开Dialer app的时候,手机就会提示并让你确认到底你是想打开已经在通话的界面还是不管正在通话中的电话而重新拨打新的通话。
假如此刻你想打开正在通话的界面,android的源码不是直接在DialtactsActivity
里startActivity
,而是经过系统通话服务最终启动。
这个过程里,代码从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 mBinderImpl
是ITelecomService
的实体,实现了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接口的使用。