androidの短彩信的接收流程深入分析(framework)

2023-10-12 05:50

本文主要是介绍androidの短彩信的接收流程深入分析(framework),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

短彩信的接收流程


涉及的文件

[plain]  view plain copy
  1. com.android.internal.telephony/Ril.java  
  2. com.android.internal.telephony/SMSDispatcher  
  3. com.android.internal.telephony/CommandsInterface  
  4. com.android.internal.telephony/GsmSMSDispatcher  
  5. com.android.internal.telephony/CdmanSMSDispatcher  
  6. com.android.internal.telephony/ImsSMSDispatcher  
  7. hardware/ril/libril/ril.cpp  
  8. com.android.mms.transaction/PrivilegedSmsReceiver  

流程分析

时序图

 

android ril java层接收短息的流程

1)监听底层上报的数据

在Ril.java中定义了一个receive的框架:当接收到短信时,底层首先通过rild将接收到的短信通过socket传送给Ril.java,那大家都知道这个短信接收是一个不定时的,所以就必须有一个监听器一直监视这个socket一旦有短信就触发其相应的操作。那Ril.java是否有定义这样的一个监听器或者类似的功能了?(补充:这涉及一个Modem端与rild的通信,rild是一个守护进程,是整个android ril层的入口点,

通过查看代码我们可以发现RILReceiver的内部类:

[html]  view plain copy
  1. class RILReceiver implements Runnable {  
  2.         byte[] buffer;  
  3.         RILReceiver() {  
  4.             buffer = new byte[RIL_MAX_COMMAND_BYTES];  
  5.         }  
  6.         public void  
  7.         run() {  
  8.             int retryCount = 0;  
  9.             String rilSocket = "rild";  
  10.   
  11.             try {for (;;) {  
  12.                 LocalSocket s = null;  
  13.                 LocalSocketAddress l;  
  14.   
  15.                 boolean multiRild = SystemProperties.getBoolean("ro.multi.rild", false);  
  16.   
  17.                 if (mInstanceId == 0 || multiRild == false) {  
  18.                     rilSocket = SOCKET_NAME_RIL;  
  19.                 } else {  
  20.                     rilSocket = SOCKET_NAME_RIL1;  
  21.                 }  
  22.                 try {  
  23.                     s = new LocalSocket();  
  24.                     l = new LocalSocketAddress(rilSocket,  
  25.                             LocalSocketAddress.Namespace.RESERVED);  
  26.                     s.connect(l);  
  27.                 }   
  28.                    if (retryCount == 8) {  
  29.                         Log.e (LOG_TAG,  
  30.                             "Couldn't find '" + rilSocket  
  31.                             + "' socket after " + retryCount  
  32.                             + " times, continuing to retry silently");  
  33.                     } else if (retryCount > 0 && retryCount < 8) {  
  34.                         Log.i (LOG_TAG,  
  35.                             "Couldn't find '" + rilSocket  
  36.                             + "' socket; retrying after timeout");  
  37.                     }  
  38.                     try {  
  39.                         Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);  
  40.                     } catch (InterruptedException er) {  
  41.                     }  
  42.                     retryCount++;  
  43.                     continue;  
  44.                 }  
  45.                 retryCount = 0;  
  46.                 mSocket = s;  
  47.                 Log.i(LOG_TAG, "Connected to '" + rilSocket + "' socket");  
  48.                 int length = 0;  
  49.                 try {  
  50.                     InputStream is = mSocket.getInputStream();  
  51.                     for (;;) {  
  52.                         Parcel p;  
  53.                         length = readRilMessage(is, buffer);  
  54.                         if (length < 0) {  
  55.                             // End-of-stream reached  
  56.                             break;  
  57.                         }  
  58.                         p = Parcel.obtain();  
  59.                         p.unmarshall(buffer, 0, length);  
  60.                         p.setDataPosition(0);  
  61. processResponse(p);  
  62.               p.recycle(); }  

2)对底层上报的数据进行处理从上面的代码看出这个线程一直和守护进程rild进行sorcket通信,并获取守护进程上报的数据。可以看出做了很多的工作但是最重要的是processResponse()方法向上汇报数据,下面是其处理代码:

[html]  view plain copy
  1. <p>  
  2.      private void  
  3.     processResponse (Parcel p) {  
  4.         int type;  
  5.   
  6.         type = p.readInt();  
  7.   
  8.         if (type == RESPONSE_UNSOLICITED) {  
  9.             processUnsolicited (p);  
  10.         } else if (type == RESPONSE_SOLICITED) {  
  11.             processSolicited (p);  
  12.         }  
  13.   
  14.         releaseWakeLockIfDone();  
  15.     }</p>  
可以看出在上报数据进行了分类处理,RESPONSE_UNSOLICITED表示接收到数据就直接上报的类型,主动上报,如网络状态和短信、来电等等。RESPONSE_SOLICITED是必须先请求然后才响应的类型。当然这里是短信接收肯定会走前者。processUnsolicited该方法会根据当前的请求的类型,如果是短信则是RIL_UNSOL_RESPONSE_NEW_SMS,以下是其调用的代码;
[html]  view plain copy
  1. case RIL_UNSOL_RESPONSE_NEW_SMS: {  
  2.                if (RILJ_LOGD) unsljLog(response);  
  3.   
  4.                // FIXME this should move up a layer  
  5.                String a[] = new String[2];  
  6.   
  7.                a[1] = (String)ret;  
  8.   
  9.                SmsMessage sms;  
  10.   
  11.                sms = SmsMessage.newFromCMT(a);  
  12.                if (mSMSRegistrant != null) {  
  13.                    mSMSRegistrant  
  14.                        .notifyRegistrant(new AsyncResult(null, sms, null));  
  15.                }  
  16.            break;  
  17.            }  

3)追溯该方法,mSMSRegistrant对象的创建过程mSMSRegistrant是BaseCommands的成员变量,且在调用setOnNewSMS()方法来赋值的,BaseCommands是干什么的有的童鞋会问,我们看一下Ril.java的继承关系就知道了,Ril.java是它的子类。了解了这些童鞋肯定会问那谁又调用了setOnNewSMS方法?以下是该流程的时序图。

 最后发现该方法设置handler的源头是在GsmSMSDispatcher类里,但最后会调用SmsDispatcher的handMessage方法。原因是GsmSMSDispatcher是SmsDispatcher的子类而且GsmSMSDispatcher没有复写handMessage方法,所以接收到消息后肯定由父类的handMessage方法来处理。

到此为止android的ril java层走完了,剩余的就交给中间层慢慢去做。

注意:

1)这是2.3的代码,对比一下4.0的代码可以发现GsmSMSDispatcher复写了handMessage的方法,自己会去处理,但是仅限于EVENT_NEW_SMS_STATUS_REPORT、EVENT_NEW_BROADCAST_SMS、EVENT_WRITE_SMS_COMPLETE其余的事件仍由父类SmsDispatcher来完成。

2)4.0中没有setOnNewSMS该方法,替换成了setOnNewGsmSms、setOnNewCdmaSms方法,对应于不同类型的phone做自己的注册。

