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重启,见原理三.