AndroidP RRO overlay (二)

4 篇文章 1 订阅
4 篇文章 0 订阅

RRO 原理 一

1、create Idmap

我们 AndroidP RRO overlay (一)中介绍了怎么使用命令来替换overlay,那接下来就看看执行这个 adb shell cmd overlay enable --user 0 com.android.car.hvacOverlay 之后做了哪些事。
接收这个命令的类是frameworks/base/services/core/java/com/android/server/om/OverlayManagerShellCommand.java

final class OverlayManagerShellCommand extends ShellCommand {
    private final IOverlayManager mInterface;

    OverlayManagerShellCommand(@NonNull final IOverlayManager iom) {
        mInterface = iom;
    }

    @Override
    public int onCommand(@Nullable final String cmd) {
        if (cmd == null) {
            return handleDefaultCommands(cmd);
        }
        final PrintWriter err = getErrPrintWriter();
        try {
            switch (cmd) {
                case "list":
                    return runList();
                case "enable":
                	//接收到enable命令
                    return runEnableDisable(true); 
                case "disable":
                    return runEnableDisable(false);
                case "enable-exclusive":
                    return runEnableExclusive();
                case "set-priority":
                    return runSetPriority();
                default:
                    return handleDefaultCommands(cmd);
            }
        } catch (IllegalArgumentException e) {
            err.println("Error: " + e.getMessage());
        } catch (RemoteException e) {
            err.println("Remote exception: " + e);
        }
        return -1;
    }
     ........省略部分代码.............
    private int runEnableDisable(final boolean enable) throws RemoteException {
        final PrintWriter err = getErrPrintWriter();

        int userId = UserHandle.USER_SYSTEM;
        String opt;
        while ((opt = getNextOption()) != null) {
            switch (opt) {
                case "--user":
                    userId = UserHandle.parseUserArg(getNextArgRequired());
                    break;
                default:
                    err.println("Error: Unknown option: " + opt);
                    return 1;
            }
        }

        final String packageName = getNextArgRequired();
         // mInterface就是OverlayManagerService,
         // packageName = com.android.car.havc, enable = true, usrid = 0
        return mInterface.setEnabled(packageName, enable, userId) ? 0 : 1; 
    }

frameworks/base/services/core/java/com/android/server/om/OverlayManagerService.java

