Android8.0隱式廣播和自定義簽名權(quán)限

我思故我在

前言

記錄一下今天同事給我分享的比較有意思的Bug,在已有的已經(jīng)在AndroidManifest.xml中注冊(cè)的廣播在部分手機(jī)上無(wú)法通過(guò)Action隱式啟動(dòng)。上網(wǎng)搜搜資料自己寫(xiě)了個(gè)Demo,Mark一下

Android官網(wǎng):Oreo后臺(tái)執(zhí)行限制

我們這里主要看對(duì)于廣播的影響,摘抄一段官網(wǎng)上的介紹:

廣播限制

如果應(yīng)用注冊(cè)為接收廣播,則在每次發(fā)送廣播時(shí),應(yīng)用的接收器都會(huì)消耗資源。 如果多個(gè)應(yīng)用注冊(cè)為接收基于系統(tǒng)事件的廣播,則會(huì)引發(fā)問(wèn)題:觸發(fā)廣播的系統(tǒng)事件會(huì)導(dǎo)致所有應(yīng)用快速地連續(xù)消耗資源,從而降低用戶體驗(yàn)。 為了緩解這一問(wèn)題,Android 7.0(API 級(jí)別 24)對(duì)廣播施加了一些限制,如后臺(tái)優(yōu)化中所述。 Android 8.0(API 級(jí)別 26)讓這些限制更為嚴(yán)格。

  • 適配 Android 8.0 或更高版本的應(yīng)用無(wú)法繼續(xù)在其清單中為隱式廣播注冊(cè)廣播接收器。 隱式廣播是一種不專門(mén)針對(duì)該應(yīng)用的廣播。 例如,ACTION_PACKAGE_REPLACED 就是一種隱式廣播,因?yàn)樵搹V播將被發(fā)送給所有已注冊(cè)偵聽(tīng)器,讓后者知道設(shè)備上的某些軟件包已被替換。 不過(guò),ACTION_MY_PACKAGE_REPLACED 不是隱式廣播,因?yàn)椴还芤褳樵搹V播注冊(cè)偵聽(tīng)器的其他應(yīng)用有多少,它都會(huì)只被發(fā)送給軟件包已被替換的應(yīng)用。
  • 應(yīng)用可以繼續(xù)在它們的清單中注冊(cè)顯式廣播。
  • 應(yīng)用可以在運(yùn)行時(shí)使用 Context.registerReceiver() 為任意廣播(不管是隱式還是顯式)注冊(cè)接收器。
  • 需要簽名權(quán)限的廣播不受此限制所限,因?yàn)檫@些廣播只會(huì)發(fā)送到使用相同證書(shū)簽名的應(yīng)用,而不是發(fā)送到設(shè)備上的所有應(yīng)用。

在許多情況下,之前注冊(cè)隱式廣播的應(yīng)用使用 JobScheduler 作業(yè)可以獲得類似的功能。 例如,一款社交照片應(yīng)用可能需要不時(shí)地執(zhí)行數(shù)據(jù)清理,并且傾向于在設(shè)備連接到充電器時(shí)執(zhí)行此操作。 之前,應(yīng)用已經(jīng)在清單中為 ACTION_POWER_CONNECTED 注冊(cè)了一個(gè)接收器;當(dāng)應(yīng)用接收到該廣播時(shí),它會(huì)檢查清理是否必要。 為了遷移到 Android 8.0 或更高版本,應(yīng)用將該接收器從其清單中移除。 應(yīng)用將清理作業(yè)安排在設(shè)備處于空閑狀態(tài)和充電時(shí)運(yùn)行。

請(qǐng)注意:很多隱式廣播當(dāng)前已不受此限制所限。 應(yīng)用可以繼續(xù)在其清單中為這些廣播注冊(cè)接收器,不管應(yīng)用適配哪個(gè) API 級(jí)別。 有關(guān)已豁免廣播的列表,請(qǐng)參閱隱式廣播例外

更具上面的描述,我們可以得到一下幾點(diǎn):

  1. 適配Android 8.0或更高版本的應(yīng)用無(wú)法繼續(xù)在其清單中為隱式廣播注冊(cè)廣播接收器;
  2. 應(yīng)用可以繼續(xù)在它們的清單中注冊(cè)顯式廣播;
  3. 推薦運(yùn)行時(shí)使用Context.registerReceiver()注冊(cè)廣播;
  4. 需要簽名權(quán)限的廣播不受此約束;

自定義權(quán)限

Android官網(wǎng):permission

<permission android:description="string resource"
    android:icon="drawable resource"
    android:label="string resource"
    android:name="string"
    android:permissionGroup="string"
    android:protectionLevel=["normal" | "dangerous" |"signature" | ...] />

android:protectionLevel

