Android 9.x 多用户机制 1 #Profile user创建过程

因为负责公司的应用多开模块,因此,对模块设计的技术进行相关的总结和备忘

0. 应用分身机制##

各个厂商的应用分身机制,大都采用原生的分身机制,实现细节上有稍微区别

OV,使用的是原生的多用户机制,跟创建一个用户的用户是一样的;这个用户空间,通过adb命令可以进入;
并且,这个空间创建的时候,会默认安装所有的系统应用;此方案的优点是被分身的应用,依赖的其他应用,在分身空间都存在,系统改动较小;缺点是,创建的分身空间占用资源较大,因为,分身空间和主空间的进程是同时运行的

xiami,也是使用的原生的多用户机制,但使用的是ProfileUser,是一个主从用户机制;并且,在创建分身用户空间的时候,并不会安装所有的系统应用到分身空间(在系统层进行了代码控制);该方案有点是,创建的分身空间占用资源较少(安装的应用少), 缺点是,需要再系统层进行大量的代码适配。

各个厂商的分身方案,不外乎这两种;

1. 多用户类型

Android中创建多用户有两种方式:

UserManager#createProfileForUser(String name, int flags, @UserIdInt int userHandle)
UserManager#createUser(String name, int flags)

我们先从profile的调用机制来分析,来分析多用户的原理

2. profile user机制创建流程

UserInfo mManagedProfileOrUserInfo;
Log.d(UserLog.TAG, "isHasExist:" + isHasExist);
mManagedProfileOrUserInfo = mUserManager.createProfileForUser("multi_user_lite",
           UserInfo.FLAG_MANAGED_PROFILE,
           Process.myUserHandle().getIdentifier());

2.1 createUserInternalUnchecked

该方法时我们要分析的主要方法,Android创建多用的主要逻辑,都在该方法中

Profile_user创建流程
  • 检查是否超出系统创建ManagedProfile数目的上限(上限为1,为常量)(canAddMoreManagedProfiles)

  • 如果创建的是非guest,非ManagedProfile ,非Demo 用户,检查是否超出系统创建User数目上线

    adb shell pm get-max-users//config_multiuserMaximumUsers
    1
    

    ManagedProfile user不受该参数控制

  • 如果创建的是Guest用户,则判断系统是否已经存在Guest用户,如果存在,则不能创建

  • 根据系统中已经存在的userid,生成本次将要创建的user的id(getNextAvailableId)(1.1.2.1 )

  • 创建/data/system/users/userid 目录(1.1.2.2)

    Environment.getUserSystemDirectory(userId).mkdirs()
    
  • 创建UserInfo,并使用UserInfo实例化UserData对象,并将UserData保存到mUsers中去;
    此时的UserInfo处于partial状态(1.1.2.3,1.1.2.4))

  • 将创建的UserData数据,写入到文件/data/system/userid.xml中去(使用XmlSerializer工具类),格式如下(1.1.2.5)

    full_k61v1_64_bsp:/data/system/users # cat 10.xml
    
    
        multi_user_lite
        
    
    
  • 根据新创建的用户,更新/data/system/users/userlist.xml,更新后的文件内容为(1.1.2.6)

    2|full_k61v1_64_bsp:/data/system/users #cat userlist.xml                                                               
    
    
        
            
        
        
        
        
    
    
  • 如果创新的是ManagerProfile,则将主用户和被创建用户的userinfo的profileGroupId置为0

            if (parent != null) {
                    if (isManagedProfile) {
                        if (parent.info.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) {
                            parent.info.profileGroupId = parent.info.id;//profileGroupId置为0
                            writeUserLP(parent);
                        }
                        userInfo.profileGroupId = parent.info.profileGroupId;//profileGroupId置为0
                    } else if (isRestricted) {
                        if (parent.info.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID) {
                            parent.info.restrictedProfileParentId = parent.info.id;
                            writeUserLP(parent);
                        }
                        userInfo.restrictedProfileParentId = parent.info.restrictedProfileParentId;
                    }
                }
  • 通过存储管理器,创建用户对应的key数据(1.1.2.7)
    storage.createUserKey(userId, userInfo.serialNumber, userInfo.isEphemeral());
    
  • 新用户数据存储区域创建和数据写入(1.1.2.8)