    ........省略部分代码.............
        @Override
        public boolean setEnabled(@Nullable final String packageName, final boolean enable,
                int userId) throws RemoteException {
            if(DEBUG){
                Slog.d(TAG, "setEnabled overlaymanagerservice : "+ packageName);
            }        
            enforceChangeOverlayPackagesPermission("setEnabled");
            userId = handleIncomingUser(userId, "setEnabled");
            if (packageName == null) {
                return false;
            }

            final long ident = Binder.clearCallingIdentity();
            try {
                synchronized (mLock) {
                    // mImpl 就是 OverlayManagerServiceImpl类
                    return mImpl.setEnabled(packageName, enable, userId);
                }
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

frameworks/base/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java

    ........省略部分代码.............
    boolean setEnabled(@NonNull final String packageName, final boolean enable,
            final int userId) {
        if (DEBUG) {
            Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d",
                        packageName, enable, userId));
        }
		//拿到 com.android.car.havc的具体信息
        final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
        if (overlayPackage == null) {
            return false;
        }
        Slog.d(TAG, String.format("setEnabled overlayPackage=%s ",overlayPackage));
        
        // Ignore static overlays.
        if (overlayPackage.isStaticOverlayPackage()) {
            return false;
        }

        try {
            final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
            boolean modified = mSettings.setEnabled(packageName, userId, enable);
            List<String> targetPackages;

			// oi.targetPackageName = com.android.car.havc
			// oi.packageName = com.android.car.havcOverlay
			// updateState()会调到IdmapManager.createIdmap()去创建idmap。
            modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0);
            Slog.d(TAG, String.format("setEnabled modified = " + modified + " oi.targetPackageName = " + oi.targetPackageName));
            if (modified) {
                    mListener.onOverlaysChanged(oi.targetPackageName, userId);
                }

            return true;
        } catch (OverlayManagerSettings.BadKeyException e) {
            return false;
        }
    }
    
    ........省略部分代码.............
	private boolean updateState(@NonNull final String targetPackageName,
            @NonNull final String overlayPackageName, final int userId, final int flags)
            throws OverlayManagerSettings.BadKeyException {

        final PackageInfo targetPackage = mPackageManager.getPackageInfo(targetPackageName, userId);
        final PackageInfo overlayPackage = mPackageManager.getPackageInfo(overlayPackageName,
                userId);

        // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
        // 最好不要overlay android原生的应用,我们这里能走进来是因为,isStaticOverlayPackage()返回了false
        // isStaticOverlayPackage 是要非静态的overlay,一般可以在overlay apk的AndroidManifest.xml里面配置,默认为false
        if (targetPackage != null && overlayPackage != null &&
                !("android".equals(targetPackageName)
                        && overlayPackage.isStaticOverlayPackage())) {
            // 将 targetPackage 和 overlayPackage都传递给createIdmap
            mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
        }

        boolean modified = false;
        if (overlayPackage != null) {
            modified |= mSettings.setBaseCodePath(overlayPackageName, userId,
                    overlayPackage.applicationInfo.getBaseCodePath());
            modified |= mSettings.setCategory(overlayPackageName, userId,
                    overlayPackage.overlayCategory);
        }

        final @OverlayInfo.State int currentState = mSettings.getState(overlayPackageName, userId);
        final @OverlayInfo.State int newState = calculateNewState(targetPackage, overlayPackage,
                userId, flags);
        if (currentState != newState) {
            if (DEBUG) {
                Slog.d(TAG, String.format("%s:%d: %s -> %s",
                            overlayPackageName, userId,
                            OverlayInfo.stateToString(currentState),
                            OverlayInfo.stateToString(newState)));
            }
            modified |= mSettings.setState(overlayPackageName, userId, newState);
        }
        return modified;
    }

frameworks/base/services/core/java/com/android/server/om/IdmapManager.java

    ........省略部分代码.............
    boolean createIdmap(@NonNull final PackageInfo targetPackage,
            @NonNull final PackageInfo overlayPackage, int userId) {
        // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
        Slog.d(TAG, "IdmapManager createIdmap callers=" + Debug.getCallers(10));
        if (DEBUG) {
            Slog.d(TAG, "create idmap for " + targetPackage.packageName + " and "
                    + overlayPackage.packageName + ",userId: "+userId);
        }
        final int sharedGid = UserHandle.getSharedAppGid(targetPackage.applicationInfo.uid);
        final String targetPath = targetPackage.applicationInfo.getBaseCodePath();
        final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath();
        if (DEBUG) {
            Slog.d(TAG, "targetPath = " + targetPath + " overlayPath = " + overlayPath + " sharedGid = " + sharedGid);
         }
        try {
            if(mInstaller == null) {
                Slog.d(TAG, "mInstaller is null ");
            }
            // targetPath = /system/priv-app/CarHvacApp/CarHvacApp.apk
            // overlayPath = /vendor/overlay/hvacOverlay.apk
            // sharedGid 是com.android.hvac 的进程 uid
            mInstaller.idmap(targetPath, overlayPath, sharedGid);
        } catch (InstallerException e) {
            Slog.w(TAG, "failed to generate idmap for " + targetPath + " and "
                    + overlayPath + ": " + e.getMessage());
            return false;
        }
        return true;
    }

frameworks/base/services/core/java/com/android/server/pm/Installer.java

    public void idmap(String targetApkPath, String overlayApkPath, int uid)
            throws InstallerException {
        if (!checkBeforeRemote()) return;
        Slog.d(TAG, " Installer idmap start");
        try {
            if(mInstalld == null) {
                Slog.w(TAG, " mInstalld is null");
            }
            // mInstalld 是 native层的 InstalldNativeService
            mInstalld.idmap(targetApkPath, overlayApkPath, uid);
        } catch (Exception e) {
            throw InstallerException.from(e);
        }
    }