說(shuō)明權(quán)限中隱含的潛在風(fēng)險(xiǎn),并指示系統(tǒng)在確定是否將權(quán)限授予請(qǐng)求授權(quán)的應(yīng)用時(shí)應(yīng)遵循的流程。

每個(gè)保護(hù)級(jí)別都包含基本權(quán)限類型以及零個(gè)或多個(gè)標(biāo)記。例如,dangerous保護(hù)級(jí)別沒(méi)有標(biāo)記。相反,保護(hù)級(jí)別 signature|privilegedsignature基本權(quán)限類型和privileged標(biāo)記的組合。

下表列出了所有基本權(quán)限類型。如需查看標(biāo)記列表,請(qǐng)參閱 protectionLevel

含義
normal 默認(rèn)值。具有較低風(fēng)險(xiǎn)的權(quán)限,此類權(quán)限允許請(qǐng)求授權(quán)的應(yīng)用訪問(wèn)隔離的應(yīng)用級(jí)功能,對(duì)其他應(yīng)用、系統(tǒng)或用戶的風(fēng)險(xiǎn)非常小。系統(tǒng)會(huì)自動(dòng)向在安裝時(shí)請(qǐng)求授權(quán)的應(yīng)用授予此類權(quán)限,無(wú)需征得用戶的明確許可(但用戶始終可以選擇在安裝之前查看這些權(quán)限)。
dangerous 具有較高風(fēng)險(xiǎn)的權(quán)限,此類權(quán)限允許請(qǐng)求授權(quán)的應(yīng)用訪問(wèn)用戶私人數(shù)據(jù)或獲取可對(duì)用戶造成不利影響的設(shè)備控制權(quán)。由于此類權(quán)限會(huì)帶來(lái)潛在風(fēng)險(xiǎn),因此系統(tǒng)可能不會(huì)自動(dòng)向請(qǐng)求授權(quán)的應(yīng)用授予此類權(quán)限。例如,應(yīng)用請(qǐng)求的任何危險(xiǎn)權(quán)限都可能會(huì)向用戶顯示并且獲得確認(rèn)才會(huì)繼續(xù)執(zhí)行操作,或者系統(tǒng)會(huì)采取一些其他方法來(lái)避免用戶自動(dòng)允許使用此類功能。
signature 只有在請(qǐng)求授權(quán)的應(yīng)用使用與聲明權(quán)限的應(yīng)用相同的證書(shū)進(jìn)行簽名時(shí)系統(tǒng)才會(huì)授予的權(quán)限。如果證書(shū)匹配,則系統(tǒng)會(huì)在不通知用戶或征得用戶明確許可的情況下自動(dòng)授予權(quán)限。
signatureOrSystem signature\privileged 的舊同義詞。在API級(jí)別23中已棄用。系統(tǒng)僅向位于Android系統(tǒng)映像的專用文件夾中的應(yīng)用或使用與聲明權(quán)限的應(yīng)用相同的證書(shū)進(jìn)行簽名的應(yīng)用授予的權(quán)限。不要使用此選項(xiàng),因?yàn)?signature 保護(hù)級(jí)別應(yīng)足以滿足大多數(shù)需求,無(wú)論應(yīng)用安裝在何處,該保護(hù)級(jí)別都能正常發(fā)揮作用。signatureOrSystem權(quán)限適用于以下特殊情況:多個(gè)供應(yīng)商將應(yīng)用內(nèi)置到一個(gè)系統(tǒng)映像中,并且需要明確共享特定功能,因?yàn)檫@些功能是一起構(gòu)建的。

自定義簽名權(quán)限并使用

<permission
        android:protectionLevel="signature"
        android:name="com.xx.xx.receiver" />
<uses-permission android:name="com.xx.xx.receiver"/>

測(cè)試

我寫(xiě)個(gè)Demo測(cè)試一下,測(cè)試機(jī)MI 8,系統(tǒng)為Android 10

聲明兩個(gè)Broadcast,一個(gè)帶權(quán)限,一個(gè)不帶權(quán)限。

public open class CustomReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        if(intent != null) {
            var param = intent.getStringExtra("message")
            var result:String?
            result = String.format("get the receiver for %s", param)
            XToast.show(result)
        }
    }
}
public class CustomReceiver2 : CustomReceiver() {}
<!--帶簽名權(quán)限的廣播-->
<receiver android:name=".main.receiver.CustomReceiver"
    android:permission="com.xx.xx.receiver">
    <intent-filter>
        <action android:name="com.xx.xx.message"/>
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</receiver>
<!--普通的廣播-->
<receiver android:name=".main.receiver.CustomReceiver2">
    <intent-filter>
        <action android:name="com.xx.xx.message2"/>
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</receiver>
  1. 發(fā)送權(quán)限隱式廣播-不加權(quán)限

