引言:本篇文章主要說明四大組件與Application間調(diào)用的一些條件注意點膳凝,包括Activity火的、Service等組件能夠被外界訪問的條件等。關(guān)于四大組件特別是Service对雪、ContentProvider和BroadcastReceiver需要注意的地方,可以查看本人另一篇文章:查漏補缺(三):重溫Service米绕、BoardcastReceiver和ContentProvider瑟捣。至于為什么沒有細講Activity,因為看過Activity生命周期以及其啟動方式的讀者對于Activity已經(jīng)比較熟悉了栅干,這里重點是講講一些細節(jié)上的東西迈套。
一、問題描述
- 在開發(fā)一個App過程中碱鳞,activityA通過Intent啟動activityB桑李,有顯式啟動和隱式啟動兩種方式,而官方的文檔中說startActivity可能會報NotFoundException,表示被start的Activity不存在贵白。因此率拒,我們很容易忽略另一個可能的Exception,Permission Denial禁荒。
- 開發(fā)多個App時俏橘,想要讓AppB去調(diào)用AppA中的Activity(如:startActivity()方式),也許你什么Exception都不會得到圈浇,也可能會直接Force Close掉寥掐。
二呻拌、問題原因
因為Start Activity時费奸,代碼是有去檢驗permission的。
如下情況蚕脏,可以成功startActivity而不會得到permission denial
- 同一個application下
- Uid相同
- permission匹配
- 目標Activity的屬性Android:exported=”true”
- 目標Activity具有相應(yīng)的IntentFilter褐隆,存在Action動作或其他過濾器并且沒有設(shè)置exported=false
- 啟動者的Pid是一個系統(tǒng)服務(wù)(System Server)的Pid【也就是系統(tǒng)服務(wù)前來調(diào)用普通App的Activity等】
- 啟動者的Uid是一個System Uid(Android規(guī)定android.system.uid=1000污它,具有該Uid的application,我們稱之為獲得Root權(quán)限)
如果上述調(diào)節(jié)庶弃,滿足一條衫贬,一般即可(與其他幾條不發(fā)生強制設(shè)置沖突),否則歇攻,將會得到Permission Denial的Exception而導致Force Close固惯。
三、Uid機制
我們知道缴守,Pid表示<u>進程ID</u>葬毫,Uid表示<u>用戶ID</u>,只是Android和計算機不一樣屡穗,計算機每個用戶都具有一個Uid贴捡,哪個用戶start的程序,這個程序的Uid就是那個那個用戶村砂,而Android中每個程序都有一個Uid烂斋,默認情況下,Android會給每個程序分配一個<u>普通級別互不相同的Uid</u>础废,如果用互相調(diào)用汛骂,只能是Uid相同才行,這就使得共享數(shù)據(jù)具有了一定安全性色迂,每個軟件之間是不能隨意獲得數(shù)據(jù)的香缺。而同一個application只有一個Uid,所以application下的Activity之間不存在訪問權(quán)限的問題歇僧。
讓你的App將它里面含有的某些activity图张、service锋拖、provider等的數(shù)據(jù)進行共享:
法一:完全暴露。這就是
android:exported=”true”
的作用祸轮,而一旦設(shè)置了intentFilter之后兽埃,exported就默認被設(shè)置為true了,除非再強制設(shè)為false适袜。當然柄错,對那些沒有intentFilter的程序體,它的exported屬性默認仍然是false苦酱,也就不能共享出去售貌。-
法二:權(quán)限提示暴露。這就是為什么經(jīng)常要設(shè)置
<uses-permission name="xxxxx"/>
的原因疫萤,如果人家設(shè)置了android:permission=”xxx.xxx.xx”
那么颂跨,你就必須在你的App的AndroidManifast.xml中uses-permission xxx.xxx.xx
才能訪問人家的東西〕度模【下面以自定義權(quán)限的用法來作為示例恒削,而關(guān)于系統(tǒng)權(quán)限大全,可以查看本人的另一篇文章:Android字典(一) -- permission權(quán)限說明尾序〉龇幔】-
<permission>
格式
-
<!--自定義權(quán)限格式-->
<permission android:description="string resource" //權(quán)限描述(描述權(quán)限)
android:icon="drawable resource" //權(quán)限圖標(描述權(quán)限)
android:label="string resource" ///權(quán)限標簽(描述權(quán)限)
android:name="string" //權(quán)限名稱(描述權(quán)限)
android:permissionGroup="string"
//權(quán)限組。此屬性是可選的每币,被用于協(xié)助系統(tǒng)向用戶顯示權(quán)限携丁,一般會像(`android:permissionGroup="android.permission-group.SYSTEM_TOOLS" `)這個一樣設(shè)置為標準系統(tǒng)組,很少自定義脯爪,最好使用已經(jīng)定義的则北,使用起來也方便。
android:protectionLevel=["normal" | "dangerous" | "signature" | "signatureOrSystem"]
//權(quán)限級別痕慢。必須聲明,用于告訴系統(tǒng)當前應(yīng)用進行訪問控制涌矢,例如對于:如網(wǎng)絡(luò)訪問(需付費)以及獲取聯(lián)系人(涉及隱私)等掖举。
//normal:低風險權(quán)限,只要申請了就可以使用(在AndroidManifest.xml中添加<uses-permission>標簽)娜庇,安裝時不需要用戶確認塔次;
//dangerous:高風險權(quán)限,安裝時需要用戶的確認才可使用名秀;
//signature:只有當申請權(quán)限的應(yīng)用程序的數(shù)字簽名與聲明此權(quán)限的應(yīng)用程序的數(shù)字簽名相同時(如果是申請系統(tǒng)權(quán)限励负,則需要與系統(tǒng)簽名相同),才能將權(quán)限授給它匕得;
//signatureOrSystem:簽名相同继榆,或者申請權(quán)限的應(yīng)用為系統(tǒng)應(yīng)用(在system image中)巾表。
/>
* 用法步驟:
首先在App中的androidManifast.xml文件中`<application>`標簽內(nèi)部定義了自己的`<permission>`,然后在`<application>`標簽之外使用`<uses-permission>`來聲明需要的權(quán)限略吨,再讓自己的`<activity>`或者`<receiver>`等組件添加上`android:permission="你剛剛自定義的權(quán)限名稱"`來使你的組件擁有被調(diào)用時檢測對方App有沒有相應(yīng)權(quán)限的能力集币。
-
法三:私有暴露。使用
sharedUserId
翠忠。在Android里面每個app都有一個唯一的linux user ID鞠苟,則這樣權(quán)限就被設(shè)置成該應(yīng)用程序的文件只對該用戶可見,只對該應(yīng)用程序自身可見秽之,而我們可以使他們對其他的應(yīng)用程序可見当娱,這會使我們用到SharedUserId,也就是讓兩個apk使用相同的userID考榨,這樣它們就可以看到對方的文件跨细。為了節(jié)省資源,具有相同ID的apk也可以在相同的linux進程中進行(注意董虱,并不是一定要在一個進程里面運行)扼鞋,共享一個虛擬機。
??假如說一個公司做了兩個產(chǎn)品愤诱,只想這兩個產(chǎn)品之間可互相調(diào)用云头,那么這個時候就必須使用shareUserID將兩個軟件的Uid強制設(shè)置為一樣的。這種情況下必須使用具有該公司簽名的簽名文檔才能淫半,如果使用一個系統(tǒng)自帶軟件的ShareUID溃槐,例如Contact,那么無須第三方簽名科吭。- 一般用法步驟:
- 第一個應(yīng)用程序為的menifest文件代碼如下:
- 一般用法步驟:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.abc.serviceID"
android:versionCode="1"
android:versionName="1.0"
android:sharedUserId="com.abc.share">
//.......
2. 第二個應(yīng)用程序的menifest文件代碼如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.abc.clientID"
android:versionCode="1"
android:versionName="1.0"
android:sharedUserId="com.abc.share">
3. 假設(shè)我們從package=“com.abc.clientID”的程序獲取package="com.abc.serviceID"的程序的context:
```
Context context=this.createPackageContext("com.abc.serviceID",Context.CONTEXT_IGNORE_SECURITY);
```
#### Uid機制的意義在于:應(yīng)用程序獲得系統(tǒng)權(quán)限
?如果一個activity是由system process跑出來的昏滴,那么它就可以橫行霸道,任意權(quán)限对人,只是你無法開發(fā)一個第三方application具有系統(tǒng)的Pid(系統(tǒng)Pid不固定)谣殊,但是你<u>完全可以開發(fā)一個具有系統(tǒng)Uid的程序,對系統(tǒng)中的所有程序任意訪問牺弄,只需在AndroidManifest.xml中聲明shareUserId為`android.uid.system`即可姻几,生成的文件也必須經(jīng)過高權(quán)限簽名才行,一般不具備這種審核條件的application势告,google不會提供給你這樣的簽名文件蛇捌。當然你是在編譯自己的系統(tǒng)的話,想把它作成系統(tǒng)軟件程序咱台,只需在Android.mk中聲明Certificate:platform則可以了络拌,既采用系統(tǒng)簽名。</u>這個系統(tǒng)Uid的獲得過程回溺,我們把它叫做獲得Root權(quán)限的過程春贸。所以很多第三方系統(tǒng)管理軟件就是有Root權(quán)限的軟件混萝,因為他需要對系統(tǒng)有任意訪問的權(quán)限。那么它的Root簽名則需要和編譯的系統(tǒng)一致祥诽,例如官方的系統(tǒng)得用官方的簽名文件譬圣,CM的系統(tǒng)就得用CM的簽名文件。至于Android.mk文件以及相關(guān)配置等雄坪,可以參考下http://dengzhangtao.iteye.com/blog/1750782 厘熟,http://blog.sina.com.cn/s/blog_628cc2b70101dcai.html
#### 拓展:Android整個permission機制
?AndroidManifest.xml里面的sharedUserID能夠讓不同的apk運行在同一個進程里,分享里面的數(shù)據(jù)维哈,比如Contacts等绳姨,當然這個sharedUserID可以設(shè)置成“android.uid.system”就可以運行在系統(tǒng)進程中,有權(quán)修改系統(tǒng)數(shù)據(jù)阔挠。
?但僅僅有著一個sharedUserID并不能夠保證你的apk一定能運行成功飘庄,怎么辦?簽名啊购撼。如果你有Android的源碼就比較方便了跪削,直接把Android.mk里面的LOCAL_CERTIFICATE 賦值為platform就行了。然后mm編輯迂求,就能安裝了碾盐。因為在安裝的時候,PackageManager會檢查揩局,如果sharedUserId是system的毫玖,它會看這個apk的簽名是不是system.crt,如果不是凌盯,會報出permission deny的error付枫。而把LOCAL_CERTIFICATE改成platform就等于給APK簽名。
?進而可以通過這個問題研究一下整個Android permission的機制驰怎。系統(tǒng)的安全機制通過給每個用戶分配單獨的uid和gid來實現(xiàn)阐滩,Android系統(tǒng)中pid代表進程ID,這個是有系統(tǒng)在程序運行時分配的县忌,這一點可以防止地址空間的數(shù)據(jù)共享叶眉,增強內(nèi)存空間的安全性。對于外部則用到了uid進行封鎖芹枷。
?系統(tǒng)會給于用戶進程單獨的uid,當然系統(tǒng)也是要運行進程的莲趣,比如System鸳慈,Radio,藍牙喧伞,IO設(shè)備走芋。系統(tǒng)中的init.rc文件會詳細定義這些文件的權(quán)限绩郎。Android中對uid的定義是Root最高,其次是system翁逞,最低的是app肋杖。這是基于Linux系統(tǒng)的結(jié)果。
?那么在APP里挖函,要對一些進程進行訪問状植,或者接受Broadcast,或者啟動Activity怨喘、Service都是需要權(quán)限的津畸,不能說你的app什么都能做,這也是需要在manifest file中設(shè)置的必怜。
?比如在startActivity時肉拓,如果你start自己apk里的activity,它們會在同一個application下梳庆,那么自然也就使用一個uid暖途,start過程自然沒有什么問題。如果你需要start別人寫的Activity或者service膏执,都需要用到同一個shareUserId才行驻售,因為在ActivityManagerService要啟動activity的之前,會首先檢查uid胧后,用checkPermission方法芋浮,透過binder獲得pid和uid,檢查你activity的binder的權(quán)限壳快,如果你有權(quán)限則已纸巷,沒權(quán)限的話就會拋出security exception。至于broadcast眶痰,檢查則更為嚴格瘤旨,會雙向的檢查發(fā)出者和接受者的權(quán)限。