frameworks/native/cmds/installd/InstalldNativeService.cpp


binder::Status InstalldNativeService::idmap(const std::string& targetApkPath,
        const std::string& overlayApkPath, int32_t uid) {
    // 检查用户和路径权限
    ENFORCE_UID(AID_SYSTEM);
    CHECK_ARGUMENT_PATH(targetApkPath);
    CHECK_ARGUMENT_PATH(overlayApkPath);
    std::lock_guard<std::recursive_mutex> lock(mLock);

    const char* target_apk = targetApkPath.c_str();
    const char* overlay_apk = overlayApkPath.c_str();
    
    int idmap_fd = -1;
    char idmap_path[PATH_MAX];
    struct stat idmap_stat;
    bool outdated = false;

    // flatten_path()就是使
    // idmap_path = vendor@overlay@hvacOverlay.apk@system@priv-app@CarHvacApp@CarHvacApp.apk@idmap
    // vendor@overlay@hvacOverlay.apk@system@priv-app@CarHvacApp@CarHvacApp.apk@idmap在实机 /data/resource-cache下
    if (flatten_path(IDMAP_PREFIX, IDMAP_SUFFIX, overlay_apk,target_apk,
                idmap_path, sizeof(idmap_path)) == -1) {
        ALOGE("idmap::InstalldNativeService cannot generate idmap path for overlay %s\n", overlay_apk);
        goto fail;
    }
    // 获取vendor@overlay@hvacOverlay.apk@system@priv-app@CarHvacApp@CarHvacApp.apk@idmap文件信息,并保存在idmap_sta中,
    //结构体 stat 就是保存了文件信息如,文件的设备编号、用户ID、文件字节数等
    
    if (stat(idmap_path, &idmap_stat)< 0) {
        outdated = true;
    } else {
        // 这里面会效验文件vendor@overlay@hvacOverlay.apk@system@priv-app@CarHvacApp@CarHvacApp.apk@idmap
        outdated = delete_stale_idmap(target_apk, overlay_apk, idmap_path, uid);
    }

    if (outdated) {
        idmap_fd = open(idmap_path, O_RDWR | O_CREAT | O_EXCL, 0644);
    } else {
        idmap_fd = open(idmap_path, O_RDWR);
    }

    if (idmap_fd < 0) {
        ALOGE("idmap::InstalldNativeService cannot open '%s' for output: %s\n", idmap_path, strerror(errno));
        goto fail;
    }
    //改变用户和group id
    if (fchown(idmap_fd, AID_SYSTEM, uid) < 0) {
        ALOGE("idmap::InstalldNativeService cannot chown '%s'\n", idmap_path);
        goto fail;
    }
    if (fchmod(idmap_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) {
        ALOGE("idmap::InstalldNativeService cannot chmod '%s'\n", idmap_path);
        goto fail;
    }

    if (!outdated) {
        close(idmap_fd);
        return ok();
    }

    pid_t pid;
    pid = fork();
    ALOGE("idmap::InstalldNativeService fork pid = %d\n", pid);
    if (pid == 0) {
        /* child -- drop privileges before continuing */
        if (setgid(uid) != 0) {
            ALOGE("setgid(%d) failed during idmap\n", uid);
            exit(1);
        }
        if (setuid(uid) != 0) {
            ALOGE("setuid(%d) failed during idmap\n", uid);
            exit(1);
        }
        if (flock(idmap_fd, LOCK_EX | LOCK_NB) != 0) {
            ALOGE("flock(%s) failed during idmap: %s\n", idmap_path, strerror(errno));
            exit(1);
        }
        // 会调到system/bin/installd
        run_idmap(target_apk, overlay_apk, idmap_fd);
        exit(1); /* only if exec call to idmap failed */
    } else {
        int status = wait_child(pid);
        if (status != 0) {
            ALOGE("idmap failed, status=0x%04x\n", status);
            goto fail;
        }
    }

    close(idmap_fd);
    return ok();
fail:
    if (idmap_fd >= 0) {
        close(idmap_fd);
        unlink(idmap_path);
    }
    return error();
}

........省略部分代码.............
static void run_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd)
{
	// static constexpr const char *kIdMapPath = "/system/bin/idmap";
	// execl 执行bin文件,传递参数target_apk、overlay_apk 和 vendor@overlay@hvacOverlay.apk@system@priv-app@CarHvacApp@CarHvacApp.apk@idmap的文件描述符
    execl(kIdMapPath, kIdMapPath, "--fd", target_apk, overlay_apk,
            StringPrintf("%d", idmap_fd).c_str(), (char*)NULL);
    PLOG(ERROR) << "execl (" << kIdMapPath << ") failed";
}