framework SMSDispatcher接收短信后的处理流程

中间层SMSDispatcher处理流程:

1)该类做了一件重要的事:

给CommandInterface设置handler的处理方法,就是当接收到短信后触发mSMSRegistrant.notifyRegistrant(new AsyncResult(null, sms, null));方法

然后回调调用之前传入的handler,接着在handler里面处理短信消息。

2)handler处理接收到的短信消息:

[html]  view plain copy
  1. @Override     
  2.     public void handleMessage(Message msg) {  
  3.   
  4.         AsyncResult ar;  
  5.   
  6.         switch (msg.what) {  
  7.   
  8.         case EVENT_NEW_SMS:  
  9.   
  10.             // A new SMS has been received by the device  
  11.   
  12.             if (Config.LOGD) {  
  13.   
  14.                 Log.d(TAG, "New SMS Message Received");  
  15.   
  16.             }  
  17.   
  18.             SmsMessage sms;  
  19.   
  20.             ar = (AsyncResult) msg.obj;  
  21.   
  22.             if (ar.exception != null) {  
  23.   
  24.                 Log.e(TAG, "Exception processing incoming SMS. Exception:"     
  25.                         + ar.exception);  
  26.   
  27.                 return;  
  28.   
  29.             }  
  30.   
  31.             sms = (SmsMessage) ar.result;  
  32.   
  33.             try {  
  34.   
  35.                 int result = dispatchMessage(sms.mWrappedSmsMessage);  
  36.   
  37.                 if (result != Activity.RESULT_OK) {  
  38.   
  39.                     // RESULT_OK means that message was broadcast for app(s) to     
  40.                     // handle.  
  41.   
  42.                     // Any other result, we should ack here.  
  43.   
  44.                     boolean handled = (result == Intents.RESULT_SMS_HANDLED);  
  45.   
  46.                     notifyAndAcknowledgeLastIncomingSms(handled, result, null);  
  47.   
  48.                 }  
  49.   
  50.             } catch (RuntimeException ex) {  
  51.   
  52.                 Log.e(TAG, "Exception dispatching message", ex);  
  53.   
  54.                 notifyAndAcknowledgeLastIncomingSms(false,     
  55.                         Intents.RESULT_SMS_GENERIC_ERROR, null);  
  56.   
  57.             }  
  58.   
  59.             break;  
  60.   
  61.         }  
  62.   
  63.     }  

 说明:

int result = dispatchMessage(sms.mWrappedSmsMessage);

