從源碼角度分析Android中UID與GID的分配

概述

UID一般理解爲User Identifier,在linux中就是用戶的ID,表明是哪個用戶運行了這個程序,GID則表明了這個用戶屬於哪個組。它們主要用於權限的管理。

而在Android中,部分權限的管理是依賴底層的linux的,所以瞭解Android的UID/GID十分必要。

網上有下面的一段話:
而在Android 中又有所不同,因爲Android爲單用戶系統,這時UID 便被賦予了新的使命,android爲每個應用幾乎都分配了不同的UID,不像傳統的linux,每個用戶相同就爲之分配相同的UID。(當然這也就表明了一個問題,android只能時單用戶系統,在設計之初就被他們的工程師給閹割了多用戶),使之成了數據共享的工具。

這段話有對有錯,這篇文章將結合源碼來分析UID與GID的分配,使我們有個最清晰的理解。源碼版本爲4.3。

首先需要明確的一點是,App的UID和GID是安裝的時候確認的。而與安裝相關的源碼目錄是:
frameworks\base\services\java\com\android\server\pm

PackageManagerService.java的scanPackageLI方法

private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
            int parseFlags, int scanMode, long currentTime) {
    ............
    //獲取一系列屬性
    pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
                    destResourceFile, pkg.applicationInfo.nativeLibraryDir,
                    pkg.applicationInfo.flags, true, false);
    ............
    //UID賦值
    pkg.applicationInfo.uid = pkgSetting.appId;
    ............

可以看到在getPackageLPw方法中,獲取了UID,那我們打開這個函數看看:
Settings.java的getPackageLPw方法

    private PackageSetting getPackageLPw(String name, PackageSetting origPackage,
            String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
            String nativeLibraryPathString, int vc, int pkgFlags, boolean create, boolean add) {
            .........
            s.userId = newUserIdLPw(s);
            .........
            }

可以看到UID是newUserIdLPw()指定的,那再打開這個看看:
依然是Settings文件

private int newUserIdLPw(Object obj) {
        // Let's be stupidly inefficient for now...
        final int N = mUserIds.size();
        for (int i = 0; i < N; i++) {
            if (mUserIds.get(i) == null) {
                mUserIds.set(i, obj);
                return Process.FIRST_APPLICATION_UID + i;
            }
        }

        // None left?
        if (N > (Process.LAST_APPLICATION_UID-Process.FIRST_APPLICATION_UID)) {
            return -1;
        }

        mUserIds.add(obj);
        return Process.FIRST_APPLICATION_UID + N;
    }

至此,UID就算是分配下來了。
可以看到for循環和if (mUserIds.get(i) == null)語句限定了一個APP只有一個UID,而這個Process.FIRST_APPLICATION_UID是在frameworks/base/core/java/android/os/Process.java中定義的,其值爲10000,這就是爲什麼Android的UID都是從10000開始的。

再回到PackageManagerService.javascanPackageLI代碼:

//invoke installer to do the actual installation
    //第二個uid就是GID
    int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
            pkg.applicationInfo.uid);
    if (ret < 0) {
        // Error from installer
        mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
        return null;
    }
    // Create data directories for all users
    // 指定工作目錄
    sUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid);

    if (dataPath.exists()) {
        pkg.applicationInfo.dataDir = dataPath.getPath();
    } else {
        Slog.w(TAG, "Unable to create data directory: " + dataPath);
        pkg.applicationInfo.dataDir = null;
    }

可以看到此函數在運行時,將UID的值直接賦值給了GID,所以通常UID和GID也是相同的。從官方註釋這可以看到Android有多用戶。事實上,Android某些平板對多用戶是支持的,我猜大家常見的是閹割版的吧,所以有誤會。
打開UserManagerinstallPackageForAllUsers方法可以看到:

public void installPackageForAllUsers(String packageName, int uid) {
    for (int userId : mUserIds) {
        // Don't do it for the primary user, it will become recursive.
        if (userId == 0)
            continue;
        mInstaller.createUserData(packageName, UserId.getUid(userId, uid),
                userId);
    }
}

寫了這麼多,相信大家對UID/GID的分配有了更深刻的理解。歡迎一起討論哈。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章