frameworks/base/cmds/idmap/create.cpp

........省略部分代码.............
int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, int fd)
{
    return create_and_write_idmap(target_apk_path, overlay_apk_path, fd, true) == 0 ?
        EXIT_SUCCESS : EXIT_FAILURE;
}


int create_and_write_idmap(const char *target_apk_path, const char *overlay_apk_path,
        int fd, bool check_if_stale)
{
    if (check_if_stale) {
        if (!is_idmap_stale_fd(target_apk_path, overlay_apk_path, fd)) {
            // already up to date -- nothing to do
            return 0;
        }
    }

    uint32_t *data = NULL;
    size_t size;
	// 终于看到了create_idmap, 从create_idmap 读出来的data,都会通过下面的write_idmap存到  vendor@overlay@hvacOverlay.apk@system@priv-app@CarHvacApp@CarHvacApp.apk@idmap中
    if (create_idmap(target_apk_path, overlay_apk_path, &data, &size) == -1) {
        return -1;
    }

    if (write_idmap(fd, data, size) == -1) {
        free(data);
        return -1;
    }

    free(data);
    return 0;
}

int create_idmap(const char *target_apk_path, const char *overlay_apk_path,
            uint32_t **data, size_t *size)
    {
        uint32_t target_crc, overlay_crc;
        if (get_zip_entry_crc(target_apk_path, AssetManager::RESOURCES_FILENAME,
    			&target_crc) == -1) {
            return -1;
        }
        if (get_zip_entry_crc(overlay_apk_path, AssetManager::RESOURCES_FILENAME,
    			&overlay_crc) == -1) {
            return -1;
        }
        ALOGD("create_idmap: target_crc = %d: overlay_crc = %d\n", target_crc, overlay_crc);
        // 最后会调到 AssetManager 的 createIdmap(),这里target_crc 和 overlay_crc应该都是读出来的效验值什么东西吧
        // base crc     0x8bd98da3 
        // overlay crc  0xca131849
        // idmap inspect vendor@overlay@hvacOverlay.apk@system@priv-app@CarHvacApp@CarHvacApp.apk@idmap 可以看到
        AssetManager am;
        bool b = am.createIdmap(target_apk_path, overlay_apk_path, target_crc, overlay_crc,
                data, size);
        return b ? 0 : -1;
    }

frameworks/base/libs/androidfw/AssetManager.cpp

........省略部分代码.............

