短信拒接流程

短信拒接设置是在

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
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。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值