android:sharedUserId

一、前言

Android給每一個APK進程分配一個單獨的空間,manifest中的userid就是對應一個分配的Linux用戶ID,而且爲它建立一個沙箱,以防止影響其餘應用程序(或者被其餘應用程序影響)。java

一般,不一樣的APK會具備不一樣的userId,所以運行時屬於不一樣的進程中,而不一樣進程中的資源是不共享的(好比只能訪問/data/data/本身包名下面的文件),保障了程序運行的穩定。而後在有些時候,咱們本身開發了多個APK而且須要他們之間互相共享資源,那麼就須要經過設置shareUserId來實現這一目的。android

經過Shared User id,擁有同一個User id的多個APK能夠配置成運行在同一個進程中,能夠互相訪問任意數據。也能夠配置成運行成不一樣的進程, 同時能夠訪問其餘APK的數據目錄下的數據庫和文件,就像訪問本程序的數據同樣(使用IPC機制,不一樣進程之間,好比AIDL)。

數據庫

二、shareUserId的屬性的最大做用是什麼呢?

前面說了,Android中每一個app都對應一個uid,每一個uid都有本身的一個沙箱,這是基於安全考慮的,那麼說到沙箱,咱們會想到的是data/data/XXXX/目錄下面的全部數據,由於咱們知道這個目錄下面的全部數據是一個應用私有的,通常狀況下其餘應用是沒有權限訪問的,固然root以後是另外狀況,這裏就很少說了。這裏只看沒有root的狀況,下面咱們在來看一個場景:安全

A應用和B應用都是一家公司的,如今想在A應用中可以拿到B應用存儲的一些值,那麼這時候該怎麼辦呢?app

這時候就須要用到了shareUserId屬性了,可是這裏咱們在介紹shareUserId屬性前,咱們先來看一個簡單的例子:ui

仍是使用上面的兩個工程:google

ShareUserIdPlugin中的MainActivity.java代碼以下:插件

這裏很簡單,咱們使用SharedPreferences來存儲一個密碼,注意模式是:Context.MODE_PRIVATE,關於模式後面會詳細介紹。
orm

Context提供了幾種模式:xml

一、Context.MODE_PRIVATE:爲默認操做模式,表明該文件是私有數據,只能被應用自己訪問,在該模式下,寫入的內容會覆蓋原文件的內容,若是想把新寫入的內容追加到原文件中。可使用Context.MODE_APPEND
二、Context.MODE_APPEND:模式會檢查文件是否存在,存在就往文件追加內容,不然就建立新文件。
三、Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用來控制其餘應用是否有權限讀寫該文件。
MODE_WORLD_READABLE:表示當前文件能夠被其餘應用讀取;
MODE_WORLD_WRITEABLE:表示當前文件能夠被其餘應用寫入

其餘應用讀取該應用目錄下此配置文件中passwd確定是失敗的。

若是咱們想讓A應用訪問到B應用的數據,咱們能夠這麼作:把B應用建立模式改爲可讀模式的,那麼A應用就能夠操做了,那麼這就有一個問題,A應用能夠訪問了,其餘應用也能夠訪問了,這樣全部的應用均可以訪問B應用的沙盒數據了,太危險了。

因此要用另外的一種方式,那麼這時候就要用到shareUserId屬性了,咱們只須要將B應用建立方式仍是private的,而後A應用和B應用公用一個uid便可,咱們下面就來修改一下代碼,仍是前面的那兩個工程,修改他們的AndroidManifest.xml,添shareUserId便可。

這時候,咱們發現把代碼中的模式改爲private的,A應用任然能夠訪問數據了,其實也好理解,他們兩個的uid都相同了,A的文件就是B的,B的就是A的了,他們兩個沒有沙盒的概念了,數據也是透明的了。

因此這裏咱們就看到了,使用shareUserId能夠達到多個應用之間的數據透明性互相訪問。