bool AssetManager::createIdmap(const char* targetApkPath, const char* overlayApkPath,
        uint32_t targetCrc, uint32_t overlayCrc, uint32_t** outData, size_t* outSize)
{
    AutoMutex _l(mLock);
    const String8 paths[2] = { String8(targetApkPath), String8(overlayApkPath) };
    Asset* assets[2] = {NULL, NULL};
    bool ret = false;
    {
        ResTable tables[2];

        for (int i = 0; i < 2; ++i) {
            asset_path ap;
            ap.type = kFileTypeRegular;
            ap.path = paths[i];
            // resources.arsc是个资源索引表,这里具体我也不是很了解,反正可以理解就是它包含了apk所要用的资源并提供了id进行索引吧
            // 拿到targetApkPath的资源索引表
            assets[i] = openNonAssetInPathLocked("resources.arsc",
                    Asset::ACCESS_BUFFER, ap);
            if (assets[i] == NULL) {
                ALOGW("failed to find resources.arsc in %s\n", ap.path.string());
                goto exit;
            }
            if (tables[i].add(assets[i]) != NO_ERROR) {
                ALOGW("failed to add %s to resource table", paths[i].string());
                goto exit;
            }
        }
        // 在targetApkPath所属的对象 ResTable里面调用 createIdmap
        ret = tables[0].createIdmap(tables[1], targetCrc, overlayCrc,
                targetApkPath, overlayApkPath, (void**)outData, outSize) == NO_ERROR;
                
    }

exit:
    delete assets[0];
    delete assets[1];
    return ret;
}

frameworks/base/libs/androidfw/ResourceTypes.cpp
到这里整个createIdmap就算到调用到最后了,至于createIdmap里面细节 请参考这里.


