1. 程式人生 > >Android系統許可權配置詳解

Android系統許可權配置詳解

Android 許可權控制程式碼分析

android系統充分利用了linux的使用者許可權管理方法,所以如果需要移植到其它系統,這一塊也是一個相當不小的工作量。那麼android系統到底是如何使用這些的有利因素呢?

首先需要知道linux許可權的兩個基本知識:

1、 一個使用者可以屬於多個組.

2、 一個檔案只能屬於某個組。

這裡主要是在AndroidManifest.xml中宣告許可權,主要是通過在AndroidManifest.xml中顯示地宣告應用程式需要的許可權,防止應用程式錯誤的使用服務,不恰當訪問資源。

Android中每種許可權都用一個獨立的標籤表示.如:

    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

當在安裝(Install) 應用程式時,Android就會給予一個UID。這個UID可連結到該應用程式的 AndroidManifest.xml檔案的內容。所User在安裝你的應用程式時,在螢幕上的窗口裡可以檢視這個 AndroidManifest.xml檔案的內容。在檢視時,使用者會看到你對應用程式的目的、許可權等說明。當你接受這支程式的意圖、許可權說明之後,Android就安裝它,並給它一個UID。萬一在你的應用程式執行期間有越軌(企圖做出非許可權範圍)的行為時,使用者將會得到Android的警告訊息。

下面是兩個安裝程式安裝時的介面


這兩個應用其實程式碼是一樣的,唯一的不同就是它們的AndroidManifest.xml

,圖2AndroidManifest.xml中多瞭如下內容:

<uses-permissionandroid:name="android.permission.RECORD_AUDIO"/>

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

即需要使用儲存裝置和錄音裝置,在安裝的時候,就會提示使用者它需要的許可權。Android裡面是怎麼去控制的呢?

在安裝apk的時候,會解析這個AndroidManifest.xml,把相應的資訊儲存起來。

程式碼路徑:frameworks\base\core\java\android\content\pm\PackageParser.java

最終呼叫的是private Package parsePackage(

        Resources res, XmlResourceParser parser, int flags, String[] outError)

這個函式,在裡面對AndroidManifest.xml進行了解析,其中就有

    private Package parsePackage(
        Resources res, XmlResourceParser parser, int flags, String[] outError)
        throws XmlPullParserException, IOException {

       ... 

      String tagName = parser.getName();
      if (tagName.equals("application")) {
       ...
      } else if (tagName.equals("permission-group")) {
          if (parsePermissionGroup(pkg, res, parser, attrs, outError) == null) {
              return null;
          }
      } else if (tagName.equals("permission")) {
          if (parsePermission(pkg, res, parser, attrs, outError) == null) {
              return null;
          }
      } else if (tagName.equals("permission-tree")) {
          if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {
              return null;
          }
      } else if (tagName.equals("uses-permission")) {
          sa = res.obtainAttributes(attrs,
                  com.android.internal.R.styleable.AndroidManifestUsesPermission);

          // Note: don't allow this value to be a reference to a resource
          // that may change.
          String name = sa.getNonResourceString(
                  com.android.internal.R.styleable.AndroidManifestUsesPermission_name);

          sa.recycle();

          if (name != null && !pkg.requestedPermissions.contains(name)) {
              pkg.requestedPermissions.add(name.intern());
          }

          XmlUtils.skipCurrentTag(parser);
      }

     ...

   }

這裡對它使用的許可權進行了解析。

這裡儲存的都是"android.permission.WRITE_EXTERNAL_STORAGE"這樣的字串,在解析完後,會呼叫grantPermissionsLP函式獲取對應的group_id

程式碼路徑:frameworks\base\services\java\com\android\server\PackageManagerService.java

private void grantPermissionsLP(PackageParser.Package pkg, boolean replace) {
...
    if (allowed) {
        if (!gp.grantedPermissions.contains(perm)) {
            changedPermission = true;
            gp.grantedPermissions.add(perm);
            gp.gids = appendInts(gp.gids, bp.gids);

        } else if (!ps.haveGids) {
            gp.gids = appendInts(gp.gids, bp.gids);
        }
    } else {
        Slog.w(TAG, "Not granting permission " + perm
                + " to package " + pkg.packageName
                + " because it was previously installed without");
    }        
...
}