注意:這裏有一個誤點,就是這裏全部的修改的前提是這個應用的AndroidManifest.xml自己就定義了這個shareUserId,而後咱們能夠反編譯看到這個值,把咱們本身的shareUserId改爲他的就能夠了,可是若是這個應用自己沒有這個屬性,那麼這裏就沒有辦法的,爲何呢,若是要添加,那就是另一條路了,就是逆向,修改AndroidManifest.xml以後,還須要重新打包在驗證,可是這時候不必了,咱們也知道有時候回編譯仍是很艱難的,若是都能回編譯了,那都不須要這些工做了,因此這裏須要注意的一個前提。

那麼修改以後是否是真的能夠呢?

答案是確定不能夠的,若是能夠的話,那google也太傻比了,其實Android系統中有一個限制,就是說若是多個應用的uid相同的話,那麼他們的apk簽名必須一致,否則是安裝失敗的,以下錯誤:

經過上面的分析,咱們就知道了,Android中是不容許相同的uid的不一樣簽名的應用。

三、經過shareduserid來獲取系統權限 

  (1)在AndroidManifest.xml中添加android:sharedUserId="android.uid.system"
    (2)在Android.mk文件裏面添加LOCAL_CERTIFICATE := platform(使用系統簽名)
    (3)在源碼下面進行mm編譯
    這樣生成的apk可以獲取system權限,能夠在任意system權限目錄下面進行目錄或者文件的建立,以及訪問其餘apk資源等(注意建立的文件(夾)只有建立者(好比system,root除外)擁有可讀可寫權限-rw-------)。

四、擴展

系統中全部使用android.uid.system做爲共享UID的APK,都會首先在manifest節點中增長android:sharedUserId="android.uid.system",而後在Android.mk中增長LOCAL_CERTIFICATE := platform。能夠參見Settings等

系統中全部使用android.uid.shared做爲共享UID的APK,都會在manifest節點中增長android:sharedUserId="android.uid.shared",而後在Android.mk中增長LOCAL_CERTIFICATE := shared。能夠參見Launcher等

系統中全部使用android.media做爲共享UID的APK,都會在manifest節點中增長android:sharedUserId="android.media",而後在Android.mk中增長LOCAL_CERTIFICATE := media。能夠參見Gallery等。

五、看看如何在一個app中去訪問另一個app的代碼和資源等信息?

在說這個知識點以前,咱們須要瞭解的一個知識點,就是咱們能夠經過一個包名來獲得對應的Context的全局變量,能夠直接使用Context的一個靜態方法:createPackageContext

關於這個方法其實很簡單,他有兩個參數:

第一個參數:須要構造出來Context的包名字符串

第二個參數:構造出來的Context的開啓模式

下面咱們能夠直接使用一個例子來看看效果:

首先咱們弄一個插件工程:ShareUserIdPlugin

 

 

這個工程很簡單,咱們編譯安裝運行便可。

在弄一個宿主工程:ShareUserIdHost

 

這裏有一個核心方法,咱們首先經過插件工程的包名:cn.wjdiankong.shareuseridplugin;建立出一個Context對象。

這裏看到第二參數有兩個模式:

Context.CONTEXT_INCLUDE_CODE:這個標誌是在咱們須要執行插件中的某段代碼須要加上的值。(下面代碼缺失會發現報錯了,找不到指定的類。因此若是想運行代碼的話,這個值必定要加上。)

CONTEXT_IGNORE_SECURITY:這個標誌是必須的,是忽視安全性,若是沒有這個值的話,那麼咱們訪問什麼都是失敗的。(下面代碼缺失報安全錯誤)

獲得了Context變量以後,咱們下面就能夠經過反射來執行代碼和獲取資源了,這裏須要注意的是,必定要先拿到Context對應的ClassLoader,而後才能加載對應的類,ClassLoader必定是Context的,是插件工程中的類加載器。

 

下面咱們運行結果看看:

 

運行成功了啦~~是否是很簡單呢。

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息