Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,36 @@ private static final class ResolvedResourceSource {
}
}

// Holds the exact archive a CRC32 match came from, so the fallback copy
// reads from that archive even if another APK exposes the same entry name
// with different bytes.
private static final class ZipSource {
final ZipEntry entry;
final SafeZipFile zipFile;

ZipSource(ZipEntry entry, SafeZipFile zipFile) {
this.entry = entry;
this.zipFile = zipFile;
}
}

BundledResourceCopier(Context context) {
this.context = context.getApplicationContext();
}

void copyFromResource(HashMap<String, ArrayList<File>> resToCopy) throws IOException {
void copyFromResource(
HashMap<String, ArrayList<File>> resToCopy,
HashMap<String, Long> crcByFrom
) throws IOException {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
ArrayList<String> apkPaths = collectApkPaths();
HashMap<String, ZipEntry> availableEntries = new HashMap<String, ZipEntry>();
HashMap<String, SafeZipFile> zipFileMap = new HashMap<String, SafeZipFile>();
HashMap<String, SafeZipFile> entryToZipFileMap = new HashMap<String, SafeZipFile>();
// Content checksum index: CRC32 -> matched archive source. Lets us
// locate a file by content when its origin path is not present verbatim
// on device (e.g. APK baseline diff applied on an AAB/split-apk install
// whose res/ paths were shortened). First entry for a given crc wins.
HashMap<Long, ZipSource> crcToEntry = new HashMap<Long, ZipSource>();

try {
for (String apkPath : apkPaths) {
Expand All @@ -55,6 +76,10 @@ void copyFromResource(HashMap<String, ArrayList<File>> resToCopy) throws IOExcep
availableEntries.put(entryName, ze);
entryToZipFileMap.put(entryName, zipFile);
}
long crc = ze.getCrc();
if (crc != -1L && !crcToEntry.containsKey(crc)) {
crcToEntry.put(crc, new ZipSource(ze, zipFile));
}
}
}

Expand All @@ -76,6 +101,7 @@ void copyFromResource(HashMap<String, ArrayList<File>> resToCopy) throws IOExcep

ZipEntry entry = availableEntries.get(fromPath);
String actualSourcePath = fromPath;
SafeZipFile matchedZipFile = null;
ResolvedResourceSource resolvedResource = null;

if (entry == null) {
Expand All @@ -87,10 +113,35 @@ void copyFromResource(HashMap<String, ArrayList<File>> resToCopy) throws IOExcep
}
}

// Content (CRC32) match: robust across APK/AAB packaging because
// the checksum is over the uncompressed file content, not its
// path. Preferred over the resource-id heuristic below.
if (entry == null && crcByFrom != null) {
Long wantedCrc = crcByFrom.get(fromPath);
if (wantedCrc != null) {
ZipSource matched = crcToEntry.get(wantedCrc);
if (matched != null) {
entry = matched.entry;
matchedZipFile = matched.zipFile;
actualSourcePath = matched.entry.getName();
}
}
}

if (entry == null) {
resolvedResource = resolveBundledResource(fromPath);
if (resolvedResource != null) {
actualSourcePath = resolvedResource.assetPath;
// resolveBundledResource resolved the density-correct
// file path; copy that exact entry from the already-open
// archives so the right variant is used. (openRawResource
// would re-resolve the id at the current configuration
// density and ignore the requested one.)
ZipEntry resolvedEntry = availableEntries.get(actualSourcePath);
if (resolvedEntry != null) {
entry = resolvedEntry;
resolvedResource = null;
}
}
}

Expand All @@ -104,7 +155,9 @@ void copyFromResource(HashMap<String, ArrayList<File>> resToCopy) throws IOExcep
if (lastTarget != null) {
UpdateFileUtils.copyFile(lastTarget, target);
} else if (entry != null) {
SafeZipFile sourceZipFile = entryToZipFileMap.get(actualSourcePath);
SafeZipFile sourceZipFile = matchedZipFile != null
? matchedZipFile
: entryToZipFileMap.get(actualSourcePath);
if (sourceZipFile == null) {
sourceZipFile = baseZipFile;
}
Expand Down Expand Up @@ -247,6 +300,9 @@ private ResolvedResourceSource resolveBundledResource(String resourcePath) {
}

private InputStream openResolvedResourceStream(ResolvedResourceSource source) throws IOException {
// Defensive fallback only: reached when the density-resolved assetPath
// is not present as a zip entry in any loaded APK. Best-effort, resolves
// at the current configuration density.
try {
return context.getResources().openRawResource(source.resourceId);
} catch (Resources.NotFoundException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ private static final class PatchArchiveContents {
final ArrayList<String> copyFroms = new ArrayList<String>();
final ArrayList<String> copyTos = new ArrayList<String>();
final ArrayList<String> deletes = new ArrayList<String>();
// Maps a copy source path ("from") to the CRC32 of the file content,
// when provided by the manifest ("copiesCrc"). Lets the resource
// copier locate the file by content if the path is not present on
// device (APK baseline -> AAB install path shortening).
final HashMap<String, Long> copyCrcs = new HashMap<String, Long>();
}

private final Context context;
Expand Down Expand Up @@ -140,8 +145,11 @@ private void appendManifestEntries(
JSONObject manifest,
ArrayList<String> copyFroms,
ArrayList<String> copyTos,
ArrayList<String> deletes
ArrayList<String> deletes,
HashMap<String, Long> copyCrcs
) throws JSONException {
JSONObject copiesCrc = manifest.optJSONObject("copiesCrc");

JSONObject copies = manifest.optJSONObject("copies");
if (copies != null) {
Iterator<?> keys = copies.keys();
Expand All @@ -153,6 +161,11 @@ private void appendManifestEntries(
}
copyFroms.add(from);
copyTos.add(to);
if (copiesCrc != null && copyCrcs != null && copiesCrc.has(to)) {
// Same content => same crc, so grouping multiple "to" under
// one "from" stays consistent.
copyCrcs.put(from, copiesCrc.getLong(to));
}
}
}

Expand Down Expand Up @@ -220,7 +233,8 @@ private PatchArchiveContents extractPatchArchive(File archiveFile, File unzipDir
manifest,
contents.copyFroms,
contents.copyTos,
contents.deletes
contents.deletes,
contents.copyCrcs
);
continue;
}
Expand Down Expand Up @@ -285,7 +299,7 @@ private void doPatchFromApk() throws IOException, JSONException {
originBundleFile.delete();
}

bundledResourceCopier.copyFromResource(copyList);
bundledResourceCopier.copyFromResource(copyList, contents.copyCrcs);
}

private void doPatchFromPpk() throws IOException, JSONException {
Expand Down
Loading