//创建目录,分别是/data/user_de/userid/,/data/system_de/userid/;/data/user/userid/,/data/system_ce/userid/
mUserDataPreparer.prepareUserData(userId, userInfo.serialNumber,
                    StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
VolumeInfo{private}:
    type=PRIVATE diskId=null partGuid=null mountFlags=0 mountUserId=-1 
    state=MOUNTED 
    fsType=null fsUuid=null fsLabel=null 
    path=/data internalPath=null 
  • 真正的创建新用户(1.1.2.8)
mPm.createNewUser(userId, disallowedPackages);
  • 更新备份文件中的user信息1.1.2.10,1.1.2.11)
    上组步骤会对userData数据进行改变,例如partial状态,因此这里是更新
    /data/system/userid.xml和/data/system/userlist.xml

  • 发送创建成功广播

以下对总体流程的某些核心逻辑,进行详细解读

2.1.1 新用户数据创建存储目录创建详解

Profile User创建流程_存储目录创建

2.1.1.1 遍历可写内存

mUserDataPreparer.prepareUserData(userId, userInfo.serialNumber,
    StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);

调用mUserDataPreparer辅助类的prepareUserData

    void prepareUserData(int userId, int userSerial, int flags) {
        synchronized (mInstallLock) {
            final StorageManager storage = mContext.getSystemService(StorageManager.class);
            for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
                final String volumeUuid = vol.getFsUuid();
                prepareUserDataLI(volumeUuid, userId, userSerial, flags, true);
            }
        }
    }

枚举所有的内部可写存储,,VolumeInfo的数据内容为,然后跳转到prepareUserDataLI

VolumeInfo{private}:
    type=PRIVATE diskId=null partGuid=null mountFlags=0 mountUserId=-1 
    state=MOUNTED 
    fsType=null fsUuid=null fsLabel=null 
    path=/data internalPath=null 

    //VolumeInfo初始化位置addInternalVolumeLocked#addInternalVolumeLocked
    private void addInternalVolumeLocked() {
        // Create a stub volume that represents internal storage
        final VolumeInfo internal = new VolumeInfo(VolumeInfo.ID_PRIVATE_INTERNAL,
                VolumeInfo.TYPE_PRIVATE, null, null);
        internal.state = VolumeInfo.STATE_MOUNTED;
        internal.path = Environment.getDataDirectory().getAbsolutePath();
        mVolumes.put(internal.id, internal);
    }

2.1.1.2 prepareUserDataLI

主要是三个阶段:

阶段一:prepareUserStorage

主要的存储目录创建逻辑在Ext4Crypt.cpp中

