Preserve compression for resources in ProtoApk#copy()
Previously all resources were stored uncompressed in copies - when resource
shrinking is enabled, for example. This required relinking the APK downstream
to correct compression, which is an unintended use of aapt2's shared library
support.
PiperOrigin-RevId: 370762729
diff --git a/src/tools/android/java/com/google/devtools/build/android/aapt2/ProtoApk.java b/src/tools/android/java/com/google/devtools/build/android/aapt2/ProtoApk.java
index 21d64f7..268043f 100644
--- a/src/tools/android/java/com/google/devtools/build/android/aapt2/ProtoApk.java
+++ b/src/tools/android/java/com/google/devtools/build/android/aapt2/ProtoApk.java
@@ -42,6 +42,7 @@
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.io.ByteStreams;
import com.google.common.xml.XmlEscapers;
import com.google.devtools.build.android.AndroidResourceOutputs.UniqueZipBuilder;
@@ -133,28 +134,35 @@
*/
public ProtoApk copy(Path destination, BiPredicate<ResourceType, String> resourceFilter)
throws IOException {
+ final ResourceTable resourceTable = getResourceTable();
+ final ResourceTable.Builder dstTableBuilder =
+ resourceTable.toBuilder()
+ .addToolFingerprint(
+ ToolFingerprint.newBuilder().setTool("ResourceProcessorBusyBox")
+ // NB: "stamp" information should go here, but that's not available:
+ // https://github.com/bazelbuild/bazel/blob/78bb263e46bf301900c1d4b1e04fabf3a6854762/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaRuleClasses.java#L380
+ );
- final URI dstZipUri = URI.create("jar:" + destination.toUri());
+ dstTableBuilder.clearPackage(); // we'll add these back, with filtering below
+
+ // keep track of all resource files still referenced in the (potentially stripped) table
+ ImmutableSet.Builder<String> resToKeepBuilder = ImmutableSet.builder();
+ for (Package pkg : resourceTable.getPackageList()) {
+ Package dstPkg = copyPackage(resourceFilter, resToKeepBuilder, pkg);
+ dstTableBuilder.addPackage(dstPkg);
+ }
+ ImmutableSet<String> resToKeep = resToKeepBuilder.build();
+
try (final ZipFile srcZip = new ZipFile(uri.getPath());
final UniqueZipBuilder dstZip = UniqueZipBuilder.createFor(destination)) {
- final ResourceTable resourceTable = getResourceTable();
- final ResourceTable.Builder dstTableBuilder =
- resourceTable.toBuilder()
- .addToolFingerprint(
- ToolFingerprint.newBuilder().setTool("ResourceProcessorBusyBox")
- // NB: "stamp" information should go here, but that's not available:
- // https://github.com/bazelbuild/bazel/blob/78bb263e46bf301900c1d4b1e04fabf3a6854762/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaRuleClasses.java#L380
- );
-
- dstTableBuilder.clearPackage(); // we'll add these back, with filtering below
- for (Package pkg : resourceTable.getPackageList()) {
- Package dstPkg = copyPackage(resourceFilter, dstZip, pkg);
- dstTableBuilder.addPackage(dstPkg);
- }
dstZip.addEntry(RESOURCE_TABLE, dstTableBuilder.build().toByteArray(), ZipEntry.DEFLATED);
+ // the rest are pure copies, and could potentially be more efficiently done via ZipIn/ZipOut
srcZip.stream()
.filter(not(ZipEntry::isDirectory))
- .filter(entry -> !entry.getName().startsWith(RES_DIRECTORY + "/"))
+ .filter(
+ entry ->
+ !entry.getName().startsWith(RES_DIRECTORY + "/")
+ || resToKeep.contains(entry.getName()))
.filter(entry -> !entry.getName().equals(RESOURCE_TABLE))
.forEach(
entry -> {
@@ -170,7 +178,7 @@
});
}
- return readFrom(dstZipUri);
+ return readFrom(URI.create("jar:" + destination.toUri()));
}
/**
@@ -187,19 +195,21 @@
}
private Package copyPackage(
- BiPredicate<ResourceType, String> resourceFilter, UniqueZipBuilder dstZip, Package pkg)
+ BiPredicate<ResourceType, String> resourceFilter,
+ ImmutableSet.Builder<String> resToKeep,
+ Package pkg)
throws IOException {
Package.Builder dstPkgBuilder = Package.newBuilder(pkg);
dstPkgBuilder.clearType();
for (Resources.Type type : pkg.getTypeList()) {
- copyResourceType(resourceFilter, dstZip, dstPkgBuilder, type);
+ copyResourceType(resourceFilter, resToKeep, dstPkgBuilder, type);
}
return dstPkgBuilder.build();
}
private void copyResourceType(
BiPredicate<ResourceType, String> resourceFilter,
- UniqueZipBuilder dstZip,
+ ImmutableSet.Builder<String> resToKeep,
Package.Builder dstPkgBuilder,
Resources.Type type)
throws IOException {
@@ -209,7 +219,7 @@
ResourceType resourceType = ResourceType.getEnum(type.getName());
for (Entry entry : type.getEntryList()) {
if (resourceFilter.test(resourceType, entry.getName())) {
- copyEntry(dstZip, dstTypeBuilder, entry);
+ copyEntry(resToKeep, dstTypeBuilder, entry);
}
}
final Resources.Type dstType = dstTypeBuilder.build();
@@ -218,18 +228,15 @@
}
}
- private void copyEntry(UniqueZipBuilder dstZip, Type.Builder dstTypeBuilder, Entry entry)
+ private void copyEntry(
+ ImmutableSet.Builder<String> resToKeep, Type.Builder dstTypeBuilder, Entry entry)
throws IOException {
dstTypeBuilder.addEntry(Entry.newBuilder(entry));
for (ConfigValue configValue : entry.getConfigValueList()) {
if (configValue.hasValue()
&& configValue.getValue().hasItem()
&& configValue.getValue().getItem().hasFile()) {
- final String path = configValue.getValue().getItem().getFile().getPath();
- final Path apkFileSystemPath = apkFileSystem.getPath(path);
- createDirectories(dstZip, apkFileSystemPath.getParent());
- byte[] content = Files.readAllBytes(apkFileSystemPath);
- dstZip.addEntry(path, content, ZipEntry.STORED);
+ resToKeep.add(configValue.getValue().getItem().getFile().getPath());
}
}
}