该段会通过子类(GsmSMSDispatcher)的dispatchMessage方法处理。
经一系列的判断处理最后普通短信将交给dispatchPdus(pdus);这个方法处理。

[html]  view plain copy
  1. protected void dispatchPdus(byte[][] pdus) {  
  2. Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION);  
  3. intent.putExtra("pdus", pdus);  
  4. dispatch(intent, "android.permission.RECEIVE_SMS");  
  5. }  
  6.   
  7. void dispatch(Intent intent, String permission) {  
  8. // Hold a wake lock for WAKE_LOCK_TIMEOUT seconds, enough to give any  
  9. // receivers time to take their own wake locks.  
  10. mWakeLock.acquire(WAKE_LOCK_TIMEOUT);  
  11. mContext.sendOrderedBroadcast(intent, permission, mResultReceiver,  
  12. this, Activity.RESULT_OK, null, null);  
  13. }  

3)我们可以看出这个方法将短信通过顺序广播播放出去(action是SMS_RECEIVED_ACTION),无论广播是否被中断最后都会调用mResultReceiver,这里会将已读或未读的状态告诉给对方。如果短信广播中间没有受到終止。然后短信app中PrivilegedSmsReceiver广播接收器接收到广播后会调用onReceiveWithPrivilege方法,由于PrivilegedSmsReceiver继承与SmsReceiver,所以会调用父类的该方法。其代码:

[html]  view plain copy
  1. protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) {
  2.         // If 'privileged' is false, it means that the intent was delivered to the base  
  3.         // no-permissions receiver class.  If we get an SMS_RECEIVED message that way, it  
  4.         // means someone has tried to spoof the message by delivering it outside the normal
  5.         // permission-checked route, so we just ignore it.  
  6.         if (!privileged && (intent.getAction().equals(Intents.SMS_RECEIVED_ACTION)  
  7.                 || intent.getAction().equals(Intents.SMS_CB_RECEIVED_ACTION))) {  
  8.             return;  
  9.         }  
  10.   
  11.         intent.setClass(context, SmsReceiverService.class);  
  12.         intent.putExtra("result", getResultCode());  
  13.         beginStartingService(context, intent);  
  14.     }  

最后交由SmsReceiverService该service去处理,到此为止已经到应用层Mms。

彩信的接收
可能有些童鞋看到这明白了短信的接收可能会问到那彩信的接收又会使什么样的了,为了解惑所以在此简单描述彩信接收的过程,由于很多和短信相似。这个过程了就画一个时序图个大家,注意:这直到应用层,应用层的具体实现会有专门的文章来讲述。在这以GSM为例,以下是其时序图:

说明:大家可以看出和短信的接收来他们的区别在于dispatchMessage方法会根据smsHeader.portAddrs来判断当前是彩信还是短信,然后调用对应的方法。

总结

   这里没有去分析rild是怎么和ril.java 建立socket通信,也没有讲rild怎么和moderm端进行通信的,这些我会继续研究,希望尽早分享给大家。


这篇关于androidの短彩信的接收流程深入分析(framework)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/193705

相关文章

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Android 10.0 mtk平板camera2横屏预览旋转90度横屏拍照图片旋转90度功能实现

1.前言 在10.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的 时候,默认预览图像也是需要横屏显示的,在上一篇已经实现了横屏预览功能,然后发现横屏预览后,拍照保存的图片 依然是竖屏的,所以说同样需要将图片也保存为横屏图标了,所以就需要看下mtk的camera2的相关横屏保存图片功能, 如何实现实现横屏保存图片功能 如图所示: 2.mtk

android应用中res目录说明

Android应用的res目录是一个特殊的项目,该项目里存放了Android应用所用的全部资源,包括图片、字符串、颜色、尺寸、样式等,类似于web开发中的public目录,js、css、image、style。。。。 Android按照约定,将不同的资源放在不同的文件夹中,这样可以方便的让AAPT(即Android Asset Packaging Tool , 在SDK的build-tools目

Android fill_parent、match_parent、wrap_content三者的作用及区别

这三个属性都是用来适应视图的水平或者垂直大小,以视图的内容或尺寸为基础的布局,比精确的指定视图的范围更加方便。 1、fill_parent 设置一个视图的布局为fill_parent将强制性的使视图扩展至它父元素的大小 2、match_parent 和fill_parent一样,从字面上的意思match_parent更贴切一些,于是从2.2开始,两个属性都可以使用,但2.3版本以后的建议使

Spring Framework系统框架

序号表示的是学习顺序 IoC(控制反转)/DI(依赖注入): ioc:思想上是控制反转,spring提供了一个容器,称为IOC容器,用它来充当IOC思想中的外部。 我的理解就是spring把这些对象集中管理,放在容器中,这个容器就叫Ioc这些对象统称为Bean 用对象的时候不用new,直接外部提供(bean) 当外部的对象有关系的时候,IOC给它俩绑好(DI) DI和IO