bool e4crypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial,
                                  int flags) {
    LOG(DEBUG) << "e4crypt_prepare_user_storage for volume " << escape_empty(volume_uuid)
               << ", user " << user_id << ", serial " << serial << ", flags " << flags;

    if (flags & android::os::IVold::STORAGE_FLAG_DE) {//设备加密 (DE) 存储空间:在直接启动模式期间以及用户解锁设备后均可用
        // DE_sys key
        auto system_legacy_path = android::vold::BuildDataSystemLegacyPath(user_id);//data/system/user_id/
        auto misc_legacy_path = android::vold::BuildDataMiscLegacyPath(user_id);//data/misc/user/user_id/
        auto profiles_de_path = android::vold::BuildDataProfilesDePath(user_id);//data/misc/profiles/cur/user_id/

        // DE_n key
        auto system_de_path = android::vold::BuildDataSystemDePath(user_id);//data/system_de/user_id/
        auto misc_de_path = android::vold::BuildDataMiscDePath(user_id);//data/misc_de/userid/
        auto vendor_de_path = android::vold::BuildDataVendorDePath(user_id);//data/vendor_de/user_id
        auto user_de_path = android::vold::BuildDataUserDePath(volume_uuid, user_id);//data/user_de/user_id
        //创建上述的七个目录,并设置目录权限
        if (volume_uuid.empty()) {
            if (!prepare_dir(system_legacy_path, 0700, AID_SYSTEM, AID_SYSTEM)) return false;
            #if MANAGE_MISC_DIRS
            if (!prepare_dir(misc_legacy_path, 0750, multiuser_get_uid(user_id, AID_SYSTEM),
                    multiuser_get_uid(user_id, AID_EVERYBODY))) return false;
            #endif
            if (!prepare_dir(profiles_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;

            if (!prepare_dir(system_de_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
            if (!prepare_dir(misc_de_path, 01771, AID_SYSTEM, AID_MISC)) return false;
            if (!prepare_dir(vendor_de_path, 0771, AID_ROOT, AID_ROOT)) return false;
        }
        if (!prepare_dir(user_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
        //如果是fbe设备
        if (e4crypt_is_native()) {
            PolicyKeyRef de_ref;
            if (volume_uuid.empty()) {
                if (!lookup_key_ref(s_de_key_raw_refs, user_id, &de_ref.key_raw_ref)) return false;
                get_data_file_encryption_modes(&de_ref);
                if (!ensure_policy(de_ref, system_de_path)) return false;
                if (!ensure_policy(de_ref, misc_de_path)) return false;
                if (!ensure_policy(de_ref, vendor_de_path)) return false;
            } else {
                if (!read_or_create_volkey(misc_de_path, volume_uuid, &de_ref)) return false;
            }
            if (!ensure_policy(de_ref, user_de_path)) return false;
        }
    }

    if (flags & android::os::IVold::STORAGE_FLAG_CE) {//凭据加密 (CE) 存储空间:这是默认存储位置,只有在用户解锁设备后才可用
        // CE_n key
        auto system_ce_path = android::vold::BuildDataSystemCePath(user_id);//data/system_ce/user_id/
        auto misc_ce_path = android::vold::BuildDataMiscCePath(user_id);//data/misc_ce/user_id/
        auto vendor_ce_path = android::vold::BuildDataVendorCePath(user_id);//data/vendor_ce/user_id/
        auto media_ce_path = android::vold::BuildDataMediaCePath(volume_uuid, user_id);//data/media/user_id/
        auto user_ce_path = android::vold::BuildDataUserCePath(volume_uuid, user_id);//data/user/user_id/

        if (volume_uuid.empty()) {
            if (!prepare_dir(system_ce_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
            if (!prepare_dir(misc_ce_path, 01771, AID_SYSTEM, AID_MISC)) return false;
            if (!prepare_dir(vendor_ce_path, 0771, AID_ROOT, AID_ROOT)) return false;
        }
        if (!prepare_dir(media_ce_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW)) return false;
        if (!prepare_dir(user_ce_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
        //如果支持FBE
        if (e4crypt_is_native()) {
            PolicyKeyRef ce_ref;
            if (volume_uuid.empty()) {
                if (!lookup_key_ref(s_ce_key_raw_refs, user_id, &ce_ref.key_raw_ref)) return false;
                get_data_file_encryption_modes(&ce_ref);
                if (!ensure_policy(ce_ref, system_ce_path)) return false;
                if (!ensure_policy(ce_ref, misc_ce_path)) return false;
                if (!ensure_policy(ce_ref, vendor_ce_path)) return false;

            } else {
- 
                if (!read_or_create_volkey(misc_ce_path, volume_uuid, &ce_ref)) return false;
            }
            if (!ensure_policy(ce_ref, media_ce_path)) return false;
            if (!ensure_policy(ce_ref, user_ce_path)) return false;
        }

        if (volume_uuid.empty()) {
            // Now that credentials have been installed, we can run restorecon
            // over these paths
            // NOTE: these paths need to be kept in sync with libselinux
            android::vold::RestoreconRecursive(system_ce_path);
            android::vold::RestoreconRecursive(misc_ce_path);
        }
    }
    if (!prepare_subdirs("prepare", volume_uuid, user_id, flags)) return false;

    return true;
}

在启用了 FBE 的设备上,每位用户均有两个可供应用使用的存储位置:

  • 设备加密 (DE) 存储空间:在直接启动模式期间以及用户解锁设备后均可用

    //DE_sys key
    
    • //data/system/user_id/
    • //data/misc/user/user_id/
    • //data/misc/profiles/cur/user_id/
      // DE_n key
    • //data/system_de/user_id/
    • //data/misc_de/userid/
    • //data/vendor_de/user_id
    • //data/user_de/user_id
  • 凭据加密 (CE) 存储空间:这是默认存储位置,只有在用户解锁设备后才可用

    • //data/system_ce/user_id/
    • //data/misc_ce/user_id/
    • //data/vendor_ce/user_id/
    • //data/media/user_id/
    • //data/user/user_id/

prepareUserDataLI主要完成以上目录的创建

阶段二:enforceSerialNumber####

        if ((flags & StorageManager.FLAG_STORAGE_DE) != 0 && !mOnlyCore) {
            enforceSerialNumber(getDataUserDeDirectory(volumeUuid, userId), userSerial);
            if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
                enforceSerialNumber(getDataSystemDeDirectory(userId), userSerial);
            }
        }
        if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && !mOnlyCore) {
            enforceSerialNumber(getDataUserCeDirectory(volumeUuid, userId), userSerial);
            if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
                enforceSerialNumber(getDataSystemCeDirectory(userId), userSerial);
            }
        }

以上四个enforceSerialNumber分别对应的目录为:

//de
/data/user_de/userid/
/data/system_de/userid/
//ce
/data/user/userid/
/data/system_ce/userid/

每个方法最后都会调用到OS的setxattr中去

    private static void setSerialNumber(File file, int serialNumber) throws IOException {
        try {
            final byte[] buf = Integer.toString(serialNumber).getBytes(StandardCharsets.UTF_8);
            Os.setxattr(file.getAbsolutePath(), XATTR_SERIAL, buf, OsConstants.XATTR_CREATE);
        } catch (ErrnoException e) {
            throw e.rethrowAsIOException();
        }
    }

Os.setxattr往user加密所需相应目录inode的属性中加入了key为“user.serial”,value为userinfo::serialNumber的值

阶段三:Installer#createUserData

如上调用序列图,最优的调用逻辑值Utils.cpp中

int ensure_config_user_dirs(userid_t userid) {
    // writable by system, readable by any app within the same user
    const int uid = multiuser_get_uid(userid, AID_SYSTEM);
    const int gid = multiuser_get_uid(userid, AID_EVERYBODY);

    // Ensure /data/misc/user/ exists
    auto path = create_data_misc_legacy_path(userid);
    return fs_prepare_dir(path.c_str(), 0750, uid, gid);
}

创建了/data/misc/user/目录并设置了新的权限,注意这段代码prepareUserStorage#e4crypt_prepare_user_storage中已经覆盖。
不过MANAGE_MISC_DIRS为0,所以e4crypt_prepare_user_storage中的相同代码其实没有走。
另一个注意点就是uid和gid是依据userid生成的,所以该目录的linux层面权限检查会更严格

2.1.2 PMS#createNewUser

    /** Called by UserManagerService */
    void createNewUser(int userId, String[] disallowedPackages) {
        synchronized (mInstallLock) {
            mSettings.createNewUserLI(this, mInstaller, userId, disallowedPackages);
        }
        synchronized (mPackages) {
            scheduleWritePackageRestrictionsLocked(userId);
            scheduleWritePackageListLocked(userId);
            applyFactoryDefaultBrowserLPw(userId);
            primeDomainVerificationsLPw(userId);
        }
    }

整体流程图如下:

PMS#createNewUser

第一步: mSettings.createNewUserLI

Profile User创建流程_createNewUser_createNewUserLI
 void createNewUserLI(@NonNull PackageManagerService service, @NonNull Installer installer,
            int userHandle, String[] disallowedPackages) {
        String[] volumeUuids;
        String[] names;
        int[] appIds;
        String[] seinfos;
        int[] targetSdkVersions;
        int packagesCount;
        synchronized (mPackages) {
            Collection packages = mPackages.values();//获取系统中的应用包名信息
            packagesCount = packages.size();
            //存储保存要安装到新用户的应用信息
            volumeUuids = new String[packagesCount];
            names = new String[packagesCount];
            appIds = new int[packagesCount];
            seinfos = new String[packagesCount];
            targetSdkVersions = new int[packagesCount];

            Iterator packagesIterator = packages.iterator();
            for (int i = 0; i < packagesCount; i++) {//遍历每个应用,只有系统应用才会被安装到新创建的用户下
                PackageSetting ps = packagesIterator.next();
                if (ps.pkg == null || ps.pkg.applicationInfo == null) {
                    continue;
                }
                final boolean shouldInstall = ps.isSystem() &&
                        !ArrayUtils.contains(disallowedPackages, ps.name);
                // Only system apps are initially installed.
                ps.setInstalled(shouldInstall, userHandle);
                if (!shouldInstall) {
                    writeKernelMappingLPr(ps);
                }
                // Need to create a data directory for all apps under this user. Accumulate all
                // required args and call the installer after mPackages lock has been released
                volumeUuids[i] = ps.volumeUuid;
                names[i] = ps.name;
                appIds[i] = ps.appId;
                seinfos[i] = ps.pkg.applicationInfo.seInfo;
                targetSdkVersions[i] = ps.pkg.applicationInfo.targetSdkVersion;
            }
        }
        for (int i = 0; i < packagesCount; i++) {
            if (names[i] == null) {
                continue;
            }
            // TODO: triage flags!
            final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
            try { 
                installer.createAppData(volumeUuids[i], names[i], userHandle, flags, appIds[i],
                        seinfos[i], targetSdkVersions[i]);//在指定的目录创建app数据
            } catch (InstallerException e) {
                Slog.w(TAG, "Failed to prepare app data", e);
            }
        }
        synchronized (mPackages) {
            applyDefaultPreferredAppsLPw(service, userHandle);
        }
    }

以上代码主要做的事情

  • 获取系统中的应用包名信息,过滤应用,只有系统应用才会被安装到新的用户中
  • 对需要安装到新用户的应用执行installer.createAppData操作,其核心逻辑是在InstalldNativeService中

** InstalldNativeService#createAppData **

binder::Status InstalldNativeService::createAppData(const std::unique_ptr& uuid,
        const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
        const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) {
    ENFORCE_UID(AID_SYSTEM);
    CHECK_ARGUMENT_UUID(uuid);
    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
    std::lock_guard lock(mLock);

    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
    const char* pkgname = packageName.c_str();

    // Assume invalid inode unless filled in below
    if (_aidl_return != nullptr) *_aidl_return = -1;

    int32_t uid = multiuser_get_uid(userId, appId);
    int32_t cacheGid = multiuser_get_cache_gid(userId, appId);
    mode_t targetMode = targetSdkVersion >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751;

    // If UID doesn't have a specific cache GID, use UID value
    if (cacheGid == -1) {
        cacheGid = uid;
    }

    if (flags & FLAG_STORAGE_CE) {
        //获取要创建的app目录:data/user/user_id/pkgname/
        auto path = create_data_user_ce_package_path(uuid_, userId, pkgname);
        bool existing = (access(path.c_str(), F_OK) == 0);//检查改目录是否存在
        //创建path/cache,/path/code_cache/目录
        if (prepare_app_dir(path, targetMode, uid) ||
                prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
                prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
            return error("Failed to prepare " + path);
        }

        // Consider restorecon over contents if label changed
       //使用restorecon来恢复app目录中所有文件的SELinux配置信息
        if (restorecon_app_data_lazy(path, seInfo, uid, existing) ||
                restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) ||
                restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) {
            return error("Failed to restorecon " + path);
        }

        // Remember inode numbers of cache directories so that we can clear
        // contents while CE storage is locked
        if (write_path_inode(path, "cache", kXattrInodeCache) ||
                write_path_inode(path, "code_cache", kXattrInodeCodeCache)) {
            return error("Failed to write_path_inode for " + path);
        }

        // And return the CE inode of the top-level data directory so we can
        // clear contents while CE storage is locked
        if ((_aidl_return != nullptr)
                && get_path_inode(path, reinterpret_cast(_aidl_return)) != 0) {
            return error("Failed to get_path_inode for " + path);
        }
    }
    if (flags & FLAG_STORAGE_DE) {
       //获取要创建的app目录:data/user_de/user_id/pkgname/
        auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
        bool existing = (access(path.c_str(), F_OK) == 0);
       //创建path/cache,/path/code_cache/目录
        if (prepare_app_dir(path, targetMode, uid) ||
                prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
                prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
            return error("Failed to prepare " + path);
        }

        // Consider restorecon over contents if label changed
        if (restorecon_app_data_lazy(path, seInfo, uid, existing) ||
                restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) ||
                restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) {
            return error("Failed to restorecon " + path);
        }

        if (prepare_app_quota(uuid, findQuotaDeviceForUuid(uuid), uid)) {
            return error("Failed to set hard quota " + path);
        }

        if (!prepare_app_profile_dir(packageName, appId, userId)) {
            return error("Failed to prepare profiles for " + packageName);
        }
    }
    return ok();
}

以上代码主要完成的事情为创建以下目录:

//ce
/data/user/user_id/pkgname/
/data/user/user_id/pkgname/cache/
/data/user/user_id/pkgname/code_cache/
//de
/data/user_de/user_id/pkgname/
/data/user_de/user_id/pkgname/cache/
/data/user_de/user_id/pkgname/code_cache/
第二步:scheduleWritePackageRestrictionsLocked
scheduleWritePackageRestrictionsLocked

该函数的主要逻辑是依据系统的Settings#mPackages重新创建新用户的/data/system/users/11/package-restrictions.xml文件。

写入的内容主要如下:

  1. 系统应用的pkg信息
  2. 当前用户的preferred-activities信息
  3. 当前用户的crossProfile-intent-filters信息
  4. 当前用户的DefaultApps信息
  5. 当前用户的block-uninstall-packages信息

该文件的主要数据信息为:



    
    
    
    
        
            
        
    
    
        
            
            
                
                
                
            
        
        
            
            
                
                
                
            
        
        
            
            
                
                
                
            
        
        
            
            
                
                
                
            
        
    
    
    
        
            
                
                
                
            
        
        ...
        
            
                
                
                
            
        
        
            
                
                
            
        
        
            
                
                
                
                
            
        
        
            
                
                
                
            
        
    
    

第三步:scheduleWritePackageListLocked#####
scheduleWritePackageListLocked

更新/data/system/packages.list文件

文件格式为:

com.android.fmradio 10019 0 /data/user/0/com.android.fmradio platform:privapp:targetSdkVersion=28 3002,1013
com.mediatek.gba 1001 0 /data/user/0/com.mediatek.gba platform:privapp:targetSdkVersion=26 1065,3002,1023,3003,3001,3007,1002,3010,3011,1004,2002,3006
com.mediatek.ims 1001 0 /data/user/0/com.mediatek.ims platform:privapp:targetSdkVersion=26 1065,3002,1023,3003,3001,3007,1002,3010,3011,1004,2002,3006
com.android.cts.priv.ctsshim 10022 0 /data/user/0/com.android.cts.priv.ctsshim default:privapp:targetSdkVersion=24 none
  • 第一列是app的包名,也就是AndroidManifest.xml文件中的package=”xxx.xxx.xxx”设置的内容
  • 第二列是app的使用的userid(uid), 如果没有在AndroidManifext.xml里使用android:sharedUserId属性指定UID, 在app安装的时候,系统会给这个app自动分配一uid,以后app运行时,就用这个UID运行
  • 第三列是app是否处于调试模式,由AndroidManifext.xml里android:debuggable指定
  • 第四列是app的数据存放路径,一般是”/data/data/${package_name}”这样的文件夹
  • 第五列是app的seinfo信息,这个好像和SEAndroid机制有关,具体我也不是太清楚,它的值好像有platform, default之分
  • 第六列是app所属的user group, 如果一个app不属于任何group, 这里的值是None
第四步:applyFactoryDefaultBrowserLPw#####

检查是否配置了default_browser字符串,如果配置了,并且系统安装了该应用,则设置改应用为默认的浏览器应用

    private void applyFactoryDefaultBrowserLPw(int userId) {
        // The default browser app's package name is stored in a string resource,
        // with a product-specific overlay used for vendor customization.
        String browserPkg = mContext.getResources().getString(
                com.android.internal.R.string.default_browser);
        //start, wangsenhao, for cts default Browser, 2018.10.25
        if (SystemProperties.get("ro.hct_fastpass_defaultBrowser").equals("1")) {
            browserPkg = "com.android.chrome";
        }
        //end, wangsenhao, for cts default Browser, 2018.10.25
        if (!TextUtils.isEmpty(browserPkg)) {
            // non-empty string => required to be a known package
            PackageSetting ps = mSettings.mPackages.get(browserPkg);
            if (ps == null) {
                Slog.e(TAG, "Product default browser app does not exist: " + browserPkg);
                browserPkg = null;
            } else {
                mSettings.setDefaultBrowserPackageNameLPw(browserPkg, userId);
            }
        }

        // Nothing valid explicitly set? Make the factory-installed browser the explicit
        // default.  If there's more than one, just leave everything alone.
        if (browserPkg == null) {
            calculateDefaultBrowserLPw(userId);
        }
    }
第五步:primeDomainVerificationsLPw#####

该方法主要逻辑也更新/data/system/users/user_id/package-restrictions.xml文件

第六步:scheduleWriteSettingsLocked#####

改方法主要逻辑是更新/data/system/packages.xml/文件,改文件的主要目录结构如下:



    
    

    
        
        ... ...
    

    
        ...
    
    ...

    
        ...
    
    ...

    
        ...
    

该文件主要包含以下内容

  • permission块: 里面包含了系统中所有定义的权限的信息
    这个权限包含了系统和应用定义的所有权限,系统是指framework-res.app应用
  
  
  
  • keyset-settings块:里面包含了已安装app签名的public key信息
    
        
            
            
            
            
            
            
        
        
            
                
            
            
                
            
            
                
            
            
                
            
            
                
            
            
                
            
        
        
        
    

keyset-settings块里收集了所有app签名的公钥信息,和后面介绍的package块中的信息有关联。

1. keysets块中包含了很多keyset, 每个keyset都有一个编号用identifier表示,keyset里包含的key-id里的identifier和public-key的identifier的值相对应
2. keys块中public-key里的value值就是从apk包里的签名文件里提取出来的的公钥的值
3. lastIssuedKeyId和lastIssuedKeySetId表示的是最近一次取出来的公钥所属的set编号
  • package块:里面包含了系统中所有安装的app的详细信息
    
        
            
        
        
            
            
            
            
            
            
            
            
        
        
    

package块里包含了每个app的详细信息,具体说明如下:

1. name:app的包名;codePath:表示apk的存放路径
2. nativeLibraryPath表示app使用的.so库存放的位置,primaryCpuAbi表示app以哪种abi架构运行
3. publicFlags和privateFlags应用的flag信息,privateFlags对应的是ApplicationInfo中的各种PRIVATE_FLAG
4. ft表示apk文件上次被更改的时间,it表示app第一次安装的时间,ut表示app上次被更新时间,它的值好像一直和ft相同, ota或app重装之后,这里的ft和ut可能会改变
5. version是app的版本号信息, 也就是在AndroidManifest.xml里配置的android:versioncode
6. userId是为app分配的user id, 如果有使用shareUserId, 这里出现的就是SharedUserId
7. sigs块里的count表示这个app有多少个签名信息,有的app可能会被多个证书签名。cert里的index表示这个app使用的证书的序号,当系统发现一个新的证书,这个号就会加1,key是app使用的证书内容的ascii码值;
8. PKMS在扫apk文件过程中,如果发现它和之前扫描到的apk使用的是相同的签名证书,这里就只会有个index的值,并没有key的值。拥有相同的index的package, 表明它们使用的是相同的签名
9. perms块里是这个app拥有的权限, 对于一般的app,这些权限是在AndroidManifest.xml里写明的;那些使用了相同UID的app, 这里的权限就是所有使用相同UID的app申请的权限的总和;granted表示这个权限是不是已经被允许
10. proper-signing-keyset里的identifier就是上面说的keysets里identifier的值。它是用来标明这个app使用的是哪个公钥
  • shared-user块:里面包含了所有系统定义的shareuser的信息
 
        
            
        
        
            
            
        
   
   
        
            
        
        
            
            
            ...
            
        
    

shared_user获取和添加的逻辑如下

    /** Gets and optionally creates a new shared user id. */
    SharedUserSetting getSharedUserLPw(String name, int pkgFlags, int pkgPrivateFlags,
            boolean create) throws PackageManagerException {
        SharedUserSetting s = mSharedUsers.get(name);
        if (s == null && create) {
            s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
            s.userId = newUserIdLPw(s);
            if (s.userId < 0) {
                // < 0 means we couldn't assign a userid; throw exception
                throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
                        "Creating shared user " + name + " failed");
            }
            Log.i(PackageManagerService.TAG, "New shared user " + name + ": id=" + s.userId);
            mSharedUsers.put(name, s);
        }
        return s;
    }

参考文档:

android 9.x mtk源码
https://blog.csdn.net/weixin_40107510/article/details/78556427
https://www.twblogs.net/a/5b7f307a2b717767c6ae36b4/zh-cn
https://www.jianshu.com/p/d25f73805729

你可能感兴趣的:(Android 9.x 多用户机制 1 #Profile user创建过程)