status_t ResTable::createIdmap(const ResTable& overlay,
        uint32_t targetCrc, uint32_t overlayCrc,
        const char* targetPath, const char* overlayPath,
        void** outData, size_t* outSize) const
{
    // see README for details on the format of map
    if (mPackageGroups.size() == 0) {
        ALOGW("idmap: target package has no package groups, cannot create idmap\n");
        return UNKNOWN_ERROR;
    }

    if (mPackageGroups[0]->packages.size() == 0) {
        ALOGW("idmap: target package has no packages in its first package group, "
                "cannot create idmap\n");
        return UNKNOWN_ERROR;
    }

    // The number of resources overlaid that were not explicitly marked overlayable.
    size_t forcedOverlayCount = 0u;

    KeyedVector<uint8_t, IdmapTypeMap> map;

    // overlaid packages are assumed to contain only one package group
    const PackageGroup* pg = mPackageGroups[0];
    // starting size is header
    *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES;

    // target package id and number of types in map
    *outSize += 2 * sizeof(uint16_t);

    // overlay packages are assumed to contain only one package group
    const ResTable_package* overlayPackageStruct = overlay.mPackageGroups[0]->packages[0]->package;
    char16_t tmpName[sizeof(overlayPackageStruct->name)/sizeof(overlayPackageStruct->name[0])];
    strcpy16_dtoh(tmpName, overlayPackageStruct->name, sizeof(overlayPackageStruct->name)/sizeof(overlayPackageStruct->name[0]));
    const String16 overlayPackage(tmpName);

    for (size_t typeIndex = 0; typeIndex < pg->types.size(); ++typeIndex) {
        const TypeList& typeList = pg->types[typeIndex];
        if (typeList.isEmpty()) {
            continue;
        }

        const Type* typeConfigs = typeList[0];

        IdmapTypeMap typeMap;
        typeMap.overlayTypeId = -1;
        typeMap.entryOffset = 0;

        for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) {
            uint32_t resID = Res_MAKEID(pg->id - 1, typeIndex, entryIndex);
            resource_name resName;
            if (!this->getResourceName(resID, false, &resName)) {
                if (typeMap.entryMap.isEmpty()) {
                    typeMap.entryOffset++;
                }
                continue;
            }

            uint32_t typeSpecFlags = 0u;
            const String16 overlayType(resName.type, resName.typeLen);
            const String16 overlayName(resName.name, resName.nameLen);
            uint32_t overlayResID = overlay.identifierForName(overlayName.string(),
                                                              overlayName.size(),
                                                              overlayType.string(),
                                                              overlayType.size(),
                                                              overlayPackage.string(),
                                                              overlayPackage.size(),
                                                              &typeSpecFlags);
            if (overlayResID == 0) {
                // No such target resource was found.
                if (typeMap.entryMap.isEmpty()) {
                    typeMap.entryOffset++;
                }
                continue;
            }

            // Now that we know this is being overlaid, check if it can be, and emit a warning if
            // it can't.
            if ((dtohl(typeConfigs->typeSpecFlags[entryIndex]) &
                    ResTable_typeSpec::SPEC_OVERLAYABLE) == 0) {
                forcedOverlayCount++;
            }

            if (typeMap.overlayTypeId == -1) {
                typeMap.overlayTypeId = Res_GETTYPE(overlayResID) + 1;
            }

            if (Res_GETTYPE(overlayResID) + 1 != static_cast<size_t>(typeMap.overlayTypeId)) {
                ALOGE("idmap: can't mix type ids in entry map. Resource 0x%08x maps to 0x%08x"
                        " but entries should map to resources of type %02zx",
                        resID, overlayResID, typeMap.overlayTypeId);
                return BAD_TYPE;
            }

            if (typeMap.entryOffset + typeMap.entryMap.size() < entryIndex) {
                // pad with 0xffffffff's (indicating non-existing entries) before adding this entry
                size_t index = typeMap.entryMap.size();
                size_t numItems = entryIndex - (typeMap.entryOffset + index);
                if (typeMap.entryMap.insertAt(0xffffffff, index, numItems) < 0) {
                    return NO_MEMORY;
                }
            }
            typeMap.entryMap.add(Res_GETENTRY(overlayResID));
        }

        if (!typeMap.entryMap.isEmpty()) {
            if (map.add(static_cast<uint8_t>(typeIndex), typeMap) < 0) {
                return NO_MEMORY;
            }
            *outSize += (4 * sizeof(uint16_t)) + (typeMap.entryMap.size() * sizeof(uint32_t));
        }
    }

    if (map.isEmpty()) {
        ALOGW("idmap: no resources in overlay package present in base package");
        return UNKNOWN_ERROR;
    }

    if ((*outData = malloc(*outSize)) == NULL) {
        return NO_MEMORY;
    }

    uint32_t* data = (uint32_t*)*outData;
    *data++ = htodl(IDMAP_MAGIC);
    *data++ = htodl(ResTable::IDMAP_CURRENT_VERSION);
    *data++ = htodl(targetCrc);
    *data++ = htodl(overlayCrc);
    const char* paths[] = { targetPath, overlayPath };
    for (int j = 0; j < 2; ++j) {
        char* p = (char*)data;
        const char* path = paths[j];
        const size_t I = strlen(path);
        if (I > 255) {
            ALOGV("path exceeds expected 255 characters: %s\n", path);
            return UNKNOWN_ERROR;
        }
        for (size_t i = 0; i < 256; ++i) {
            *p++ = i < I ? path[i] : '\0';
        }
        data += 256 / sizeof(uint32_t);
    }
    const size_t mapSize = map.size();
    uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
    *typeData++ = htods(pg->id);
    *typeData++ = htods(mapSize);
    for (size_t i = 0; i < mapSize; ++i) {
        uint8_t targetTypeId = map.keyAt(i);
        const IdmapTypeMap& typeMap = map[i];
        *typeData++ = htods(targetTypeId + 1);
        *typeData++ = htods(typeMap.overlayTypeId);
        *typeData++ = htods(typeMap.entryMap.size());
        *typeData++ = htods(typeMap.entryOffset);

        const size_t entryCount = typeMap.entryMap.size();
        uint32_t* entries = reinterpret_cast<uint32_t*>(typeData);
        for (size_t j = 0; j < entryCount; j++) {
            entries[j] = htodl(typeMap.entryMap[j]);
        }
        typeData += entryCount * 2;
    }

    if (forcedOverlayCount > 0) {
        ALOGW("idmap: overlaid %zu resources not marked overlayable", forcedOverlayCount);
    }

    return NO_ERROR;
}

createIdmap之后就会进行activity重启,见原理三.

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值