短信拒接设置是在
packages/services/Telecomm/src/com/android/server/telecom/RespondViaSmsSettings.java
而通话拒接的UI显示是在InCallUI中的,那么信息内容是怎么跨进程传递的呢,本文分析下这个流程
RespondViaSmsSettings
布局文件是respond_via_sms_settings.xml
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/respond_via_sms_setting_title_2">
<com.android.server.telecom.MultiLineTitleEditTextPreference
android:key="canned_response_pref_1"
android:defaultValue="@string/respond_via_sms_canned_response_1"
android:dialogTitle="@string/respond_via_sms_edittext_dialog_title"
android:persistent="false" />
<com.android.server.telecom.MultiLineTitleEditTextPreference
android:key="canned_response_pref_2"
android:defaultValue="@string/respond_via_sms_canned_response_2"
android:dialogTitle="@string/respond_via_sms_edittext_dialog_title"
android:persistent="false" />
<com.android.server.telecom.MultiLineTitleEditTextPreference
android:key="canned_response_pref_3"
android:defaultValue="@string/respond_via_sms_canned_response_3"
android:dialogTitle="@string/respond_via_sms_edittext_dialog_title"
android:persistent="false" />
<com.android.server.telecom.MultiLineTitleEditTextPreference
android:key="canned_response_pref_4"
android:defaultValue="@string/respond_via_sms_canned_response_4"
android:dialogTitle="@string/respond_via_sms_edittext_dialog_title"
android:persistent="false" />
</PreferenceScreen>
布局很简单,就是4条短信拒接的EditTextPreference,可以编辑但是没有增加或者删除的功能
public boolean onPreferenceChange(Preference preference, Object newValue) {
EditTextPreference pref = (EditTextPreference) preference;
pref.setTitle((String) newValue);
SharedPreferences.Editor editor = mPrefs.edit();
editor.putString(pref.getKey(), (String) newValue).commit();
return true; // means it's OK to update the state of the Preference with the new value
}
使用SharedPreference保存设置项目
QuickResponseUtils
packages/services/Telecomm/src/com/android/server/telecom/QuickResponseUtils.java
只有一个方法
public static void maybeMigrateLegacyQuickResponses(Context context)
它的作用是把Telephony包的设置值导入到Telecomm包下,这个是因为原先这个设置是在Telephony包下的。是针对升级Android版本的手机。
RespondViaSmsManager
packages/services/Telecomm/src/com/android/server/telecom/RespondViaSmsManager.java
public void loadCannedTextMessages(final Response<Void, List<String>> response,
final Context context) {
new Thread() {
@Override
public void run() {
QuickResponseUtils.maybeMigrateLegacyQuickResponses(context);
final SharedPreferences prefs = context.getSharedPreferences(
QuickResponseUtils.SHARED_PREFERENCES_NAME,
Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);
final Resources res = context.getResources();
final ArrayList<String> textMessages = new ArrayList<>(
QuickResponseUtils.NUM_CANNED_RESPONSES);
textMessages.add(0, prefs.getString(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_1,
res.getString(R.string.respond_via_sms_canned_response_1)));
textMessages.add(1, prefs.getString(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_2,
res.getString(R.string.respond_via_sms_canned_response_2)));
textMessages.add(2, prefs.getString(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_3,
res.getString(R.string.respond_via_sms_canned_response_3)));
textMessages.add(3, prefs.getString(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_4,
res.getString(R.string.respond_via_sms_canned_response_4)));
synchronized (mLock) {
response.onResult(null, textMessages);
}
}
}.start();
}
读取短信内容,在回调方法onResult中传给调用方
还有个方法是拒接功能的实现:
@Override
public void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage) {
if (rejectWithMessage && call.getHandle() != null) {
int subId = mCallsManager.getPhoneAccountRegistrar().getSubscriptionIdForPhoneAccount(
call.getTargetPhoneAccount());
rejectCallWithMessage(call.getContext(), call.getHandle().getSchemeSpecificPart(),
textMessage, subId);
}
}
RespondViaSmsManager会监听短信拒接的事件,完成拒接短信的发送。
短信内容上报流程
packages/services/Telecomm/src/com/android/server/telecom/Call.java
public Call(
Context context,
CallsManager callsManager,
TelecomSystem.SyncRoot lock,
ConnectionServiceRepository repository,
ContactsAsyncHelper contactsAsyncHelper,
CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
Uri handle,
GatewayInfo gatewayInfo,
PhoneAccountHandle connectionManagerPhoneAccountHandle,
PhoneAccountHandle targetPhoneAccountHandle,
boolean isIncoming,
boolean isConference) {
...
maybeLoadCannedSmsResponses();
...
}
构造方法中调用maybeLoadCannedSmsResponses
private void maybeLoadCannedSmsResponses() {
if (mIsIncoming && isRespondViaSmsCapable() && !mCannedSmsResponsesLoadingStarted) {
mCannedSmsResponsesLoadingStarted = true;
mCallsManager.getRespondViaSmsManager().loadCannedTextMessages(
new Response<Void, List<String>>() {
@Override
public void onResult(Void request, List<String>... result) {
if (result.length > 0) {
mCannedSmsResponses = result[0];
for (Listener l : mListeners) {
l.onCannedSmsResponsesLoaded(Call.this);
}
}
}
...
},
mContext
);
} else {
Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
}
}
使用RespondViaSmsManager获取短信内容,在回调方法中通知在监听的对象
packages/services/Telecomm/src/com/android/server/telecom/InCallController.java
private final Call.Listener mCallListener = new Call.ListenerBase() {
...
@Override
public void onCannedSmsResponsesLoaded(Call call) {
updateCall(call);
}
...
}
private void updateCall(Call call, boolean videoProviderChanged) {
...
inCallService.updateCall(parcelableCall);
...
}
frameworks/base/telecomm/java/android/telecom/InCallService.java
这里的InCallService对象的实现端其实就是packages/apps/InCallUI/src/com/android/incallui/InCallServiceImpl.java,那么从这里开始已经从telecom的进程(实际是system进程)切换到了incallui的进程(实际是com.android.dialer进程)
private final class InCallServiceBinder extends IInCallService.Stub {
...
@Override
public void updateCall(ParcelableCall call) {
mHandler.obtainMessage(MSG_UPDATE_CALL, call).sendToTarget();
}
...
}
消息处理中:
case MSG_UPDATE_CALL:
mPhone.internalUpdateCall((ParcelableCall) msg.obj);
frameworks/base/telecomm/java/android/telecom/Phone.java
final void internalUpdateCall(ParcelableCall parcelableCall) {
Call call = mCallByTelecomCallId.get(parcelableCall.getId());
if (call != null) {
checkCallTree(parcelableCall);
call.internalUpdate(parcelableCall, mCallByTelecomCallId);
}
}
frameworks/base/telecomm/java/android/telecom/Call.java
final void internalUpdate(ParcelableCall parcelableCall, Map<String, Call> callIdMap) {
...
boolean cannedTextResponsesChanged = false;
if (mCannedTextResponses == null && parcelableCall.getCannedSmsResponses() != null
&& !parcelableCall.getCannedSmsResponses().isEmpty()) {
mCannedTextResponses =
Collections.unmodifiableList(parcelableCall.getCannedSmsResponses());
///M: Text response should be updated
cannedTextResponsesChanged = true;
}
...
if (cannedTextResponsesChanged) {
fireCannedTextResponsesLoaded(mCannedTextResponses);
}
...
}
private void fireCannedTextResponsesLoaded(final List<String> cannedTextResponses) {
for (CallbackRecord<Callback> record : mCallbackRecords) {
final Call call = this;
final Callback callback = record.getCallback();
record.getHandler().post(new Runnable() {
@Override
public void run() {
callback.onCannedTextResponsesLoaded(call, cannedTextResponses);
}
});
}
}
那么这个mCallBackRecords是在registerCallback中添加的
public void registerCallback(Callback callback, Handler handler) {
unregisterCallback(callback);
// Don't allow new callback registration if the call is already being destroyed.
if (callback != null && handler != null && mState != STATE_DISCONNECTED) {
mCallbackRecords.add(new CallbackRecord<Callback>(callback, handler));
}
}
这个方法又是谁调用的?
packages/apps/InCallUI/src/com/android/incallui/Call.java
public Call(android.telecom.Call telecommCall) {
...
mTelecommCall.registerCallback(mTelecomCallCallback);
...
}
构造方法中注册了回调,mTelecomCallCallBack定义如下:
private android.telecom.Call.Callback mTelecomCallCallback =
new android.telecom.Call.Callback() {
...
@Override
public void onCannedTextResponsesLoaded(android.telecom.Call call,
List<String> cannedTextResponses) {
update();
}
...
}
然后update后续继续往上通知,后续的流程可见
点击打开链接
短信拒接操作的下发流程
packages/apps/InCallUI/src/com/android/incallui/AnswerPresenter.java
public void rejectCallWithMessage(String message) {
TelecomAdapter.getInstance().rejectCall(mCall.getId(), true, message);
if (mCall.getHandle() != null && mCall.can(android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT)) {
TelecomAdapter.getInstance().sendMessageIfCallEnded(getUi().getContext(),
mCall.getId(), mCall.getHandle().getSchemeSpecificPart(), message);
}
...
}
命令有两个,一个是挂断电话,一个是发送短信。
packages/apps/InCallUI/src/com/android/incallui/TelecomAdapter.java
void sendMessageIfCallEnded(Context context, String callId, String phoneNumber,
String textMessage) {
...
}
sendMessageIfCallEnded是mtk新加的方法,和RespondViaSmsManager的发送短信代码是一样的,从代码看是重复的,原生代码没有这个,不知是mtk的什么特殊设计。
public void rejectCall(String callId, boolean rejectWithMessage, String message) {
android.telecom.Call call = getTelecommCallById(callId);
if (call != null) {
call.reject(rejectWithMessage, message);
} else {
Log.e(this, "error rejectCall, call not in call list: " + callId);
}
}
到android.telecom.Call中
public void reject(boolean rejectWithMessage, String textMessage) {
mInCallAdapter.rejectCall(mTelecomCallId, rejectWithMessage, textMessage);
}
这里的mInCallAdapter的实现端就是packages/services/Telecomm/src/com/android/server/telecom/InCallAdapter.java,这里完成了incall进程到telecom进程的切换
@Override
public void rejectCall(String callId, boolean rejectWithMessage, String textMessage) {
...
mCallsManager.rejectCall(call, rejectWithMessage, textMessage);
...
}
packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java
void rejectCall(Call call, boolean rejectWithMessage, String textMessage) {
...
for (CallsManagerListener listener : mListeners) {
listener.onIncomingCallRejected(call, rejectWithMessage, textMessage);
}
call.reject(rejectWithMessage, textMessage);
...
}
packages/services/Telecomm/src/com/android/server/telecom/Call.java
void reject(boolean rejectWithMessage, String textMessage) {
...
mConnectionService.reject(this);
...
}
packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java
void reject(Call call) {
...
mServiceInterface.reject(callId);
...
}
这里mServiceInterface的实现端在frameworks/base/telecomm/java/android/telecom/ConnectionService.java中,实例对象就是
packages/services/Telephony/src/com/android/services/telephony/TelephonyConnectionService.java,这里就完成了telecom所在进程到com.android.phone进程的切换
private final IBinder mBinder = new IConnectionService.Stub() {
...
@Override
public void reject(String callId) {
mHandler.obtainMessage(MSG_REJECT, callId).sendToTarget();
}
...
}
消息处理
case MSG_REJECT:
reject((String) msg.obj);
private void reject(String callId) {
findConnectionForAction(callId, "reject").onReject();
}
frameworks/base/telecomm/java/android/telecom/Connection.java,实现该方法的是packages/services/Telephony/src/com/android/services/telephony/TelephonyConnection.java
@Override
public void onReject() {
Log.v(this, "onReject");
if (isValidRingingCall()) {
hangup(android.telephony.DisconnectCause.INCOMING_REJECTED);
}
super.onReject();
}
protected void hangup(int telephonyDisconnectCode) {
...
call.hangup();
...
}
这里的call已经是telephony framework中的Call对象了,frameworks/opt/telephony/src/java/com/android/internal/telephony/Call.java,不继续分析了。
Telephony上报到Telecom的流程
分析了Telecom和InCallUI的交互,还有Telecom下发命令到Telephony,只有Telephony上报消息到Telecom没有分析。关键文件是frameworks/base/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl,通过它进行数据的上报。下面拿挂断事件上报讲述这个流程
packages/services/Telephony/src/com/android/services/telephony/TelephonyConnection.java
void setOriginalConnection(com.android.internal.telephony.Connection originalConnection) {
...
getPhone().registerForDisconnect(mHandler, MSG_DISCONNECT, null);
...
}
注册了监听挂断事件
case MSG_DISCONNECT:
...
updateState();
...
break;
void updateState(boolean force) {
...
case DISCONNECTED:
...
setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
mOriginalConnection.getDisconnectCause(),
mOriginalConnection.getVendorDisconnectCause()));
...
}
frameworks/base/telecomm/java/android/telecom/Connection.java
public final void setDisconnected(DisconnectCause disconnectCause) {
checkImmutable();
mDisconnectCause = disconnectCause;
setState(STATE_DISCONNECTED);
for (Listener l : mListeners) {
l.onDisconnected(this, disconnectCause);
}
}
这里的mListeners中有frameworks/base/telecomm/java/android/telecom/ConnectionService.java中定义的一个
private final Connection.Listener mConnectionListener = new Connection.Listener() {
...
@Override
public void onDisconnected(Connection c, DisconnectCause disconnectCause) {
String id = mIdByConnection.get(c);
Log.d(this, "Adapter set disconnected %s", disconnectCause);
mAdapter.setDisconnected(id, disconnectCause);
}
...
}
mAdapter是
private final ConnectionServiceAdapter mAdapter = new ConnectionServiceAdapter();
这个类其实就是IConnectionServiceAdapter的封装,通过它就跳转到了Telecom所在进程。这个实现类是packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java
public void setDisconnected(String callId, DisconnectCause disconnectCause) {
...
mCallsManager.markCallAsDisconnected(call, disconnectCause);
...
}
然后telecom再一步步上报,后续不再分析。
总结
其实本文主要讨论的还是Telcomm,InCallUI和Telephony怎么进程间通信的流程。
Telephony到Telecomm
frameworks/base/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
frameworks/base/telecomm/java/android/telecom/ConnectionServiceAdapter.java
packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java
ConnectionServiceWrapper中有IConnectionServiceAdapter的实现,ConnectionServiceAdapter是使用IConnectionServiceAdapter的辅助类
Telecomm到Telephony
frameworks/base/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
frameworks/base/telecomm/java/android/telecom/ConnectionService.java
packages/services/Telephony/src/com/android/services/telephony/TelephonyConnectionService.java
ConnectionService继承Service并有IConnectionService的实现,TelephonyConnectionService继承ConnectionService
InCallUI到Telecomm
frameworks/base/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
frameworks/base/telecomm/java/android/telecom/InCallAdapter.java
frameworks/base/telecomm/java/android/telecom/InCallAdapter.java
packages/services/Telecomm/src/com/android/server/telecom/InCallAdapter.java
frameworks中的InCallAdapter是使用IInCallAdapter的辅助类,app中的是IInCallAdapter的实现
Telecomm到InCallUI
frameworks/base/telecomm/java/com/android/internal/telecom/IInCallService.aidl
frameworks/base/telecomm/java/android/telecom/InCallService.java
packages/apps/InCallUI/src/com/android/incallui/InCallServiceImpl.java
InCallService继承Service,其中InCallServiceBinder实现了IInCallService。InCallServiceImpl继承InCallService
可以看出Telecom与InCallUI,Telecom与Telephony有某种对称的关系,Telecom到另外两个模块是通过xxxService,其它两个模块到Telecom是通过xxxAdapter。