var intent = Intent("com.xx.xx.message" + num++)
intent.putExtra("message","custom test" + num++)
sendBroadcast(intent)

結(jié)果:吐司無(wú)法展現(xiàn)。

15:59:23.420#1635#1758#W#BroadcastQueue #Background execution not allowed: receiving Intent { act=com.xx.xx.message flg=0x10 (has extras) } to com.xx.xx.demo/.main.receiver.CustomReceiver
  1. 發(fā)送權(quán)限隱式廣播-加權(quán)限

var intent = Intent("com.xx.xx.message")
intent.putExtra("message","custom test")
sendOrderedBroadcast(intent, "com.xx.xx.receiver")

結(jié)果:吐司展現(xiàn)。

  1. 發(fā)送權(quán)限隱式廣播-加權(quán)限-錯(cuò)誤

var intent = Intent("com.xx.xx.message")
intent.putExtra("message","custom test")
sendOrderedBroadcast(intent, "com.xx.xx.receiver.error")

結(jié)果:吐司無(wú)法展現(xiàn)。

15:44:54.514#1696#1774#W#BroadcastQueue #Permission Denial: receiving Intent { act=com.xx.xx.message flg=0x1000010 (has extras) } to com.xx.xx.ztemplate/.main.receiver.CustomReceiver requires com.xx.xx.receiver.error due to sender com.xx.xx.demo (uid 10547)
  1. 發(fā)送隱式廣播

var intent = Intent("com.xx.xx.message2")
intent.putExtra("message","custom test")
sendBroadcast(intent)

結(jié)果:吐司無(wú)法展現(xiàn)。

15:59:23.420#1635#1758#W#BroadcastQueue #Background execution not allowed: receiving Intent { act=com.xx.xx.message flg=0x10 (has extras) } to com.xx.xx.demo/.main.receiver.CustomReceiver
  1. 發(fā)送隱式廣播-添加package

var intent = Intent("com.xx.xx.message2")
intent.`package` = "com.xx.xx.demo"
intent.putExtra("message","custom test")
sendBroadcast(intent)

結(jié)果:吐司展現(xiàn)。

  1. 發(fā)送隱式廣播-setClass(等同于添加component)

var intent = Intent("com.xx.xx.message2")
intent.setClass(this, CustomReceiver2::class.java)
intent.putExtra("message","custom test")
sendBroadcast(intent)

結(jié)果:吐司展現(xiàn)。

其實(shí)第5和第6個(gè)case已經(jīng)不算隱式廣播了,他們都為Intent設(shè)置了package指明了當(dāng)前的環(huán)境。

錯(cuò)誤分析

BroadcastQueue #Permission Denial:

BroadcastQueue-permission-denial.png

這里提示權(quán)限有問(wèn)題,需要添加或修改權(quán)限。

BroadcastQueue #Background execution not allowed:

BroadcastQueue-not-allow.png
//android.content.Intent.java
public static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 0x01000000;
public static final int FLAG_RECEIVER_EXCLUDE_BACKGROUND = 0x00800000;

這個(gè)代碼塊有一個(gè)||操作,我們不想讓其進(jìn)入到改邏輯需要使前面判斷為false,后面判斷為false

判斷r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND 中攜帶了FLAG_RECEIVER_EXCLUDE_BACKGROUND標(biāo)志位。我們一般都不會(huì)攜帶,所以前面邏輯為false

后面邏輯有三個(gè)&&操作,那么只需要讓其中一個(gè)為false即可。

  1. r.intent.getComponent() == null, 會(huì)進(jìn)入此邏輯(設(shè)置component)。

  2. r.intent.getPackage() == null,會(huì)進(jìn)入此邏輯(設(shè)置component)。

  3. r.intent.getFlags() & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0 不能帶有FLAG_RECEIVER_INCLUDE_BACKGROUND這個(gè)標(biāo)志位,否則會(huì)進(jìn)入此邏輯(設(shè)置FLAG_RECEIVER_INCLUDE_BACKGROUND)。

  4. 如果啟動(dòng)廣播的時(shí)候攜帶了權(quán)限,那么如果不是簽名權(quán)限會(huì)進(jìn)入此邏輯(設(shè)置簽名權(quán)限)。

其實(shí)12我們上面已經(jīng)測(cè)試過(guò)了(第5個(gè)和第6個(gè)case);

3設(shè)置Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,但改常量是hide無(wú)法通過(guò)Intent訪問(wèn)。我們只能寫(xiě)0x01000000,但不建議這么做;

4其實(shí)就是文檔中說(shuō)明的簽名權(quán)限不受Android 8.0后臺(tái)執(zhí)行優(yōu)化的控制;

文章到這里就全部講述完啦,若有其他需要交流的可以留言哦

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容