這裡把相應的組都儲存到了gids中。

當應用程式啟動的過程中會呼叫

private final void startProcessLocked(ProcessRecord app,

            String hostingType, String hostingNameStr)

程式碼路徑:frameworks\base\services\java\com\android\server\am\ActivityManagerService.java

private final void startProcessLocked(ProcessRecord app,
        String hostingType, String hostingNameStr) {
...
    try {
        int uid = app.info.uid;
        int[] gids = null;
        try {
            gids = mContext.getPackageManager().getPackageGids(
                    app.info.packageName);

        } catch (PackageManager.NameNotFoundException e) {
            Slog.w(TAG, "Unable to retrieve gids", e);
        }     
...
    int pid = Process.start("android.app.ActivityThread",
          mSimpleProcessManagement ? app.processName : null, uid, uid,
          gids, debugFlags, null);        

}


這裡就是獲取前面儲存的gids,再後面呼叫建立了一個新的程序,這裡傳的引數就有gids

建立新程序利用jni最終呼叫 forkAndSpecializeCommon 函式 (路徑:\dalvik\vm\native\dalvik_system_Zygote.c)
static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer)
{
    pid_t pid;
    uid_t uid = (uid_t) args[0];
    gid_t gid = (gid_t) args[1];
    ArrayObject* gids = (ArrayObject *)args[2];
    u4 debugFlags = args[3];
    ArrayObject *rlimits = (ArrayObject *)args[4];
    int64_t permittedCapabilities, effectiveCapabilities;
...
    pid = fork();
    if (pid == 0) {
        int err;
        /* The child process */
    
        err = setgroupsIntarray(gids);

        if (err < 0) {
            LOGE("cannot setgroups(): %s", strerror(errno));
            dvmAbort();
        }
        err = setrlimitsFromArray(rlimits);

        if (err < 0) {
            LOGE("cannot setrlimit(): %s", strerror(errno));
            dvmAbort();
        }
        err = setgid(gid);
        if (err < 0) {
            LOGE("cannot setgid(%d): %s", gid, strerror(errno));
            dvmAbort();
        }

        err = setuid(uid);
        if (err < 0) {
            LOGE("cannot setuid(%d): %s", uid, strerror(errno));
            dvmAbort();
        }        
        ...
}

我們看到在子程序裡呼叫setgroupsIntarray設定該程序所屬的組,這樣它就擁有了該組的許可權。也通過setgid及setuid決定了應用程式的uid及gid值。

舉個例子:

我們新建一Android工程,讀取其應用的uid/gid值,貼入如下程式碼:

try{

java.lang.Process process = Runtime.getRuntime().exec("id");

InputStream input = process.getInputStream();

byte[] bytes = newbyte[1204];

int len;

while((len = (input.read(bytes))) > 0)

{

System.out.print(new String(bytes, 0, len)); 

}

input.close();

這裡執行執行linuxid命令的程式碼,id命令的功能是輸出當前使用者的uid,主要的group id和所在的group

在其AndroidManifest.xml新增如下許可權:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

執行後看log裡面有:


這裡面就有groups=1015

再看下android_filesystem_config.h裡面

程式碼路徑:

system\core\include\private

有如下程式碼

#define AID_ROOT             0  /* traditional unix root user */

#define AID_SYSTEM        1000  /* system server */

#define AID_RADIO         1001  /* telephony subsystem, RIL */
#define AID_BLUETOOTH     1002  /* bluetooth subsystem */
#define AID_GRAPHICS      1003  /* graphics devices */

...

#define AID_WIFI          1010  /* wifi subsystem */
#define AID_ADB           1011  /* android debug bridge (adbd) */
#define AID_INSTALL       1012  /* group for installing packages */
#define AID_MEDIA         1013  /* mediaserver process */
#define AID_DHCP          1014  /* dhcp client */
#define AID_SDCARD_RW     1015  /* external storage write access */

再看下platform.xml

路徑:frameworks\base\data\etc

有如下配置

<permission name="android.permission.WRITE_EXTERNAL_STORAGE" >

        <group gid="sdcard_rw" />

</permission>

這樣就把android.permission.WRITE_EXTERNAL_STORAGE 、"sdcard_rw"、以及 1015關聯起來了

我們再看下當sd卡掛載上去後目錄的許可權:

ls -l

drwxr-xr-x root     system            1970-01-01 08:00 obb

drwxr-xr-x root     system            1970-01-01 08:00 asec

drwx------ root     root              1970-01-01 08:00 secure

d---rwxr-x system   sdcard_rw          2012-03-29 17:11 sdcard

可以看到對於sd卡,組使用者具有讀寫許可權,而我們的應用也加入了這個組,這樣它就可以操作sdcard了。

當一個應用需要操作sdcard而沒有在AndroidManifest.xml新增相應的許可權時,就不能成功完成。

以下是同一個程式,一個有在AndroidManifest.xml新增WRITE_EXTERNAL_STORAGE許可權,一個沒有,它們的對比,可以看到由於沒有許可權程式執行異常了。

對於管理許可權的xml檔案補充說明一下:

原始碼中許可權檔案位於: frameworks\base\data\etc 下面,映象生成在 system\etc\permissions\platform.xml中

檔案許可權讀取點:

readPermissions @ PackageManagerService.java

    void readPermissions() {
        // Read permissions from .../etc/permission directory.
        File libraryDir = new File(Environment.getRootDirectory(), "etc/permissions");

        if (!libraryDir.exists() || !libraryDir.isDirectory()) {
            Slog.w(TAG, "No directory " + libraryDir + ", skipping");
            return;
        }
        if (!libraryDir.canRead()) {
            Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
            return;
        }

        // Iterate over the files in the directory and scan .xml files
        for (File f : libraryDir.listFiles()) {
            // We'll read platform.xml last
            if (f.getPath().endsWith("etc/permissions/platform.xml")) {
                continue;
            }



            if (!f.getPath().endsWith(".xml")) {
                Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
                continue;
            }
            if (!f.canRead()) {
                Slog.w(TAG, "Permissions library file " + f + " cannot be read");
                continue;
            }

            readPermissionsFromXml(f);
        }

        // Read permissions from .../etc/permissions/platform.xml last so it will take precedence
        final File permFile = new File(Environment.getRootDirectory(),
                "etc/permissions/platform.xml");
        readPermissionsFromXml(permFile);

    }

platform.xml 的重要性在於:

<!-- This file is used to define the mappings between lower-level system
     user and group IDs and the higher-level permission names managed
     by the platform.


     Be VERY careful when editing this file!  Mistakes made here can open
     big security holes.
-->

Android的系統許可權不是由使用者控制,而是由開發者根據開發的需要控制相關許可權的開放與否,許可權控制主要放置在AndroidManifest.xml檔案中。

將如下的許可權控制屬性寫入AndroidManifest.xml檔案就可以獲取相應的系統許可權。如果在開發中遇到一些除錯的問題很可以就是許可權的原因。

<uses-permission android:name="android.permission.ACCESS_CHECKIN_PROPERTIES" ></uses-permission>
允許讀寫訪問"properties"表在checkin資料庫中,改值可以修改上傳
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" ></uses-permission>
允許一個程式訪問CellID或WiFi熱點來獲取粗略的位置
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" ></uses-permission>
允許一個程式訪問精良位置(如GPS)
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ></uses-permission>
允許應用程式訪問額外的位置提供命令
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" ></uses-permission>
允許程式建立模擬位置提供用於測試
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" ></uses-permission>
允許程式訪問有關GSM網路資訊
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" ></uses-permission>
允許程式使用SurfaceFlinger底層特性
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" ></uses-permission>
允許程式訪問Wi-Fi網路狀態資訊
<uses-permission android:name="android.permission.ADD_SYSTEM_SERVICE" ></uses-permission>
允許程式釋出系統級服務
<uses-permission android:name="android.permission.BATTERY_STATS" ></uses-permission>
允許程式更新手機電池統計資訊
<uses-permission android:name="android.permission.BLUETOOTH" ></uses-permission>
允許程式連線到已配對的藍芽裝置
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" ></uses-permission>
允許程式發現和配對藍芽裝置
<uses-permission android:name="android.permission.BRICK" ></uses-permission>
請求能夠禁用裝置
<uses-permission android:name="android.permission.BROADCAST_PACKAGE_REMOVED" ></uses-permission>
允許程式廣播一個提示訊息在一個應用程式包已經移除後
<uses-permission android:name="android.permission.BROADCAST_STICKY" ></uses-permission>
允許一個程式廣播常用intents
<uses-permission android:name="android.permission.CALL_PHONE" ></uses-permission>
允許一個程式初始化一個電話撥號不需通過撥號使用者介面需要使用者確認
<uses-permission android:name="android.permission.CALL_PRIVILEGED" ></uses-permission>
允許一個程式撥打任何號碼,包含緊急號碼無需通過撥號使用者介面需要使用者確認
<uses-permission android:name="android.permission.CAMERA" ></uses-permission>
請求訪問使用照相裝置
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" ></uses-permission>
允許一個程式是否改變一個元件或其他的啟用或禁用
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" ></uses-permission>
允許一個程式修改當前設定,如本地化
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" ></uses-permission>
允許程式改變網路連線狀態
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" ></uses-permission>
允許程式改變Wi-Fi連線狀態
<uses-permission android:name="android.permission.CLEAR_APP_CACHE" ></uses-permission>
允許一個程式清楚快取從所有安裝的程式在裝置中
<uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" ></uses-permission>
允許一個程式清除使用者設定
<uses-permission android:name="android.permission.CONTROL_LOCATION_UPDATES" ></uses-permission>
允許啟用禁止位置更新提示從無線模組
<uses-permission android:name="android.permission.DELETE_CACHE_FILES" ></uses-permission>
允許程式刪除快取檔案
<uses-permission android:name="android.permission.DELETE_PACKAGES" ></uses-permission>
允許一個程式刪除包
<uses-permission android:name="android.permission.DEVICE_POWER" ></uses-permission>
允許訪問底層電源管理
<uses-permission android:name="android.permission.DIAGNOSTIC" ></uses-permission>
允許程式RW診斷資源
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" ></uses-permission>
允許程式禁用鍵盤鎖
<uses-permission android:name="android.permission.DUMP" ></uses-permission>
允許程式返回狀態抓取資訊從系統服務
android.permission.EXPAND_STATUS_BAR
允許一個程式擴充套件收縮狀態列
android.permission.FACTORY_TEST
作為一個工廠測試程式,執行在root使用者
android.permission.FLASHLIGHT
訪問閃光燈
android.permission.FORCE_BACK
允許程式強行一個後退操作是否在頂層activities
android.permission.FOTA_UPDATE android
 一個預留許可權
android.permission.GET_ACCOUNTS
訪問一個帳戶列表在Accounts Service中
android.permission.GET_PACKAGE_SIZE
允許一個程式獲取任何package佔用空間容量
android.permission.GET_TASKS
允許一個程式獲取資訊有關當前或最近執行的任務,一個縮略的任務狀態,是否活動等等
android.permission.HARDWARE_TEST
允許訪問硬體
android.permission.INJECT_EVENTS
允許一個程式截獲使用者事件如按鍵、觸控、軌跡球等等到一個時間流
android.permission.INSTALL_PACKAGES
允許一個程式安裝packages
android.permission.INTERNAL_SYSTEM_WINDOW
允許開啟視窗使用系統使用者介面
android.permission.INTERNET
允許程式開啟網路套接字
android.permission.MANAGE_APP_TOKENS
允許程式管理(建立、催後、z- order預設向z軸推移)程式引用在視窗管理器中
android.permission.MASTER_CLEAR
恢復出廠設定許可權,清除一切使用者資料
android.permission.MODIFY_AUDIO_SETTINGS
允許程式修改全域性音訊設定
android.permission.MODIFY_PHONE_STATE
允許修改話機狀態,如電源,人機介面等
android.permission.MOUNT_UNMOUNT_FILESYSTEMS
允許掛載和反掛載檔案系統可移動儲存
android.permission.PERSISTENT_ACTIVITY
允許一個程式設定他的activities顯示
android.permission.PROCESS_OUTGOING_CALLS
允許程式監視、修改有關播出電話
android.permission.READ_CALENDAR
允許程式讀取使用者日曆資料
android.permission.READ_CONTACTS
允許程式讀取使用者聯絡人資料
android.permission.READ_FRAME_BUFFER
允許程式螢幕波或和更多常規的訪問幀緩衝資料
android.permission.READ_INPUT_STATE
允許程式讀取底層系統日誌檔案
android.permission.READ_OWNER_DATA
允許程式讀取所有者資料
android.permission.READ_SMS
允許程式讀取簡訊息
android.permission.READ_SYNC_SETTINGS
允許程式讀取同步設定
android.permission.READ_SYNC_STATS
允許程式讀取同步狀態
android.permission.REBOOT
請求能夠重新啟動裝置
android.permission.RECEIVE_BOOT_COMPLETED
允許一個程式接收到 ACTION_BOOT_COMPLETED廣播在系統完成啟動
android.permission.RECEIVE_MMS
允許一個程式監控將收到MMS彩信,記錄或處理
android.permission.RECEIVE_SMS
允許程式監控一個將收到簡訊息,記錄或處理
android.permission.RECEIVE_WAP_PUSH
允許程式監控將收到WAP PUSH資訊
android.permission.RECORD_AUDIO
允許程序錄制音訊
android.permission.REORDER_TASKS
允許程式改變Z軸排列任務
android.permission.RESTART_PACKAGES
允許程式重新啟動其他程式
android.permission.SEND_SMS
允許程式傳送SMS簡訊
android.permission.SET_ACTIVITY_WATCHER
允許程式監控或控制activities已經啟動全域性系統中
android.permission.SET_ALWAYS_FINISH
允許程式控制是否活動間接完成在處於後臺時
android.permission.SET_ANIMATION_SCALE
修改全域性資訊比例
android.permission.SET_DEBUG_APP
配置一個程式用於除錯
android.permission.SET_ORIENTATION
允許底層訪問設定螢幕方向和實際旋轉
android.permission.SET_PREFERRED_APPLICATIONS
允許一個程式修改列表引數PackageManager.addPackageToPreferred() 和PackageManager.removePackageFromPreferred()方法
android.permission.SET_PROCESS_FOREGROUND
允許程式當前執行程式強行到前臺
android.permission.SET_PROCESS_LIMIT
允許設定最大的執行程序數量
android.permission.SET_TIME_ZONE
允許程式設定時間區域
android.permission.SET_WALLPAPER
允許程式設定桌布
android.permission.SET_WALLPAPER_HINTS
允許程式設定桌布hits
android.permission.SIGNAL_PERSISTENT_PROCESSES
允許程式請求傳送訊號到所有顯示的程序中
android.permission.STATUS_BAR
允許程式開啟、關閉或禁用狀態列及圖示
android.permission.SUBSCRIBED_FEEDS_READ
允許一個程式訪問訂閱RSS Feed內容提供
android.permission.SUBSCRIBED_FEEDS_WRITE
系統暫時保留改設定
android.permission.SYSTEM_ALERT_WINDOW
允許一個程式開啟視窗使用 TYPE_SYSTEM_ALERT,顯示在其他所有程式的頂層
android.permission.VIBRATE
允許訪問振動裝置
android.permission.WAKE_LOCK
允許使用PowerManager的 WakeLocks保持程序在休眠時從螢幕消失
android.permission.WRITE_APN_SETTINGS
允許程式寫入API設定
android.permission.WRITE_CALENDAR
允許一個程式寫入但不讀取使用者日曆資料
android.permission.WRITE_CONTACTS
允許程式寫入但不讀取使用者聯絡人資料
android.permission.WRITE_GSERVICES
允許程式修改Google服務地圖
android.permission.WRITE_OWNER_DATA
允許一個程式寫入但不讀取所有者資料
android.permission.WRITE_SETTINGS
允許程式讀取或寫入系統設定
android.permission.WRITE_SMS
允許程式寫簡訊
android.permission.WRITE_SYNC_SETTINGS
允許程式寫入同步設定