use of com.android.bundle.Commands.ApkDescription in project bundletool by google.
the class BuildApksPreprocessingTest method localTestingMode_enabled_addsMetadata.
@Test
public void localTestingMode_enabled_addsMetadata() throws Exception {
AppBundle appBundle = createAppBundleWithBaseAndFeatureModules("feature");
new AppBundleSerializer().writeToDisk(appBundle, bundlePath);
BuildApksCommand command = BuildApksCommand.builder().setBundlePath(bundlePath).setOutputFile(outputFilePath).setLocalTestingMode(true).build();
command.execute();
try (ZipFile apkSet = new ZipFile(outputFilePath.toFile())) {
BuildApksResult result = extractTocFromApkSetFile(apkSet, outputDir);
assertThat(result.hasLocalTestingInfo()).isTrue();
assertThat(result.getLocalTestingInfo().getEnabled()).isTrue();
assertThat(result.getLocalTestingInfo().getLocalTestingPath()).isNotEmpty();
ImmutableList<ApkDescription> apkDescriptions = apkDescriptions(result.getVariantList());
assertThat(apkDescriptions).isNotEmpty();
assertThat(apkDescriptions.stream().map(ApkDescription::getPath)).contains("splits/base-master.apk");
for (ApkDescription apkDescription : apkDescriptions) {
File apk = extractFromApkSetFile(apkSet, apkDescription.getPath(), outputDir);
// The local testing metadata is set if and only if the apk is the base master.
assertThat((apkDescription.hasSplitApkMetadata() && apkDescription.getSplitApkMetadata().getSplitId().isEmpty() && apkDescription.getSplitApkMetadata().getIsMasterSplit()) || apkDescription.hasStandaloneApkMetadata()).isEqualTo(extractAndroidManifest(apk).getMetadataValue(LocalTestingPreprocessor.METADATA_NAME).isPresent());
}
}
}
use of com.android.bundle.Commands.ApkDescription in project bundletool by google.
the class BuildApksPreprocessingTest method localTestingMode_disabled_doesNotAddMetadata.
@Test
public void localTestingMode_disabled_doesNotAddMetadata() throws Exception {
AppBundle appBundle = createAppBundleWithBaseAndFeatureModules("feature");
new AppBundleSerializer().writeToDisk(appBundle, bundlePath);
BuildApksCommand command = BuildApksCommand.builder().setBundlePath(bundlePath).setOutputFile(outputFilePath).setLocalTestingMode(false).build();
command.execute();
try (ZipFile apkSet = new ZipFile(outputFilePath.toFile())) {
BuildApksResult result = extractTocFromApkSetFile(apkSet, outputDir);
assertThat(result.getLocalTestingInfo().getEnabled()).isFalse();
ImmutableList<ApkDescription> apkDescriptions = apkDescriptions(result.getVariantList());
assertThat(apkDescriptions).isNotEmpty();
for (ApkDescription apkDescription : apkDescriptions) {
File apk = extractFromApkSetFile(apkSet, apkDescription.getPath(), outputDir);
assertThat(extractAndroidManifest(apk).getMetadataValue(LocalTestingPreprocessor.METADATA_NAME)).isEmpty();
}
}
}
use of com.android.bundle.Commands.ApkDescription in project bundletool by google.
the class BuildApksResourcePinningTest method resourceIds_pinnedToMasterSplits.
@Test
public void resourceIds_pinnedToMasterSplits() throws Exception {
ResourceTable baseResourceTable = new ResourceTableBuilder().addPackage("com.test.app").addStringResource("test_label", "Module title").addFileResourceForMultipleConfigs("drawable", "image", ImmutableMap.of(Configuration.getDefaultInstance(), "res/drawable/image1.jpg", locale("fr"), "res/drawable-fr/image1.jpg")).addFileResourceForMultipleConfigs("drawable", "image2", ImmutableMap.of(Configuration.getDefaultInstance(), "res/drawable/image2.jpg", locale("fr"), "res/drawable-fr/image2.jpg")).build();
ResourceTable featureResourceTable = new ResourceTableBuilder().addPackage("com.test.app.feature", 0x80).addFileResourceForMultipleConfigs("drawable", "image3", ImmutableMap.of(Configuration.getDefaultInstance(), "res/drawable/image3.jpg", locale("fr"), "res/drawable-fr/image3.jpg")).addFileResourceForMultipleConfigs("drawable", "image4", ImmutableMap.of(Configuration.getDefaultInstance(), "res/drawable/image4.jpg", locale("fr"), "res/drawable-fr/image4.jpg")).build();
AppBundle appBundle = new AppBundleBuilder().addModule("base", builder -> builder.addFile("dex/classes.dex").addFile("res/drawable/image1.jpg").addFile("res/drawable-fr/image1.jpg").addFile("res/drawable/image2.jpg").addFile("res/drawable-fr/image2.jpg").setManifest(androidManifest("com.test.app", withMinSdkVersion(14))).setResourceTable(baseResourceTable)).addModule("feature", builder -> builder.addFile("res/drawable/image3.jpg").addFile("res/drawable-fr/image3.jpg").addFile("res/drawable/image4.jpg").addFile("res/drawable-fr/image4.jpg").setManifest(androidManifest("com.test.app", withMinSdkVersion(14), withOnDemandDelivery(), withFusingAttribute(true), withTitle("@string/test_label", 0x7f010000))).setResourceTable(featureResourceTable)).setBundleConfig(BundleConfigBuilder.create().addResourcePinnedToMasterSplit(// image1 from "base" module
0x7f020000).addResourcePinnedToMasterSplit(// image4 from "feature" module
0x80010001).build()).build();
TestComponent.useTestModule(this, TestModule.builder().withAppBundle(appBundle).withOutputPath(outputFilePath).build());
buildApksManager.execute();
ZipFile apkSetFile = new ZipFile(outputFilePath.toFile());
BuildApksResult result = extractTocFromApkSetFile(apkSetFile, outputDir);
// Verifying that standalone APKs contain all entries.
assertThat(standaloneApkVariants(result)).hasSize(1);
List<ApkSet> standaloneApkSets = standaloneApkVariants(result).get(0).getApkSetList();
assertThat(standaloneApkSets).hasSize(1);
List<ApkDescription> standaloneApkDescription = standaloneApkSets.get(0).getApkDescriptionList();
assertThat(standaloneApkDescription).hasSize(1);
File standaloneApkFile = extractFromApkSetFile(apkSetFile, standaloneApkDescription.get(0).getPath(), outputDir);
try (ZipFile standaloneZip = new ZipFile(standaloneApkFile)) {
assertThat(filesUnderPath(standaloneZip, ZipPath.create("res"))).containsExactly("res/drawable/image1.jpg", "res/drawable-fr/image1.jpg", "res/drawable/image2.jpg", "res/drawable-fr/image2.jpg", "res/drawable/image3.jpg", "res/drawable-fr/image3.jpg", "res/drawable/image4.jpg", "res/drawable-fr/image4.jpg", "res/xml/splits0.xml");
}
// Verifying split APKs.
assertThat(splitApkVariants(result)).hasSize(1);
List<ApkSet> splitApkSetList = splitApkVariants(result).get(0).getApkSetList();
Map<String, ApkSet> modules = Maps.uniqueIndex(splitApkSetList, apkSet -> apkSet.getModuleMetadata().getName());
assertThat(modules.keySet()).containsExactly("base", "feature");
List<ApkDescription> baseModuleApks = modules.get("base").getApkDescriptionList();
assertThat(baseModuleApks).hasSize(2);
Map<Boolean, ApkDescription> apkBaseMaster = Maps.uniqueIndex(baseModuleApks, apkDescription -> apkDescription.getSplitApkMetadata().getIsMasterSplit());
ApkDescription baseMaster = apkBaseMaster.get(/* isMasterSplit= */
true);
File baseMasterFile = extractFromApkSetFile(apkSetFile, baseMaster.getPath(), outputDir);
try (ZipFile baseMasterZip = new ZipFile(baseMasterFile)) {
assertThat(filesUnderPath(baseMasterZip, ZipPath.create("res"))).containsExactly("res/drawable/image1.jpg", "res/drawable-fr/image1.jpg", "res/drawable/image2.jpg", "res/xml/splits0.xml", "res/xml/locales_config.xml");
}
ApkDescription baseFr = apkBaseMaster.get(/* isMasterSplit= */
false);
File baseFrFile = extractFromApkSetFile(apkSetFile, baseFr.getPath(), outputDir);
try (ZipFile baseFrZip = new ZipFile(baseFrFile)) {
assertThat(filesUnderPath(baseFrZip, ZipPath.create("res"))).containsExactly("res/drawable-fr/image2.jpg");
}
List<ApkDescription> featureModuleApks = modules.get("feature").getApkDescriptionList();
assertThat(featureModuleApks).hasSize(2);
Map<Boolean, ApkDescription> apkFeatureMaster = Maps.uniqueIndex(featureModuleApks, apkDescription -> apkDescription.getSplitApkMetadata().getIsMasterSplit());
ApkDescription featureMaster = apkFeatureMaster.get(/* isMasterSplit= */
true);
File featureMasterFile = extractFromApkSetFile(apkSetFile, featureMaster.getPath(), outputDir);
try (ZipFile featureMasterZip = new ZipFile(featureMasterFile)) {
assertThat(filesUnderPath(featureMasterZip, ZipPath.create("res"))).containsExactly("res/drawable/image3.jpg", "res/drawable/image4.jpg", "res/drawable-fr/image4.jpg");
}
ApkDescription featureFr = apkFeatureMaster.get(/* isMasterSplit= */
false);
File featureFrFile = extractFromApkSetFile(apkSetFile, featureFr.getPath(), outputDir);
try (ZipFile featureFrZip = new ZipFile(featureFrFile)) {
assertThat(filesUnderPath(featureFrZip, ZipPath.create("res"))).containsExactly("res/drawable-fr/image3.jpg");
}
}
use of com.android.bundle.Commands.ApkDescription in project bundletool by google.
the class ModuleSplitSerializer method serialize.
/**
* Serializes module splits on disk under {@code outputDirectory}.
*
* <p>Returns {@link ApkDescription} for each serialized split keyed by relative path of module
* split.
*/
public ImmutableMap<ZipPath, ApkDescription> serialize(Path outputDirectory, ImmutableMap<ZipPath, ModuleSplit> splitsByRelativePath) {
// Prepare original splits by:
// * signing embedded APKs
// * injecting manifest and resource table as module entries.
ImmutableList<ModuleSplit> preparedSplits = splitsByRelativePath.values().stream().map(apkSigner::signEmbeddedApks).map(ModuleSplitSerializer::injectManifestAndResourceTableAsEntries).collect(toImmutableList());
try (SerializationFilesManager filesManager = new SerializationFilesManager()) {
// Convert module splits to binary format and apply uncompressed globs specified in
// BundleConfig. We do it in this order because as specified in documentation the matching
// for uncompressed globs is done against paths in final APKs.
ImmutableList<ModuleSplit> binarySplits = aapt2ResourceConverter.convert(preparedSplits, filesManager).stream().map(this::applyUncompressedGlobsAndUncompressedNativeLibraries).collect(toImmutableList());
// Build a pack from entries which may be compressed inside final APKs. 'May be
// compressed' means that for these entries we will decide later should they be compressed
// or not based on whether we gain enough savings from compression.
ModuleEntriesPack maybeCompressedEntriesPack = buildCompressedEntriesPack(filesManager.getCompressedResourceEntriesPackPath(), filesManager.getCompressedEntriesPackPath(), binarySplits);
// Build a pack with entries that are uncompressed in final APKs: force uncompressed entries
// + entries that have very low compression ratio.
ModuleEntriesPack uncompressedEntriesPack = buildUncompressedEntriesPack(filesManager.getUncompressedEntriesPackPath(), binarySplits, maybeCompressedEntriesPack);
// Now content of all binary apks is already moved to compressed/uncompressed packs. Delete
// them to free space.
filesManager.closeAndRemoveBinaryApks();
// Merge two packs together, so we have all entries for final APKs inside one pack. If the
// same entry is in both packs we prefer uncompressed one, because it means this entry
// has very low compression ratio, it makes no sense to put it in compressed form.
ModuleEntriesPack allEntriesPack = maybeCompressedEntriesPack.mergeWith(uncompressedEntriesPack);
// Serialize and sign final APKs.
ImmutableList<ListenableFuture<ApkDescription>> apkDescriptions = Streams.zip(splitsByRelativePath.keySet().stream(), binarySplits.stream(), (relativePath, split) -> executorService.submit(() -> serializeAndSignSplit(outputDirectory, relativePath, split, allEntriesPack, uncompressedEntriesPack))).collect(toImmutableList());
return ConcurrencyUtils.waitForAll(apkDescriptions).stream().collect(toImmutableMap(apk -> ZipPath.create(apk.getPath()), identity()));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
use of com.android.bundle.Commands.ApkDescription in project bundletool by google.
the class ModuleSplitSerializer method serializeAndSignSplit.
private ApkDescription serializeAndSignSplit(Path outputDirectory, ZipPath apkRelativePath, ModuleSplit split, ModuleEntriesPack allEntriesPack, ModuleEntriesPack uncompressedEntriesPack) {
Path outputPath = outputDirectory.resolve(apkRelativePath.toString());
ApkDescription apkDescription = ApkDescriptionHelper.createApkDescription(apkRelativePath, split);
serializeSplit(outputPath, split, allEntriesPack, uncompressedEntriesPack);
apkSigner.signApk(outputPath, split);
notifyApkSerialized(apkDescription, split.getSplitType());
return apkDescription;
}
Aggregations