diff --git a/README.md b/README.md index e89d66fb..2cc13c95 100644 --- a/README.md +++ b/README.md @@ -31,4 +31,4 @@ https://developer.android.com/studio/command-line/bundletool ## Releases -Latest release: [1.7.0](https://github.com/google/bundletool/releases) +Latest release: [1.7.1](https://github.com/google/bundletool/releases) diff --git a/gradle.properties b/gradle.properties index 3bf72790..1742301f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -release_version = 1.7.0 +release_version = 1.7.1 diff --git a/src/main/java/com/android/tools/build/bundletool/commands/BuildApksCommand.java b/src/main/java/com/android/tools/build/bundletool/commands/BuildApksCommand.java index dedae65f..93cfb975 100644 --- a/src/main/java/com/android/tools/build/bundletool/commands/BuildApksCommand.java +++ b/src/main/java/com/android/tools/build/bundletool/commands/BuildApksCommand.java @@ -153,7 +153,7 @@ public enum OutputFormat { private static final Flag DEVICE_SPEC_FLAG = Flag.path("device-spec"); private static final Flag> SYSTEM_APK_OPTIONS = Flag.enumSet("system-apk-options", SystemApkOption.class); - private static final Flag DEVICE_TIER_FLAG = Flag.string("device-tier"); + private static final Flag DEVICE_TIER_FLAG = Flag.nonNegativeInteger("device-tier"); private static final Flag VERBOSE_FLAG = Flag.booleanFlag("verbose"); @@ -206,7 +206,7 @@ public enum OutputFormat { public abstract Optional getDeviceSpec(); - public abstract Optional getDeviceTier(); + public abstract Optional getDeviceTier(); public abstract ImmutableSet getSystemApkOptions(); @@ -340,7 +340,7 @@ public Builder setDeviceSpec(Path deviceSpecFile) { * Sets the device tier to use for APK matching. This will override the device tier of the given * device spec. */ - public abstract Builder setDeviceTier(String deviceTier); + public abstract Builder setDeviceTier(Integer deviceTier); /** Sets options to generated APKs in system mode. */ public abstract Builder setSystemApkOptions(ImmutableSet options); diff --git a/src/main/java/com/android/tools/build/bundletool/commands/BuildApksManager.java b/src/main/java/com/android/tools/build/bundletool/commands/BuildApksManager.java index 49e49835..c6145d63 100644 --- a/src/main/java/com/android/tools/build/bundletool/commands/BuildApksManager.java +++ b/src/main/java/com/android/tools/build/bundletool/commands/BuildApksManager.java @@ -58,6 +58,7 @@ import com.android.tools.build.bundletool.validation.AppBundleValidator; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -108,6 +109,7 @@ public final class BuildApksManager { } public void execute() throws IOException { + ImmutableSet permanentlyFusedModules = ImmutableSet.of(); ImmutableSet requestedModules = command.getModules().isEmpty() ? ImmutableSet.of() @@ -131,6 +133,9 @@ public void execute() throws IOException { AppBundleValidator bundleValidator = AppBundleValidator.create(command.getExtraValidators()); bundleValidator.validate(mergedAppBundle); generatedApksBuilder.setSplitApks(generateSplitApks(mergedAppBundle)); + permanentlyFusedModules = + Sets.difference(appBundle.getModules().keySet(), mergedAppBundle.getModules().keySet()) + .immutableCopy(); } // Instant APKs @@ -192,7 +197,8 @@ public void execute() throws IOException { generatedAssetSlices.build(), command.getApkBuildMode(), deviceSpec, - getLocalTestingInfo(appBundle)); + getLocalTestingInfo(appBundle), + permanentlyFusedModules); if (command.getOverwriteOutput()) { Files.deleteIfExists(command.getOutputFile()); diff --git a/src/main/java/com/android/tools/build/bundletool/commands/BuildApksModule.java b/src/main/java/com/android/tools/build/bundletool/commands/BuildApksModule.java index e7e482f8..37f59b7a 100644 --- a/src/main/java/com/android/tools/build/bundletool/commands/BuildApksModule.java +++ b/src/main/java/com/android/tools/build/bundletool/commands/BuildApksModule.java @@ -31,6 +31,7 @@ import com.android.tools.build.bundletool.optimizations.ApkOptimizations; import com.android.tools.build.bundletool.optimizations.OptimizationsMerger; import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.protobuf.Int32Value; import dagger.Module; import dagger.Provides; import java.io.PrintStream; @@ -130,7 +131,10 @@ static Optional provideDeviceSpec(BuildApksCommand command) { checkState(deviceSpec.isPresent(), "Device tier specified but no device was provided."); deviceSpec = deviceSpec.map( - spec -> spec.toBuilder().setDeviceTier(command.getDeviceTier().get()).build()); + spec -> + spec.toBuilder() + .setDeviceTier(Int32Value.of(command.getDeviceTier().get())) + .build()); } return deviceSpec; } diff --git a/src/main/java/com/android/tools/build/bundletool/commands/ExtractApksCommand.java b/src/main/java/com/android/tools/build/bundletool/commands/ExtractApksCommand.java index 1c65040f..c322f49d 100644 --- a/src/main/java/com/android/tools/build/bundletool/commands/ExtractApksCommand.java +++ b/src/main/java/com/android/tools/build/bundletool/commands/ExtractApksCommand.java @@ -52,6 +52,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.io.ByteStreams; +import com.google.protobuf.Int32Value; import com.google.protobuf.util.JsonFormat; import java.io.IOException; import java.io.InputStream; @@ -333,20 +334,21 @@ private static Path createTempDirectory() { } private static DeviceSpec applyDefaultsToDeviceSpec(DeviceSpec deviceSpec, BuildApksResult toc) { - if (!deviceSpec.getDeviceTier().isEmpty()) { + if (deviceSpec.hasDeviceTier()) { return deviceSpec; } - Optional defaultDeviceTier = + int defaultDeviceTier = toc.getDefaultTargetingValueList().stream() .filter( defaultTargetingValue -> defaultTargetingValue.getDimension().equals(Value.DEVICE_TIER)) .map(DefaultTargetingValue::getDefaultValue) - .collect(toOptional()); - if (defaultDeviceTier.isPresent()) { - return deviceSpec.toBuilder().setDeviceTier(defaultDeviceTier.get()).build(); - } - return deviceSpec; + // Don't fail if the default value is empty. + .filter(defaultValue -> !defaultValue.isEmpty()) + .map(Integer::parseInt) + .collect(toOptional()) + .orElse(0); + return deviceSpec.toBuilder().setDeviceTier(Int32Value.of(defaultDeviceTier)).build(); } public static CommandHelp help() { diff --git a/src/main/java/com/android/tools/build/bundletool/commands/GetDeviceSpecCommand.java b/src/main/java/com/android/tools/build/bundletool/commands/GetDeviceSpecCommand.java index edcfa8d0..bc517e3c 100644 --- a/src/main/java/com/android/tools/build/bundletool/commands/GetDeviceSpecCommand.java +++ b/src/main/java/com/android/tools/build/bundletool/commands/GetDeviceSpecCommand.java @@ -35,6 +35,7 @@ import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableSet; import com.google.common.io.MoreFiles; +import com.google.protobuf.Int32Value; import com.google.protobuf.util.JsonFormat; import java.io.IOException; import java.io.UncheckedIOException; @@ -55,7 +56,7 @@ public abstract class GetDeviceSpecCommand { private static final Flag DEVICE_ID_FLAG = Flag.string("device-id"); private static final Flag OUTPUT_FLAG = Flag.path("output"); private static final Flag OVERWRITE_OUTPUT_FLAG = Flag.booleanFlag("overwrite"); - private static final Flag DEVICE_TIER_FLAG = Flag.string("device-tier"); + private static final Flag DEVICE_TIER_FLAG = Flag.nonNegativeInteger("device-tier"); private static final Flag> DEVICE_GROUPS_FLAG = Flag.stringSet("device-groups"); @@ -74,7 +75,7 @@ public abstract class GetDeviceSpecCommand { abstract AdbServer getAdbServer(); - public abstract Optional getDeviceTier(); + public abstract Optional getDeviceTier(); public abstract Optional> getDeviceGroups(); @@ -102,7 +103,7 @@ public abstract static class Builder { /** The caller is responsible for the lifecycle of the {@link AdbServer}. */ public abstract Builder setAdbServer(AdbServer adbServer); - public abstract Builder setDeviceTier(String deviceTier); + public abstract Builder setDeviceTier(Integer deviceTier); public abstract Builder setDeviceGroups(ImmutableSet deviceGroups); @@ -160,7 +161,8 @@ public DeviceSpec execute() { adb.init(getAdbPath()); DeviceSpec deviceSpec = new DeviceAnalyzer(adb).getDeviceSpec(getDeviceId()); if (getDeviceTier().isPresent()) { - deviceSpec = deviceSpec.toBuilder().setDeviceTier(getDeviceTier().get()).build(); + deviceSpec = + deviceSpec.toBuilder().setDeviceTier(Int32Value.of(getDeviceTier().get())).build(); } if (getDeviceGroups().isPresent()) { deviceSpec = deviceSpec.toBuilder().addAllDeviceGroups(getDeviceGroups().get()).build(); diff --git a/src/main/java/com/android/tools/build/bundletool/commands/InstallApksCommand.java b/src/main/java/com/android/tools/build/bundletool/commands/InstallApksCommand.java index 3760a2d0..d5080bdc 100644 --- a/src/main/java/com/android/tools/build/bundletool/commands/InstallApksCommand.java +++ b/src/main/java/com/android/tools/build/bundletool/commands/InstallApksCommand.java @@ -49,6 +49,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; +import com.google.protobuf.Int32Value; import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; @@ -66,7 +67,7 @@ public abstract class InstallApksCommand { private static final Flag> MODULES_FLAG = Flag.stringSet("modules"); private static final Flag ALLOW_DOWNGRADE_FLAG = Flag.booleanFlag("allow-downgrade"); private static final Flag ALLOW_TEST_ONLY_FLAG = Flag.booleanFlag("allow-test-only"); - private static final Flag DEVICE_TIER_FLAG = Flag.string("device-tier"); + private static final Flag DEVICE_TIER_FLAG = Flag.nonNegativeInteger("device-tier"); private static final Flag> DEVICE_GROUPS_FLAG = Flag.stringSet("device-groups"); private static final Flag> ADDITIONAL_LOCAL_TESTING_FILES_FLAG = @@ -88,7 +89,7 @@ public abstract class InstallApksCommand { public abstract boolean getAllowTestOnly(); - public abstract Optional getDeviceTier(); + public abstract Optional getDeviceTier(); public abstract Optional> getDeviceGroups(); @@ -123,7 +124,7 @@ public abstract static class Builder { public abstract Builder setAllowTestOnly(boolean allowTestOnly); - public abstract Builder setDeviceTier(String deviceTier); + public abstract Builder setDeviceTier(Integer deviceTier); public abstract Builder setDeviceGroups(ImmutableSet deviceGroups); @@ -149,7 +150,7 @@ public static InstallApksCommand fromFlags( Optional> modules = MODULES_FLAG.getValue(flags); Optional allowDowngrade = ALLOW_DOWNGRADE_FLAG.getValue(flags); Optional allowTestOnly = ALLOW_TEST_ONLY_FLAG.getValue(flags); - Optional deviceTier = DEVICE_TIER_FLAG.getValue(flags); + Optional deviceTier = DEVICE_TIER_FLAG.getValue(flags); Optional> deviceGroups = DEVICE_GROUPS_FLAG.getValue(flags); Optional> additionalLocalTestingFiles = ADDITIONAL_LOCAL_TESTING_FILES_FLAG.getValue(flags); @@ -181,7 +182,8 @@ public void execute() { try (TempDirectory tempDirectory = new TempDirectory()) { DeviceSpec deviceSpec = new DeviceAnalyzer(adbServer).getDeviceSpec(getDeviceId()); if (getDeviceTier().isPresent()) { - deviceSpec = deviceSpec.toBuilder().setDeviceTier(getDeviceTier().get()).build(); + deviceSpec = + deviceSpec.toBuilder().setDeviceTier(Int32Value.of(getDeviceTier().get())).build(); } if (getDeviceGroups().isPresent()) { deviceSpec = deviceSpec.toBuilder().addAllDeviceGroups(getDeviceGroups().get()).build(); diff --git a/src/main/java/com/android/tools/build/bundletool/device/AbstractSizeAggregator.java b/src/main/java/com/android/tools/build/bundletool/device/AbstractSizeAggregator.java index 8cee3f4a..c51f2201 100644 --- a/src/main/java/com/android/tools/build/bundletool/device/AbstractSizeAggregator.java +++ b/src/main/java/com/android/tools/build/bundletool/device/AbstractSizeAggregator.java @@ -262,7 +262,7 @@ protected SizeConfiguration getSizeConfiguration( } if (dimensions.contains(DEVICE_TIER)) { - SizeConfiguration.getDeviceTierName(deviceTierTargeting) + SizeConfiguration.getDeviceTierLevel(deviceTierTargeting) .ifPresent(sizeConfiguration::setDeviceTier); } @@ -319,7 +319,7 @@ protected SizeConfiguration mergeWithDeviceSpec( } if (dimensions.contains(DEVICE_TIER) && !isDeviceTierMissing(deviceSpec)) { - mergedSizeConfiguration.setDeviceTier(deviceSpec.getDeviceTier()); + mergedSizeConfiguration.setDeviceTier(deviceSpec.getDeviceTier().getValue()); } return mergedSizeConfiguration.build(); diff --git a/src/main/java/com/android/tools/build/bundletool/device/ApkMatcher.java b/src/main/java/com/android/tools/build/bundletool/device/ApkMatcher.java index ac9cb23d..be15d535 100644 --- a/src/main/java/com/android/tools/build/bundletool/device/ApkMatcher.java +++ b/src/main/java/com/android/tools/build/bundletool/device/ApkMatcher.java @@ -29,6 +29,7 @@ import com.android.bundle.Commands.BuildApksResult; import com.android.bundle.Commands.DeliveryType; import com.android.bundle.Commands.ModuleMetadata; +import com.android.bundle.Commands.PermanentlyFusedModule; import com.android.bundle.Commands.Variant; import com.android.bundle.Devices.DeviceSpec; import com.android.bundle.Targeting.ApkTargeting; @@ -252,13 +253,17 @@ private void validateVariant(Variant variant, BuildApksResult buildApksResult) { Set availableModules = Sets.union( - variant.getApkSetList().stream() - .map(ApkSet::getModuleMetadata) - .map(ModuleMetadata::getName) - .collect(toImmutableSet()), - buildApksResult.getAssetSliceSetList().stream() - .map(AssetSliceSet::getAssetModuleMetadata) - .map(AssetModuleMetadata::getName) + Sets.union( + variant.getApkSetList().stream() + .map(ApkSet::getModuleMetadata) + .map(ModuleMetadata::getName) + .collect(toImmutableSet()), + buildApksResult.getAssetSliceSetList().stream() + .map(AssetSliceSet::getAssetModuleMetadata) + .map(AssetModuleMetadata::getName) + .collect(toImmutableSet())), + buildApksResult.getPermanentlyFusedModulesList().stream() + .map(PermanentlyFusedModule::getName) .collect(toImmutableSet())); Set unknownModules = Sets.difference(requestedModuleNames.get(), availableModules); if (!unknownModules.isEmpty()) { diff --git a/src/main/java/com/android/tools/build/bundletool/device/DeviceGroupModuleMatcher.java b/src/main/java/com/android/tools/build/bundletool/device/DeviceGroupModuleMatcher.java index c7e31142..5e310a0a 100644 --- a/src/main/java/com/android/tools/build/bundletool/device/DeviceGroupModuleMatcher.java +++ b/src/main/java/com/android/tools/build/bundletool/device/DeviceGroupModuleMatcher.java @@ -44,7 +44,7 @@ public boolean matchesTargeting(DeviceGroupModuleTargeting targetingValue) { @Override protected boolean isDeviceDimensionPresent() { - return !getDeviceSpec().getDeviceTier().isEmpty(); + return !getDeviceSpec().getDeviceGroupsList().isEmpty(); } @Override diff --git a/src/main/java/com/android/tools/build/bundletool/device/DeviceSpecUtils.java b/src/main/java/com/android/tools/build/bundletool/device/DeviceSpecUtils.java index 6d32681c..65eb433c 100644 --- a/src/main/java/com/android/tools/build/bundletool/device/DeviceSpecUtils.java +++ b/src/main/java/com/android/tools/build/bundletool/device/DeviceSpecUtils.java @@ -36,6 +36,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Streams; +import com.google.protobuf.Int32Value; import java.util.Optional; /** Utils for {@link DeviceSpec}. */ @@ -63,7 +64,7 @@ public static boolean isTextureCompressionFormatMissing(DeviceSpec deviceSpec) { } public static boolean isDeviceTierMissing(DeviceSpec deviceSpec) { - return deviceSpec.getDeviceTier().isEmpty(); + return !deviceSpec.hasDeviceTier(); } /** Extracts the GL ES version, if any, form the device features. */ @@ -163,7 +164,8 @@ DeviceSpecFromTargetingBuilder setSupportedTextureCompressionFormats( DeviceSpecFromTargetingBuilder setDeviceTier(DeviceTierTargeting deviceTierTargeting) { if (!deviceTierTargeting.equals(DeviceTierTargeting.getDefaultInstance())) { - deviceSpec.setDeviceTier(Iterables.getOnlyElement(deviceTierTargeting.getValueList())); + deviceSpec.setDeviceTier( + Int32Value.of(Iterables.getOnlyElement(deviceTierTargeting.getValueList()).getValue())); } return this; } diff --git a/src/main/java/com/android/tools/build/bundletool/device/DeviceTierApkMatcher.java b/src/main/java/com/android/tools/build/bundletool/device/DeviceTierApkMatcher.java index 64e38605..533bf49f 100644 --- a/src/main/java/com/android/tools/build/bundletool/device/DeviceTierApkMatcher.java +++ b/src/main/java/com/android/tools/build/bundletool/device/DeviceTierApkMatcher.java @@ -16,6 +16,8 @@ package com.android.tools.build.bundletool.device; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.ImmutableSet.toImmutableSet; +import static java.util.stream.Collectors.joining; import com.android.bundle.Devices.DeviceSpec; import com.android.bundle.Targeting.ApkTargeting; @@ -25,6 +27,8 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.google.common.collect.Sets.SetView; +import com.google.common.collect.Streams; +import com.google.protobuf.Int32Value; /** * A {@link TargetingDimensionMatcher} that provides APK matching on device tier. @@ -55,22 +59,26 @@ public boolean matchesTargeting(DeviceTierTargeting targeting) { return true; } - ImmutableSet values = ImmutableSet.copyOf(targeting.getValueList()); - ImmutableSet alternatives = ImmutableSet.copyOf(targeting.getAlternativesList()); + ImmutableSet values = + targeting.getValueList().stream().map(Int32Value::getValue).collect(toImmutableSet()); + ImmutableSet alternatives = + targeting.getAlternativesList().stream() + .map(Int32Value::getValue) + .collect(toImmutableSet()); - SetView intersection = Sets.intersection(values, alternatives); + SetView intersection = Sets.intersection(values, alternatives); checkArgument( intersection.isEmpty(), "Expected targeting values and alternatives to be mutually exclusive, but both contain: %s", intersection); - if (getDeviceSpec().getDeviceTier().isEmpty()) { + if (!getDeviceSpec().hasDeviceTier()) { throw InvalidDeviceSpecException.builder() .withUserMessage( "The bundle uses device tier targeting, but no device tier was specified.") .build(); } - if (values.contains(getDeviceSpec().getDeviceTier())) { + if (values.contains(getDeviceSpec().getDeviceTier().getValue())) { return true; } return false; @@ -78,7 +86,7 @@ public boolean matchesTargeting(DeviceTierTargeting targeting) { @Override protected boolean isDeviceDimensionPresent() { - return !getDeviceSpec().getDeviceTier().isEmpty(); + return getDeviceSpec().hasDeviceTier(); } @Override @@ -86,15 +94,15 @@ protected void checkDeviceCompatibleInternal(DeviceTierTargeting targeting) { if (targeting.equals(DeviceTierTargeting.getDefaultInstance())) { return; } - ImmutableSet valuesAndAlternatives = - ImmutableSet.builder() - .addAll(targeting.getValueList()) - .addAll(targeting.getAlternativesList()) - .build(); + ImmutableSet valuesAndAlternatives = + Streams.concat( + targeting.getValueList().stream().map(Int32Value::getValue), + targeting.getAlternativesList().stream().map(Int32Value::getValue)) + .collect(toImmutableSet()); checkArgument( - valuesAndAlternatives.contains(getDeviceSpec().getDeviceTier()), + valuesAndAlternatives.contains(getDeviceSpec().getDeviceTier().getValue()), "The specified device tier '%s' does not match any of the available values: %s.", - getDeviceSpec().getDeviceTier(), - String.join(", ", valuesAndAlternatives)); + getDeviceSpec().getDeviceTier().getValue(), + valuesAndAlternatives.stream().map(i -> i.toString()).collect(joining(", "))); } } diff --git a/src/main/java/com/android/tools/build/bundletool/flags/Flag.java b/src/main/java/com/android/tools/build/bundletool/flags/Flag.java index 85b1ceda..c296a9ee 100644 --- a/src/main/java/com/android/tools/build/bundletool/flags/Flag.java +++ b/src/main/java/com/android/tools/build/bundletool/flags/Flag.java @@ -99,6 +99,12 @@ public static Flag positiveInteger(String name) { name, /* validator= */ n -> n > 0, /* errorMessage= */ "The value must be positive."); } + /** Integer flag holding a single value. */ + public static Flag nonNegativeInteger(String name) { + return new IntegerFlag( + name, /* validator= */ n -> n >= 0, /* errorMessage= */ "The value must be non-negative."); + } + /** String flag holding a single value. */ public static Flag string(String name) { return new StringFlag(name); diff --git a/src/main/java/com/android/tools/build/bundletool/io/ApkSerializerManager.java b/src/main/java/com/android/tools/build/bundletool/io/ApkSerializerManager.java index 691a4344..dfcfef78 100644 --- a/src/main/java/com/android/tools/build/bundletool/io/ApkSerializerManager.java +++ b/src/main/java/com/android/tools/build/bundletool/io/ApkSerializerManager.java @@ -37,6 +37,7 @@ import com.android.bundle.Commands.DeliveryType; import com.android.bundle.Commands.InstantMetadata; import com.android.bundle.Commands.LocalTestingInfo; +import com.android.bundle.Commands.PermanentlyFusedModule; import com.android.bundle.Commands.Variant; import com.android.bundle.Config.AssetModulesConfig; import com.android.bundle.Config.BundleConfig; @@ -71,8 +72,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.protobuf.Int32Value; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; @@ -124,7 +127,8 @@ public void populateApkSetBuilder( GeneratedAssetSlices generatedAssetSlices, ApkBuildMode apkBuildMode, Optional deviceSpec, - LocalTestingInfo localTestingInfo) { + LocalTestingInfo localTestingInfo, + ImmutableSet permanentlyFusedModules) { ImmutableList allVariantsWithTargeting = serializeApks(apkSetBuilder, generatedApks, apkBuildMode, deviceSpec); ImmutableList allAssetSliceSets = @@ -144,6 +148,10 @@ public void populateApkSetBuilder( getAssetModulesInfo(appBundle.getBundleConfig().getAssetModulesConfig())); } apksResult.addAllDefaultTargetingValue(getDefaultTargetingValues(appBundle.getBundleConfig())); + permanentlyFusedModules.forEach( + moduleName -> + apksResult.addPermanentlyFusedModules( + PermanentlyFusedModule.newBuilder().setName(moduleName.getName()))); apkSetBuilder.setTableOfContentsFile(apksResult.build()); } @@ -426,10 +434,11 @@ private static DeliveryType getDeliveryType(ManifestDeliveryElement deliveryElem * Adds a default device tier to the given {@link DeviceSpec} if it has none. * *

The default tier is taken from the optimization settings in the {@link - * com.android.bundle.Config.BundleConfig}. + * com.android.bundle.Config.BundleConfig}. If suffix stripping is enabled but the default tier is + * unspecified, it defaults to 0. */ private DeviceSpec addDefaultDeviceTierIfNecessary(DeviceSpec deviceSpec) { - if (!deviceSpec.getDeviceTier().isEmpty()) { + if (deviceSpec.hasDeviceTier()) { return deviceSpec; } Optional deviceTierSuffix = @@ -438,7 +447,19 @@ private DeviceSpec addDefaultDeviceTierIfNecessary(DeviceSpec deviceSpec) { if (!deviceTierSuffix.isPresent()) { return deviceSpec; } - return deviceSpec.toBuilder().setDeviceTier(deviceTierSuffix.get().getDefaultSuffix()).build(); + return deviceSpec.toBuilder() + .setDeviceTier( + Int32Value.of( + deviceTierSuffix + .map( + suffix -> + // Use the standard default value 0 if the app doesn't specify an + // explicit default. + suffix.getDefaultSuffix().isEmpty() + ? 0 + : Integer.parseInt(suffix.getDefaultSuffix())) + .orElse(0))) + .build(); } private final class ApkSerializer { diff --git a/src/main/java/com/android/tools/build/bundletool/mergers/BundleModuleMerger.java b/src/main/java/com/android/tools/build/bundletool/mergers/BundleModuleMerger.java index c0f3b390..90cad59a 100644 --- a/src/main/java/com/android/tools/build/bundletool/mergers/BundleModuleMerger.java +++ b/src/main/java/com/android/tools/build/bundletool/mergers/BundleModuleMerger.java @@ -31,6 +31,7 @@ import com.android.tools.build.bundletool.model.AndroidManifest; import com.android.tools.build.bundletool.model.AppBundle; import com.android.tools.build.bundletool.model.BundleModule; +import com.android.tools.build.bundletool.model.BundleModule.ModuleType; import com.android.tools.build.bundletool.model.BundleModuleName; import com.android.tools.build.bundletool.model.ModuleEntry; import com.android.tools.build.bundletool.model.ZipPath; @@ -103,6 +104,10 @@ public static AppBundle mergeNonRemovableInstallTimeModules( private static boolean shouldMerge( BundleModule module, BundleConfig bundleConfig, boolean overrideBundleToolVersion) { + if (module.getModuleType() != ModuleType.FEATURE_MODULE) { + return false; + } + return module .getAndroidManifest() .getManifestDeliveryElement() diff --git a/src/main/java/com/android/tools/build/bundletool/mergers/D8DexMerger.java b/src/main/java/com/android/tools/build/bundletool/mergers/D8DexMerger.java index c98c852f..36f79b75 100644 --- a/src/main/java/com/android/tools/build/bundletool/mergers/D8DexMerger.java +++ b/src/main/java/com/android/tools/build/bundletool/mergers/D8DexMerger.java @@ -33,6 +33,8 @@ import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.Streams; +import com.google.common.io.MoreFiles; +import com.google.common.io.RecursiveDeleteOption; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; @@ -100,6 +102,8 @@ public void error(Diagnostic error) { return Arrays.stream(mergedFiles).map(File::toPath).collect(toImmutableList()); } catch (CompilationFailedException e) { + // Make sure to delete entries in the output dir. + cleanupOutputDir(outputDir); if (isCoreDesugaringException(e)) { // If merge fails because of core desugaring library, exclude dex files related to core // desugaring lib and try again. @@ -107,7 +111,7 @@ public void error(Diagnostic error) { dexFiles, outputDir, mainDexListFile, proguardMap, isDebuggable, minSdkVersion); } if (proguardMap.isPresent()) { - // Try without the proguard map + // Try without the proguard map. return merge( dexFiles, outputDir, mainDexListFile, Optional.empty(), isDebuggable, minSdkVersion); } else { @@ -215,4 +219,12 @@ private static boolean isCoreDesugaringDex(Path dexFile) { .build(); } } + + private static void cleanupOutputDir(Path outputDir) { + try { + MoreFiles.deleteDirectoryContents(outputDir, RecursiveDeleteOption.ALLOW_INSECURE); + } catch (IOException e1) { + throw new UncheckedIOException(e1); + } + } } diff --git a/src/main/java/com/android/tools/build/bundletool/mergers/MergingUtils.java b/src/main/java/com/android/tools/build/bundletool/mergers/MergingUtils.java index 9b7b74a5..4f922d29 100644 --- a/src/main/java/com/android/tools/build/bundletool/mergers/MergingUtils.java +++ b/src/main/java/com/android/tools/build/bundletool/mergers/MergingUtils.java @@ -29,6 +29,7 @@ import static com.android.tools.build.bundletool.model.utils.TargetingProtoUtils.textureCompressionFormatUniverse; import static com.android.tools.build.bundletool.model.utils.TargetingProtoUtils.textureCompressionFormatValues; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.ImmutableList.toImmutableList; import com.android.bundle.Files.TargetedAssetsDirectory; import com.android.bundle.Targeting.Abi; @@ -44,6 +45,7 @@ import com.android.bundle.Targeting.TextureCompressionFormatTargeting; import com.android.tools.build.bundletool.model.exceptions.CommandExecutionException; import com.google.common.collect.Sets; +import com.google.protobuf.Int32Value; import java.util.List; import java.util.Map; import java.util.Optional; @@ -215,12 +217,13 @@ private static TextureCompressionFormatTargeting mergeTextureCompressionFormatTa private static DeviceTierTargeting mergeDeviceTierTargetingsOf( ApkTargeting targeting1, ApkTargeting targeting2) { - Set universe = + Set universe = Sets.union(deviceTierUniverse(targeting1), deviceTierUniverse(targeting2)); - Set values = Sets.union(deviceTierValues(targeting1), deviceTierValues(targeting2)); + Set values = Sets.union(deviceTierValues(targeting1), deviceTierValues(targeting2)); + Set alternatives = Sets.difference(universe, values); return DeviceTierTargeting.newBuilder() - .addAllValue(values) - .addAllAlternatives(Sets.difference(universe, values)) + .addAllValue(values.stream().map(Int32Value::of).collect(toImmutableList())) + .addAllAlternatives(alternatives.stream().map(Int32Value::of).collect(toImmutableList())) .build(); } diff --git a/src/main/java/com/android/tools/build/bundletool/model/Aapt2Command.java b/src/main/java/com/android/tools/build/bundletool/model/Aapt2Command.java new file mode 100644 index 00000000..a02c403b --- /dev/null +++ b/src/main/java/com/android/tools/build/bundletool/model/Aapt2Command.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.tools.build.bundletool.model; + +import java.nio.file.Path; + +/** + * This unit is left for compatibility with old versions of Android Gradle Plugin. Please do not use + * it directly. + */ +@Deprecated +public interface Aapt2Command { + + /** + * This method is just for compatibility with old versions of Android Gradle Plugin. + * + * @deprecated Use com.android.tools.build.bundletool.androidtools.Aapt2Command directly. + */ + @Deprecated + static com.android.tools.build.bundletool.androidtools.Aapt2Command createFromExecutablePath( + Path aapt2Path) { + return com.android.tools.build.bundletool.androidtools.Aapt2Command.createFromExecutablePath( + aapt2Path); + } +} diff --git a/src/main/java/com/android/tools/build/bundletool/model/ModuleSplit.java b/src/main/java/com/android/tools/build/bundletool/model/ModuleSplit.java index 7f816152..18bb15d1 100644 --- a/src/main/java/com/android/tools/build/bundletool/model/ModuleSplit.java +++ b/src/main/java/com/android/tools/build/bundletool/model/ModuleSplit.java @@ -210,7 +210,7 @@ public String getSuffix() { getApkTargeting() .getDeviceTierTargeting() .getValueList() - .forEach(value -> suffixJoiner.add("tier_" + Ascii.toLowerCase(value))); + .forEach(value -> suffixJoiner.add("tier_" + value.getValue())); return suffixJoiner.toString(); } diff --git a/src/main/java/com/android/tools/build/bundletool/model/SizeConfiguration.java b/src/main/java/com/android/tools/build/bundletool/model/SizeConfiguration.java index 99ebd6ad..e218ad49 100644 --- a/src/main/java/com/android/tools/build/bundletool/model/SizeConfiguration.java +++ b/src/main/java/com/android/tools/build/bundletool/model/SizeConfiguration.java @@ -52,7 +52,7 @@ public abstract class SizeConfiguration { public abstract Optional getTextureCompressionFormat(); - public abstract Optional getDeviceTier(); + public abstract Optional getDeviceTier(); public abstract Builder toBuilder(); @@ -112,11 +112,11 @@ public static Optional getTextureCompressionFormatName( Iterables.getOnlyElement(textureCompressionFormatTargeting.getValueList()).getAlias())); } - public static Optional getDeviceTierName(DeviceTierTargeting deviceTierTargeting) { + public static Optional getDeviceTierLevel(DeviceTierTargeting deviceTierTargeting) { if (deviceTierTargeting.getValueList().isEmpty()) { return Optional.empty(); } - return Optional.of(Iterables.getOnlyElement(deviceTierTargeting.getValueList())); + return Optional.of(Iterables.getOnlyElement(deviceTierTargeting.getValueList()).getValue()); } /** Builder for the {@link SizeConfiguration}. */ @@ -132,7 +132,7 @@ public abstract static class Builder { public abstract Builder setTextureCompressionFormat(String textureCompressionFormat); - public abstract Builder setDeviceTier(String deviceTier); + public abstract Builder setDeviceTier(Integer deviceTier); public abstract SizeConfiguration build(); } diff --git a/src/main/java/com/android/tools/build/bundletool/model/targeting/TargetedDirectorySegment.java b/src/main/java/com/android/tools/build/bundletool/model/targeting/TargetedDirectorySegment.java index ef7d1dbf..3a037e2c 100644 --- a/src/main/java/com/android/tools/build/bundletool/model/targeting/TargetedDirectorySegment.java +++ b/src/main/java/com/android/tools/build/bundletool/model/targeting/TargetedDirectorySegment.java @@ -31,6 +31,7 @@ import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Iterables; import com.google.errorprone.annotations.Immutable; +import com.google.protobuf.Int32Value; import java.util.Collection; import java.util.Optional; import java.util.regex.Matcher; @@ -186,7 +187,9 @@ private static Optional getTargetingValue(AssetsDirectoryTargeting targe .getAlias(), null)); } else if (targeting.hasDeviceTier()) { - return Optional.of(Iterables.getOnlyElement(targeting.getDeviceTier().getValueList())); + return Optional.of( + Integer.toString( + Iterables.getOnlyElement(targeting.getDeviceTier().getValueList()).getValue())); } return Optional.empty(); @@ -246,7 +249,8 @@ private static AssetsDirectoryTargeting parseLanguage(String name, String value) private static AssetsDirectoryTargeting parseDeviceTier(String name, String value) { DeviceTargetingUtils.validateDeviceTierForAssetsDirectory(name, value); return AssetsDirectoryTargeting.newBuilder() - .setDeviceTier(DeviceTierTargeting.newBuilder().addValue(value)) + .setDeviceTier( + DeviceTierTargeting.newBuilder().addValue(Int32Value.of(Integer.parseInt(value)))) .build(); } } diff --git a/src/main/java/com/android/tools/build/bundletool/model/targeting/TargetingUtils.java b/src/main/java/com/android/tools/build/bundletool/model/targeting/TargetingUtils.java index c4523bf7..7d96620e 100644 --- a/src/main/java/com/android/tools/build/bundletool/model/targeting/TargetingUtils.java +++ b/src/main/java/com/android/tools/build/bundletool/model/targeting/TargetingUtils.java @@ -40,6 +40,7 @@ import com.google.common.collect.Range; import com.google.common.collect.Sets; import com.google.common.collect.Streams; +import com.google.protobuf.Int32Value; import java.util.Optional; /** Utility functions for Targeting proto. */ @@ -252,20 +253,13 @@ public static ImmutableSet extractTextureCompress * when validators are run). Prefer using {@link BundleModule#getAssetsConfig} for all other * cases. */ - public static ImmutableSet extractDeviceTiers( + public static ImmutableSet extractDeviceTiers( ImmutableSet targetedDirectories) { return targetedDirectories.stream() .map(directory -> directory.getTargeting(TargetingDimension.DEVICE_TIER)) .flatMap(Streams::stream) .flatMap(targeting -> targeting.getDeviceTier().getValueList().stream()) + .map(Int32Value::getValue) .collect(toImmutableSet()); } - - /** Checks if any of the targeted directories utilize device tier targeting. */ - public static boolean containsDeviceTierTargeting( - ImmutableSet targetedDirectories) { - return targetedDirectories.stream() - .map(directory -> directory.getTargeting(TargetingDimension.DEVICE_TIER)) - .anyMatch(Optional::isPresent); - } } diff --git a/src/main/java/com/android/tools/build/bundletool/model/utils/ConfigurationSizesMerger.java b/src/main/java/com/android/tools/build/bundletool/model/utils/ConfigurationSizesMerger.java index e112fffb..3268a0b4 100644 --- a/src/main/java/com/android/tools/build/bundletool/model/utils/ConfigurationSizesMerger.java +++ b/src/main/java/com/android/tools/build/bundletool/model/utils/ConfigurationSizesMerger.java @@ -72,7 +72,7 @@ && areCompatible( * *

This happens if they have the same value or any of them is absent. */ - private static boolean areCompatible(Optional value1, Optional value2) { + private static boolean areCompatible(Optional value1, Optional value2) { return value1.equals(value2) || !value1.isPresent() || !value2.isPresent(); } diff --git a/src/main/java/com/android/tools/build/bundletool/model/utils/DeviceTargetingUtils.java b/src/main/java/com/android/tools/build/bundletool/model/utils/DeviceTargetingUtils.java index 12c6594c..8efc6331 100644 --- a/src/main/java/com/android/tools/build/bundletool/model/utils/DeviceTargetingUtils.java +++ b/src/main/java/com/android/tools/build/bundletool/model/utils/DeviceTargetingUtils.java @@ -18,21 +18,22 @@ import static com.android.tools.build.bundletool.model.AndroidManifest.DEVICE_GROUP_ELEMENT_NAME; import com.android.tools.build.bundletool.model.exceptions.InvalidBundleException; +import com.google.common.primitives.Ints; import java.util.regex.Pattern; +import javax.annotation.Nullable; /** Utilities for device group and tier values. */ public class DeviceTargetingUtils { private static final Pattern DEVICE_GROUP_PATTERN = Pattern.compile("[a-zA-Z][a-zA-Z0-9_]*"); - @Deprecated - private static final Pattern DEVICE_TIER_PATTERN = Pattern.compile("[a-zA-Z][a-zA-Z0-9_]*"); - public static void validateDeviceTierForAssetsDirectory(String directory, String tierName) { - if (!DEVICE_TIER_PATTERN.matcher(tierName).matches()) { + @Nullable Integer tier = Ints.tryParse(tierName); + + if (tier == null || tier < 0) { throw InvalidBundleException.builder() .withUserMessage( - "Device tier names should start with a letter and contain only letters, numbers and" - + " underscores. Found tier named '%s' for directory '%s'.", + "Device tiers should be non-negative integers. " + + "Found tier '%s' for directory '%s'.", tierName, directory) .build(); } diff --git a/src/main/java/com/android/tools/build/bundletool/model/utils/GetSizeCsvUtils.java b/src/main/java/com/android/tools/build/bundletool/model/utils/GetSizeCsvUtils.java index 2a561bb9..5db819bf 100644 --- a/src/main/java/com/android/tools/build/bundletool/model/utils/GetSizeCsvUtils.java +++ b/src/main/java/com/android/tools/build/bundletool/model/utils/GetSizeCsvUtils.java @@ -92,7 +92,9 @@ private static ImmutableList getSizeTotalCsvRow( .put( Dimension.TEXTURE_COMPRESSION_FORMAT, sizeConfiguration::getTextureCompressionFormat) - .put(Dimension.DEVICE_TIER, sizeConfiguration::getDeviceTier) + .put( + Dimension.DEVICE_TIER, + () -> sizeConfiguration.getDeviceTier().map(i -> i.toString())) .build(); return Stream.concat( diff --git a/src/main/java/com/android/tools/build/bundletool/model/utils/TargetingNormalizer.java b/src/main/java/com/android/tools/build/bundletool/model/utils/TargetingNormalizer.java index 2508b3d6..03b7cc92 100644 --- a/src/main/java/com/android/tools/build/bundletool/model/utils/TargetingNormalizer.java +++ b/src/main/java/com/android/tools/build/bundletool/model/utils/TargetingNormalizer.java @@ -37,6 +37,8 @@ import com.android.bundle.Targeting.TextureCompressionFormatTargeting; import com.android.bundle.Targeting.VariantTargeting; import com.google.common.collect.ImmutableList; +import com.google.protobuf.Int32Value; +import java.util.Collection; import java.util.Comparator; /** Helpers related to APK resources qualifiers. */ @@ -191,10 +193,18 @@ private static TextureCompressionFormatTargeting normalizeTextureCompressionForm private static DeviceTierTargeting normalizeDeviceTierTargeting(DeviceTierTargeting targeting) { return DeviceTierTargeting.newBuilder() - .addAllValue(ImmutableList.sortedCopyOf(targeting.getValueList())) - .addAllAlternatives(ImmutableList.sortedCopyOf(targeting.getAlternativesList())) + .addAllValue(sortInt32Values(targeting.getValueList())) + .addAllAlternatives(sortInt32Values(targeting.getAlternativesList())) .build(); } + private static ImmutableList sortInt32Values(Collection values) { + return values.stream() + .map(Int32Value::getValue) + .sorted() + .map(Int32Value::of) + .collect(toImmutableList()); + } + private TargetingNormalizer() {} } diff --git a/src/main/java/com/android/tools/build/bundletool/model/utils/TargetingProtoUtils.java b/src/main/java/com/android/tools/build/bundletool/model/utils/TargetingProtoUtils.java index 10e0b6ce..5922783a 100644 --- a/src/main/java/com/android/tools/build/bundletool/model/utils/TargetingProtoUtils.java +++ b/src/main/java/com/android/tools/build/bundletool/model/utils/TargetingProtoUtils.java @@ -17,6 +17,7 @@ package com.android.tools.build.bundletool.model.utils; import static com.android.tools.build.bundletool.model.utils.Versions.ANDROID_L_API_VERSION; +import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.MoreCollectors.onlyElement; import com.android.bundle.Targeting.Abi; @@ -30,6 +31,7 @@ import com.android.bundle.Targeting.TextureCompressionFormat; import com.android.bundle.Targeting.VariantTargeting; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Streams; import com.google.protobuf.Int32Value; import java.util.Optional; @@ -159,16 +161,19 @@ public static ImmutableSet textureCompressionFormatUni } /** Extracts Texture Compression Format values from the targeting. */ - public static ImmutableSet deviceTierValues(ApkTargeting targeting) { - return ImmutableSet.copyOf(targeting.getDeviceTierTargeting().getValueList()); + public static ImmutableSet deviceTierValues(ApkTargeting targeting) { + return targeting.getDeviceTierTargeting().getValueList().stream() + .map(Int32Value::getValue) + .collect(toImmutableSet()); } /** Extracts targeted Device Tier universe (values and alternatives) from the targeting. */ - public static ImmutableSet deviceTierUniverse(ApkTargeting targeting) { - return ImmutableSet.builder() - .addAll(targeting.getDeviceTierTargeting().getValueList()) - .addAll(targeting.getDeviceTierTargeting().getAlternativesList()) - .build(); + public static ImmutableSet deviceTierUniverse(ApkTargeting targeting) { + return Streams.concat( + targeting.getDeviceTierTargeting().getValueList().stream(), + targeting.getDeviceTierTargeting().getAlternativesList().stream()) + .map(Int32Value::getValue) + .collect(toImmutableSet()); } public static SdkVersion sdkVersionFrom(int from) { diff --git a/src/main/java/com/android/tools/build/bundletool/model/version/BundleToolVersion.java b/src/main/java/com/android/tools/build/bundletool/model/version/BundleToolVersion.java index f6f098ac..6a194eaf 100644 --- a/src/main/java/com/android/tools/build/bundletool/model/version/BundleToolVersion.java +++ b/src/main/java/com/android/tools/build/bundletool/model/version/BundleToolVersion.java @@ -26,7 +26,7 @@ */ public final class BundleToolVersion { - private static final String CURRENT_VERSION = "1.7.0"; + private static final String CURRENT_VERSION = "1.7.1"; /** Returns the version of BundleTool being run. */ public static Version getCurrentVersion() { diff --git a/src/main/java/com/android/tools/build/bundletool/shards/SuffixStripper.java b/src/main/java/com/android/tools/build/bundletool/shards/SuffixStripper.java index de6e31c9..662342c4 100644 --- a/src/main/java/com/android/tools/build/bundletool/shards/SuffixStripper.java +++ b/src/main/java/com/android/tools/build/bundletool/shards/SuffixStripper.java @@ -36,6 +36,7 @@ import com.android.tools.build.bundletool.model.utils.TextureCompressionUtils; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import com.google.protobuf.Int32Value; /** * Strips suffixes on a module for a given targeting dimension. @@ -350,7 +351,8 @@ public AssetsDirectoryTargeting clearTargetingDimension( @Override public ApkTargeting setTargetingDimension(ApkTargeting apkTargeting, String value) { return apkTargeting.toBuilder() - .setDeviceTierTargeting(DeviceTierTargeting.newBuilder().addValue(value)) + .setDeviceTierTargeting( + DeviceTierTargeting.newBuilder().addValue(Int32Value.of(Integer.parseInt(value)))) .build(); } @@ -371,8 +373,14 @@ public boolean isDirectoryTargetingOtherValue( return false; } - String targetingValue = Iterables.getOnlyElement(targeting.getDeviceTier().getValueList()); - return !searchedValue.equals(targetingValue); + // If the searched value (default tier) is empty, we default it to "0", which is the standard + // default when the developer doesn't provide an override. + String searchedValueWithDefault = searchedValue.isEmpty() ? "0" : searchedValue; + + String targetingValue = + Integer.toString( + Iterables.getOnlyElement(targeting.getDeviceTier().getValueList()).getValue()); + return !searchedValueWithDefault.equals(targetingValue); } } } diff --git a/src/main/java/com/android/tools/build/bundletool/validation/BundleConfigValidator.java b/src/main/java/com/android/tools/build/bundletool/validation/BundleConfigValidator.java index 00d34bc7..658eef0f 100644 --- a/src/main/java/com/android/tools/build/bundletool/validation/BundleConfigValidator.java +++ b/src/main/java/com/android/tools/build/bundletool/validation/BundleConfigValidator.java @@ -15,16 +15,12 @@ */ package com.android.tools.build.bundletool.validation; -import static com.android.tools.build.bundletool.model.targeting.TargetingUtils.containsDeviceTierTargeting; -import static com.android.tools.build.bundletool.model.targeting.TargetingUtils.extractAssetsTargetedDirectories; import static com.google.common.collect.ImmutableSet.toImmutableSet; import static java.util.stream.Collectors.joining; import com.android.bundle.Config.BundleConfig; import com.android.bundle.Config.Compression; import com.android.bundle.Config.Optimizations; -import com.android.bundle.Config.ResourceOptimizations; -import com.android.bundle.Config.ResourceOptimizations.SparseEncoding; import com.android.bundle.Config.SplitDimension; import com.android.bundle.Config.SplitDimension.Value; import com.android.tools.build.bundletool.model.AppBundle; @@ -36,7 +32,6 @@ import com.android.tools.build.bundletool.model.utils.PathMatcher.GlobPatternSyntaxException; import com.android.tools.build.bundletool.model.utils.ResourcesUtils; import com.android.tools.build.bundletool.model.utils.TextureCompressionUtils; -import com.android.tools.build.bundletool.model.utils.Versions; import com.android.tools.build.bundletool.model.version.BundleToolVersion; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; @@ -62,9 +57,8 @@ public void validateBundle(AppBundle bundle) { validateVersion(bundleConfig); validateCompression(bundleConfig.getCompression()); - validateOptimizations(bundleConfig.getOptimizations(), bundle); + validateOptimizations(bundleConfig.getOptimizations()); validateMasterResources(bundleConfig, bundle); - validateDefaultDeviceTier(bundleConfig, bundle); } private void validateCompression(Compression compression) { @@ -86,9 +80,8 @@ private void validateCompression(Compression compression) { } } - private void validateOptimizations(Optimizations optimizations, AppBundle bundle) { + private void validateOptimizations(Optimizations optimizations) { validateSplitDimensions(optimizations.getSplitsConfig().getSplitDimensionList()); - validateResourceOptimizations(optimizations.getResourceOptimizations(), bundle); } private void validateSplitDimensions(List splitDimensions) { @@ -150,22 +143,6 @@ private void validateSplitDimensions(List splitDimensions) { }); } - private void validateResourceOptimizations( - ResourceOptimizations resourceOptimizations, AppBundle bundle) { - if (bundle.isAssetOnly()) { - return; - } - int minSdk = bundle.getBaseModule().getAndroidManifest().getEffectiveMinSdkVersion(); - if (resourceOptimizations.getSparseEncoding().equals(SparseEncoding.ENFORCED) - && minSdk < Versions.ANDROID_O_API_VERSION) { - throw InvalidBundleException.builder() - .withUserMessage( - "Sparse encoding is available for applications with minSdk >= %d.", - Versions.ANDROID_O_API_VERSION) - .build(); - } - } - private void validateVersion(BundleConfig bundleConfig) { try { BundleToolVersion.getVersionFromBundleConfig(bundleConfig); @@ -205,28 +182,4 @@ private void validateMasterResources(BundleConfig bundleConfig, AppBundle bundle .build(); } } - - private static void validateDefaultDeviceTier(BundleConfig bundleConfig, AppBundle bundle) { - boolean defaultDeviceTierSpecified = - bundleConfig.getOptimizations().getSplitsConfig().getSplitDimensionList().stream() - .filter(splitDimension -> splitDimension.getValue().equals(Value.DEVICE_TIER)) - .map(splitDimension -> splitDimension.getSuffixStripping().getDefaultSuffix()) - .anyMatch(suffix -> !suffix.isEmpty()); - bundle - .getModules() - .values() - .forEach( - module -> { - boolean containsDeviceTierTargetedAssets = - containsDeviceTierTargeting(extractAssetsTargetedDirectories(module)); - if (containsDeviceTierTargetedAssets && !defaultDeviceTierSpecified) { - throw InvalidBundleException.builder() - .withUserMessage( - "Module '%s' contains assets targeted by device tier but default device" - + " tier value has not been specified for the bundle.", - module.getName()) - .build(); - } - }); - } } diff --git a/src/main/java/com/android/tools/build/bundletool/validation/DeviceTierParityValidator.java b/src/main/java/com/android/tools/build/bundletool/validation/DeviceTierParityValidator.java index 3fb20fdf..fc1a3ed2 100644 --- a/src/main/java/com/android/tools/build/bundletool/validation/DeviceTierParityValidator.java +++ b/src/main/java/com/android/tools/build/bundletool/validation/DeviceTierParityValidator.java @@ -30,9 +30,9 @@ public class DeviceTierParityValidator extends SubValidator { @Override public void validateAllModules(ImmutableList modules) { BundleModule referenceModule = null; - ImmutableSet referenceTiers = null; + ImmutableSet referenceTiers = null; for (BundleModule module : modules) { - ImmutableSet moduleTiers = + ImmutableSet moduleTiers = extractDeviceTiers(extractAssetsTargetedDirectories(module)); if (moduleTiers.isEmpty()) { diff --git a/src/main/proto/commands.proto b/src/main/proto/commands.proto index d3a1cbb3..00fe62aa 100644 --- a/src/main/proto/commands.proto +++ b/src/main/proto/commands.proto @@ -30,6 +30,10 @@ message BuildApksResult { // Default values for targeting dimensions, as specified in the BundleConfig. // Only set for dimensions that have a default suffix specified. repeated DefaultTargetingValue default_targeting_value = 7; + + // Information about permanently fused install-time modules, which were + // presented in original bundle but fused into base in all variants. + repeated PermanentlyFusedModule permanently_fused_modules = 8; } // Variant is a group of APKs that covers a part of the device configuration @@ -239,3 +243,8 @@ message DefaultTargetingValue { // The default value being targeted. string default_value = 2; } + +message PermanentlyFusedModule { + // Module name. + string name = 1; +} diff --git a/src/main/proto/devices.proto b/src/main/proto/devices.proto index 7c0c2c33..f1bd5758 100644 --- a/src/main/proto/devices.proto +++ b/src/main/proto/devices.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package android.bundle; +import "google/protobuf/wrappers.proto"; + option java_package = "com.android.bundle"; message DeviceSpec { @@ -29,7 +31,7 @@ message DeviceSpec { string codename = 7; // Device tier. - string device_tier = 8; + google.protobuf.Int32Value device_tier = 8; // Device groups the device belongs to. repeated string device_groups = 9; diff --git a/src/main/proto/targeting.proto b/src/main/proto/targeting.proto index c7b0d863..016bb360 100644 --- a/src/main/proto/targeting.proto +++ b/src/main/proto/targeting.proto @@ -34,8 +34,9 @@ message ModuleTargeting { SdkVersionTargeting sdk_version_targeting = 1; repeated DeviceFeatureTargeting device_feature_targeting = 2; UserCountriesTargeting user_countries_targeting = 3; - DeviceTierModuleTargeting device_tier_targeting = 4 [deprecated = true]; DeviceGroupModuleTargeting device_group_targeting = 5; + + reserved 4; } // User Countries targeting describing an inclusive/exclusive list of country @@ -199,13 +200,12 @@ message DeviceFeatureTargeting { // Targets assets and APKs to a concrete device tier. message DeviceTierTargeting { - repeated string value = 1; - repeated string alternatives = 2; -} + repeated google.protobuf.Int32Value value = 3; + repeated google.protobuf.Int32Value alternatives = 4; -// Targets conditional modules to a set of device tiers. -message DeviceTierModuleTargeting { - repeated string value = 1; + // TODO(b/192929170): remove them once all callers are migrated. + repeated string value_deprecated = 1 [deprecated = true]; + repeated string alternatives_deprecated = 2 [deprecated = true]; } // Targets conditional modules to a set of device groups. diff --git a/src/test/java/com/android/tools/build/bundletool/commands/BuildApksCommandTest.java b/src/test/java/com/android/tools/build/bundletool/commands/BuildApksCommandTest.java index 97177ac8..4534750c 100644 --- a/src/test/java/com/android/tools/build/bundletool/commands/BuildApksCommandTest.java +++ b/src/test/java/com/android/tools/build/bundletool/commands/BuildApksCommandTest.java @@ -611,7 +611,7 @@ public void buildingViaFlagsAndBuilderHasSameResult_optionalDeviceTier() throws createDeviceSpecFile( mergeSpecs(sdkVersion(28), density(DensityAlias.HDPI), abis("x86"), locales("en")), tmpDir.resolve("device.json")); - final String deviceTier = "low"; + final int deviceTier = 1; ByteArrayOutputStream output = new ByteArrayOutputStream(); BuildApksCommand commandViaFlags = BuildApksCommand.fromFlags( diff --git a/src/test/java/com/android/tools/build/bundletool/commands/BuildApksManagerTest.java b/src/test/java/com/android/tools/build/bundletool/commands/BuildApksManagerTest.java index 7787762c..e4cf0cab 100644 --- a/src/test/java/com/android/tools/build/bundletool/commands/BuildApksManagerTest.java +++ b/src/test/java/com/android/tools/build/bundletool/commands/BuildApksManagerTest.java @@ -137,6 +137,7 @@ import com.android.bundle.Commands.DeliveryType; import com.android.bundle.Commands.InstantMetadata; import com.android.bundle.Commands.ModuleMetadata; +import com.android.bundle.Commands.PermanentlyFusedModule; import com.android.bundle.Commands.SplitApkMetadata; import com.android.bundle.Commands.StandaloneApkMetadata; import com.android.bundle.Commands.SystemApkMetadata; @@ -3129,16 +3130,16 @@ public void buildApksCommand_standalone_deviceTierTargetingWithSuffixStripped() "device_tier_assets", builder -> builder - .addFile("assets/img#tier_low/asset_low.dat") - .addFile("assets/img#tier_high/asset_high.dat") + .addFile("assets/img#tier_0/asset_low.dat") + .addFile("assets/img#tier_1/asset_high.dat") .setAssetsConfig( assets( targetedAssetsDirectory( - "assets/img#tier_low", - assetsDirectoryTargeting(deviceTierTargeting("low"))), + "assets/img#tier_0", + assetsDirectoryTargeting(deviceTierTargeting(0))), targetedAssetsDirectory( - "assets/img#tier_high", - assetsDirectoryTargeting(deviceTierTargeting("high"))))) + "assets/img#tier_1", + assetsDirectoryTargeting(deviceTierTargeting(1))))) .setManifest( androidManifestForAssetModule( "com.test.app", withInstallTimeDelivery()))) @@ -3148,7 +3149,7 @@ public void buildApksCommand_standalone_deviceTierTargetingWithSuffixStripped() Value.DEVICE_TIER, /* negate= */ false, /* stripSuffix= */ true, - /* defaultSuffix= */ "low") + /* defaultSuffix= */ "0") .build()) .build(); TestComponent.useTestModule( @@ -3169,12 +3170,13 @@ public void buildApksCommand_standalone_deviceTierTargetingWithSuffixStripped() new ZipFile(extractFromApkSetFile(apkSetFile, shard.getPath(), outputDir))) { assertThat(shardZip).hasFile("assets/img/asset_low.dat"); assertThat(shardZip).doesNotHaveFile("assets/img/asset_high.dat"); - assertThat(shardZip).doesNotHaveFile("assets/img#tier_low/asset_low.dat"); - assertThat(shardZip).doesNotHaveFile("assets/img#tier_high/asset_high.dat"); + assertThat(shardZip).doesNotHaveFile("assets/img#tier_0/asset_low.dat"); + assertThat(shardZip).doesNotHaveFile("assets/img#tier_1/asset_high.dat"); } // Check that default device tier targeting was applied to the APK - assertThat(shard.getTargeting().getDeviceTierTargeting().getValueList()).containsExactly("low"); + assertThat(shard.getTargeting().getDeviceTierTargeting().getValueList()) + .containsExactly(Int32Value.of(0)); } @Test @@ -3825,6 +3827,8 @@ public void mergeInstallTimeModulesByDefault() throws Exception { BuildApksResult result = extractTocFromApkSetFile(apkSetFile, outputDir); ImmutableList splitApkVariants = splitApkVariants(result); assertThat(splitApkVariants).isNotEmpty(); + assertThat(result.getPermanentlyFusedModulesList()) + .containsExactly(PermanentlyFusedModule.newBuilder().setName("abi_feature").build()); for (Variant splitApkVariant : splitApkVariants(result)) { assertThat(splitApkVariant.getApkSetList()) @@ -3873,6 +3877,7 @@ public void mergeInstallTimeDisabled() throws Exception { BuildApksResult result = extractTocFromApkSetFile(apkSetFile, outputDir); ImmutableList splitApkVariants = splitApkVariants(result); assertThat(splitApkVariants).isNotEmpty(); + assertThat(result.getPermanentlyFusedModulesList()).isEmpty(); for (Variant splitApkVariant : splitApkVariants(result)) { assertThat(splitApkVariant.getApkSetList()) @@ -4375,30 +4380,30 @@ public void deviceTieredAssets_inBaseModule() throws Exception { "base", builder -> builder - .addFile("assets/images#tier_low/image.jpg") - .addFile("assets/images#tier_medium/image.jpg") + .addFile("assets/images#tier_0/image.jpg") + .addFile("assets/images#tier_1/image.jpg") .setManifest(androidManifest("com.test.app")) .setAssetsConfig( assets( targetedAssetsDirectory( - "assets/images#tier_low", + "assets/images#tier_0", assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "low", - /* alternatives= */ ImmutableList.of("medium")))), + /* value= */ 0, + /* alternatives= */ ImmutableList.of(1)))), targetedAssetsDirectory( - "assets/images#tier_medium", + "assets/images#tier_1", assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "medium", - /* alternatives= */ ImmutableList.of("low"))))))) + /* value= */ 1, + /* alternatives= */ ImmutableList.of(0))))))) .setBundleConfig( BundleConfigBuilder.create() .addSplitDimension( Value.DEVICE_TIER, /* negate= */ false, /* stripSuffix= */ false, - /* defaultSuffix= */ "low") + /* defaultSuffix= */ "0") .build()) .build(); TestComponent.useTestModule( @@ -4415,40 +4420,37 @@ public void deviceTieredAssets_inBaseModule() throws Exception { assertThat(splitApkVariants(result)).hasSize(1); Variant splitApkVariant = splitApkVariants(result).get(0); - // Check that apks for "low" and "medium" tiers have been created + // Check that apks for tier 0 and 1 have been created assertThat(splitApkVariant.getApkSetList()).hasSize(1); ImmutableList deviceTierSplits = splitApks.stream() .filter(apkDesc -> apkDesc.getTargeting().hasDeviceTierTargeting()) .collect(toImmutableList()); assertThat(apkNamesInApkDescriptions(deviceTierSplits)) - .containsExactly("base-tier_low.apk", "base-tier_medium.apk"); + .containsExactly("base-tier_0.apk", "base-tier_1.apk"); // Check the content of the APKs ImmutableMap deviceTierSplitsByTargeting = Maps.uniqueIndex(deviceTierSplits, apk -> apk.getTargeting().getDeviceTierTargeting()); ApkDescription lowTierSplit = deviceTierSplitsByTargeting.get( - deviceTierTargeting( - /* value= */ "low", /* alternatives= */ ImmutableList.of("medium"))); + deviceTierTargeting(/* value= */ 0, /* alternatives= */ ImmutableList.of(1))); assertThat(ZipPath.create(lowTierSplit.getPath()).getFileName().toString()) - .isEqualTo("base-tier_low.apk"); - assertThat(filesInApk(lowTierSplit, apkSetFile)).contains("assets/images#tier_low/image.jpg"); + .isEqualTo("base-tier_0.apk"); + assertThat(filesInApk(lowTierSplit, apkSetFile)).contains("assets/images#tier_0/image.jpg"); ApkDescription mediumTierSplit = deviceTierSplitsByTargeting.get( - deviceTierTargeting( - /* value= */ "medium", /* alternatives= */ ImmutableList.of("low"))); + deviceTierTargeting(/* value= */ 1, /* alternatives= */ ImmutableList.of(0))); assertThat(ZipPath.create(mediumTierSplit.getPath()).getFileName().toString()) - .isEqualTo("base-tier_medium.apk"); - assertThat(filesInApk(mediumTierSplit, apkSetFile)) - .contains("assets/images#tier_medium/image.jpg"); + .isEqualTo("base-tier_1.apk"); + assertThat(filesInApk(mediumTierSplit, apkSetFile)).contains("assets/images#tier_1/image.jpg"); assertThat(result.getDefaultTargetingValueList()) .containsExactly( DefaultTargetingValue.newBuilder() .setDimension(Value.DEVICE_TIER) - .setDefaultValue("low") + .setDefaultValue("0") .build()); } @@ -4461,30 +4463,30 @@ public void deviceTieredAssets_inAssetModule() throws Exception { "assetmodule", builder -> builder - .addFile("assets/images#tier_low/image.jpg") - .addFile("assets/images#tier_medium/image.jpg") + .addFile("assets/images#tier_0/image.jpg") + .addFile("assets/images#tier_1/image.jpg") .setManifest(androidManifestForAssetModule("com.test.app")) .setAssetsConfig( assets( targetedAssetsDirectory( - "assets/images#tier_low", + "assets/images#tier_0", assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "low", - /* alternatives= */ ImmutableList.of("medium")))), + /* value= */ 0, + /* alternatives= */ ImmutableList.of(1)))), targetedAssetsDirectory( - "assets/images#tier_medium", + "assets/images#tier_1", assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "medium", - /* alternatives= */ ImmutableList.of("low"))))))) + /* value= */ 1, + /* alternatives= */ ImmutableList.of(0))))))) .setBundleConfig( BundleConfigBuilder.create() .addSplitDimension( Value.DEVICE_TIER, /* negate= */ false, /* stripSuffix= */ false, - /* defaultSuffix= */ "low") + /* defaultSuffix= */ "0") .build()) .build(); TestComponent.useTestModule( @@ -4498,13 +4500,13 @@ public void deviceTieredAssets_inAssetModule() throws Exception { assertThat(result.getAssetSliceSetList()).hasSize(1); List assetSlices = result.getAssetSliceSet(0).getApkDescriptionList(); - // Check that apks for "low" and "medium" tiers have been created + // Check that apks for tier 0 and 1 have been created ImmutableList deviceTierSplits = assetSlices.stream() .filter(apkDesc -> apkDesc.getTargeting().hasDeviceTierTargeting()) .collect(toImmutableList()); assertThat(apkNamesInApkDescriptions(deviceTierSplits)) - .containsExactly("assetmodule-tier_low.apk", "assetmodule-tier_medium.apk"); + .containsExactly("assetmodule-tier_0.apk", "assetmodule-tier_1.apk"); // Check the content of the APKs ImmutableMap deviceTierSplitsByTargeting = @@ -4512,20 +4514,17 @@ public void deviceTieredAssets_inAssetModule() throws Exception { ApkDescription lowTierSplit = deviceTierSplitsByTargeting.get( - deviceTierTargeting( - /* value= */ "low", /* alternatives= */ ImmutableList.of("medium"))); + deviceTierTargeting(/* value= */ 0, /* alternatives= */ ImmutableList.of(1))); assertThat(ZipPath.create(lowTierSplit.getPath()).getFileName().toString()) - .isEqualTo("assetmodule-tier_low.apk"); - assertThat(filesInApk(lowTierSplit, apkSetFile)).contains("assets/images#tier_low/image.jpg"); + .isEqualTo("assetmodule-tier_0.apk"); + assertThat(filesInApk(lowTierSplit, apkSetFile)).contains("assets/images#tier_0/image.jpg"); ApkDescription mediumTierSplit = deviceTierSplitsByTargeting.get( - deviceTierTargeting( - /* value= */ "medium", /* alternatives= */ ImmutableList.of("low"))); + deviceTierTargeting(/* value= */ 1, /* alternatives= */ ImmutableList.of(0))); assertThat(ZipPath.create(mediumTierSplit.getPath()).getFileName().toString()) - .isEqualTo("assetmodule-tier_medium.apk"); - assertThat(filesInApk(mediumTierSplit, apkSetFile)) - .contains("assets/images#tier_medium/image.jpg"); + .isEqualTo("assetmodule-tier_1.apk"); + assertThat(filesInApk(mediumTierSplit, apkSetFile)).contains("assets/images#tier_1/image.jpg"); } @Test @@ -4536,30 +4535,30 @@ public void deviceTieredAssets_withDeviceSpec_deviceTierSet() throws Exception { "base", builder -> builder - .addFile("assets/images#tier_low/image.jpg") - .addFile("assets/images#tier_medium/image.jpg") + .addFile("assets/images#tier_0/image.jpg") + .addFile("assets/images#tier_1/image.jpg") .setManifest(androidManifest("com.test.app")) .setAssetsConfig( assets( targetedAssetsDirectory( - "assets/images#tier_low", + "assets/images#tier_0", assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "low", - /* alternatives= */ ImmutableList.of("medium")))), + /* value= */ 0, + /* alternatives= */ ImmutableList.of(1)))), targetedAssetsDirectory( - "assets/images#tier_medium", + "assets/images#tier_1", assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "medium", - /* alternatives= */ ImmutableList.of("low"))))))) + /* value= */ 1, + /* alternatives= */ ImmutableList.of(0))))))) .setBundleConfig( BundleConfigBuilder.create() .addSplitDimension( Value.DEVICE_TIER, /* negate= */ false, /* stripSuffix= */ false, - /* defaultSuffix= */ "low") + /* defaultSuffix= */ "0") .build()) .build(); TestComponent.useTestModule( @@ -4573,7 +4572,7 @@ public void deviceTieredAssets_withDeviceSpec_deviceTierSet() throws Exception { abis("x86"), density(DensityAlias.MDPI), locales("en-US"), - deviceTier("medium"))) + deviceTier(1))) .build()); buildApksManager.execute(); @@ -4584,12 +4583,12 @@ public void deviceTieredAssets_withDeviceSpec_deviceTierSet() throws Exception { ImmutableList splitApkVariants = splitApkVariants(result); ImmutableList splitApks = apkDescriptions(splitApkVariants); - // Check that only an APK for "low" tier has been created, not for "medium" + // Check that only an APK for tier 1 has been created, not for 0 ImmutableList deviceTierSplits = splitApks.stream() .filter(apkDesc -> apkDesc.getTargeting().hasDeviceTierTargeting()) .collect(toImmutableList()); - assertThat(apkNamesInApkDescriptions(deviceTierSplits)).containsExactly("base-tier_medium.apk"); + assertThat(apkNamesInApkDescriptions(deviceTierSplits)).containsExactly("base-tier_1.apk"); // Check the content of the APK ImmutableMap deviceTierSplitsByTargeting = @@ -4597,10 +4596,8 @@ public void deviceTieredAssets_withDeviceSpec_deviceTierSet() throws Exception { ApkDescription mediumTierSplit = deviceTierSplitsByTargeting.get( - deviceTierTargeting( - /* value= */ "medium", /* alternatives= */ ImmutableList.of("low"))); - assertThat(filesInApk(mediumTierSplit, apkSetFile)) - .contains("assets/images#tier_medium/image.jpg"); + deviceTierTargeting(/* value= */ 1, /* alternatives= */ ImmutableList.of(0))); + assertThat(filesInApk(mediumTierSplit, apkSetFile)).contains("assets/images#tier_1/image.jpg"); } @Test @@ -4611,30 +4608,30 @@ public void deviceTieredAssets_withDeviceSpec_deviceTierNotSet_defaultIsUsed() t "base", builder -> builder - .addFile("assets/images#tier_low/image.jpg") - .addFile("assets/images#tier_medium/image.jpg") + .addFile("assets/images#tier_0/image.jpg") + .addFile("assets/images#tier_1/image.jpg") .setManifest(androidManifest("com.test.app")) .setAssetsConfig( assets( targetedAssetsDirectory( - "assets/images#tier_low", + "assets/images#tier_0", assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "low", - /* alternatives= */ ImmutableList.of("medium")))), + /* value= */ 0, + /* alternatives= */ ImmutableList.of(1)))), targetedAssetsDirectory( - "assets/images#tier_medium", + "assets/images#tier_1", assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "medium", - /* alternatives= */ ImmutableList.of("low"))))))) + /* value= */ 1, + /* alternatives= */ ImmutableList.of(0))))))) .setBundleConfig( BundleConfigBuilder.create() .addSplitDimension( Value.DEVICE_TIER, /* negate= */ false, /* stripSuffix= */ false, - /* defaultSuffix= */ "low") + /* defaultSuffix= */ "0") .build()) .build(); TestComponent.useTestModule( @@ -4655,12 +4652,12 @@ public void deviceTieredAssets_withDeviceSpec_deviceTierNotSet_defaultIsUsed() t ImmutableList splitApkVariants = splitApkVariants(result); ImmutableList splitApks = apkDescriptions(splitApkVariants); - // Check that only an APK for "low" tier has been created, not for "medium" + // Check that only an APK for tier 0 has been created, not for 1 ImmutableList deviceTierSplits = splitApks.stream() .filter(apkDesc -> apkDesc.getTargeting().hasDeviceTierTargeting()) .collect(toImmutableList()); - assertThat(apkNamesInApkDescriptions(deviceTierSplits)).containsExactly("base-tier_low.apk"); + assertThat(apkNamesInApkDescriptions(deviceTierSplits)).containsExactly("base-tier_0.apk"); // Check the content of the APK ImmutableMap deviceTierSplitsByTargeting = @@ -4668,9 +4665,8 @@ public void deviceTieredAssets_withDeviceSpec_deviceTierNotSet_defaultIsUsed() t ApkDescription lowTierSplit = deviceTierSplitsByTargeting.get( - deviceTierTargeting( - /* value= */ "low", /* alternatives= */ ImmutableList.of("medium"))); - assertThat(filesInApk(lowTierSplit, apkSetFile)).contains("assets/images#tier_low/image.jpg"); + deviceTierTargeting(/* value= */ 0, /* alternatives= */ ImmutableList.of(1))); + assertThat(filesInApk(lowTierSplit, apkSetFile)).contains("assets/images#tier_0/image.jpg"); } @Test diff --git a/src/test/java/com/android/tools/build/bundletool/commands/BuildBundleCommandTest.java b/src/test/java/com/android/tools/build/bundletool/commands/BuildBundleCommandTest.java index 75bb822d..88f5bcc7 100644 --- a/src/test/java/com/android/tools/build/bundletool/commands/BuildBundleCommandTest.java +++ b/src/test/java/com/android/tools/build/bundletool/commands/BuildBundleCommandTest.java @@ -421,18 +421,18 @@ public void assetsTargeting_generated() throws Exception { .setTargeting(AssetsDirectoryTargeting.getDefaultInstance())) .addDirectory( TargetedAssetsDirectory.newBuilder() - .setPath("assets/texture#tcf_atc/device#tier_low") + .setPath("assets/texture#tcf_atc/device#tier_0") .setTargeting( mergeAssetsTargeting( assetsDirectoryTargeting( textureCompressionTargeting(TextureCompressionFormatAlias.ATC)), - assetsDirectoryTargeting(deviceTierTargeting("low"))))) + assetsDirectoryTargeting(deviceTierTargeting(0))))) .build(); Path module = new ZipBuilder() .addFileWithContent(ZipPath.create("assets/anything.dat"), "any".getBytes(UTF_8)) .addFileWithContent( - ZipPath.create("assets/texture#tcf_atc/device#tier_low/file.dat"), + ZipPath.create("assets/texture#tcf_atc/device#tier_0/file.dat"), "any2".getBytes(UTF_8)) .addFileWithContent(ZipPath.create("dex/classes.dex"), "dex".getBytes(UTF_8)) .addFileWithProtoContent(ZipPath.create("manifest/AndroidManifest.xml"), manifest) @@ -447,7 +447,7 @@ public void assetsTargeting_generated() throws Exception { try (ZipFile bundle = new ZipFile(bundlePath.toFile())) { assertThat(bundle).hasFile("base/assets/anything.dat").withContent("any".getBytes(UTF_8)); assertThat(bundle) - .hasFile("base/assets/texture#tcf_atc/device#tier_low/file.dat") + .hasFile("base/assets/texture#tcf_atc/device#tier_0/file.dat") .withContent("any2".getBytes(UTF_8)); assertThat(bundle).hasFile("base/dex/classes.dex").withContent("dex".getBytes(UTF_8)); assertThat(bundle) diff --git a/src/test/java/com/android/tools/build/bundletool/commands/ExtractApksCommandTest.java b/src/test/java/com/android/tools/build/bundletool/commands/ExtractApksCommandTest.java index f7d9b0e5..0c2ee04d 100644 --- a/src/test/java/com/android/tools/build/bundletool/commands/ExtractApksCommandTest.java +++ b/src/test/java/com/android/tools/build/bundletool/commands/ExtractApksCommandTest.java @@ -56,6 +56,7 @@ import static com.android.tools.build.bundletool.testing.TargetingUtils.variantSdkTargeting; import static com.android.tools.build.bundletool.testing.TestUtils.expectMissingRequiredFlagException; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth8.assertThat; import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -69,6 +70,7 @@ import com.android.bundle.Commands.ExtractedApk; import com.android.bundle.Commands.LocalTestingInfo; import com.android.bundle.Commands.LocalTestingInfoForMetadata; +import com.android.bundle.Commands.PermanentlyFusedModule; import com.android.bundle.Config.Bundletool; import com.android.bundle.Config.SplitDimension.Value; import com.android.bundle.Devices.DeviceSpec; @@ -91,6 +93,7 @@ import com.google.common.collect.Iterables; import com.google.common.io.ByteStreams; import com.google.common.io.MoreFiles; +import com.google.protobuf.Int32Value; import com.google.protobuf.util.JsonFormat; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; @@ -209,6 +212,40 @@ public void outputDirectorySetWhenUsingDirectory_throws() throws Exception { .contains("Output directory should not be set when APKs are inside directory"); } + @Test + public void permanentlyMergedModule() throws Exception { + ZipPath apkLBase = ZipPath.create("apkL-base.apk"); + BuildApksResult tableOfContentsProto = + BuildApksResult.newBuilder() + .setBundletool( + Bundletool.newBuilder() + .setVersion(BundleToolVersion.getCurrentVersion().toString())) + .addVariant( + createVariant( + VariantTargeting.getDefaultInstance(), + createSplitApkSet( + "base", + createMasterApkDescription(ApkTargeting.getDefaultInstance(), apkLBase)))) + .addPermanentlyFusedModules( + PermanentlyFusedModule.newBuilder().setName("permanent").build()) + .build(); + + Path apksArchiveFile = + createApksArchiveFile(tableOfContentsProto, tmpDir.resolve("bundle.apks")); + Path deviceSpecFile = createDeviceSpecFile(deviceWithSdk(21), tmpDir.resolve("device.json")); + ExtractApksCommand command = + ExtractApksCommand.fromFlags( + new FlagParser() + .parse( + "--device-spec=" + deviceSpecFile, + "--apks=" + apksArchiveFile, + "--modules=permanent")); + + ImmutableList matchedApks = command.execute(); + assertThat(matchedApks.stream().map(apk -> apk.getFileName().toString())) + .containsExactly("apkL-base.apk"); + } + @Test public void nonExistentModule_throws() throws Exception { ZipPath apkLBase = ZipPath.create("apkL-base.apk"); @@ -1721,8 +1758,8 @@ public void extractAssetModules_allModules() throws Exception { @Test public void bundleWithDeviceTierTargeting_noDeviceTierSpecified_usesDefaults() throws Exception { ZipPath baseMasterApk = ZipPath.create("base-master.apk"); - ZipPath baseLowApk = ZipPath.create("base-tier_low.apk"); - ZipPath baseHighApk = ZipPath.create("base-tier_high.apk"); + ZipPath baseLowApk = ZipPath.create("base-tier_0.apk"); + ZipPath baseHighApk = ZipPath.create("base-tier_1.apk"); BuildApksResult buildApksResult = BuildApksResult.newBuilder() .setBundletool( @@ -1739,19 +1776,68 @@ public void bundleWithDeviceTierTargeting_noDeviceTierSpecified_usesDefaults() t splitApkDescription( apkDeviceTierTargeting( deviceTierTargeting( - /* value= */ "low", - /* alternatives= */ ImmutableList.of("high"))), + /* value= */ 0, /* alternatives= */ ImmutableList.of(1))), baseLowApk), splitApkDescription( apkDeviceTierTargeting( deviceTierTargeting( - /* value= */ "high", - /* alternatives= */ ImmutableList.of("low"))), + /* value= */ 1, /* alternatives= */ ImmutableList.of(0))), baseHighApk)))) .addDefaultTargetingValue( DefaultTargetingValue.newBuilder() .setDimension(Value.DEVICE_TIER) - .setDefaultValue("low")) + .setDefaultValue("1")) + .build(); + + Path apksArchiveFile = createApksArchiveFile(buildApksResult, tmpDir.resolve("bundle.apks")); + + DeviceSpec deviceSpec = lDevice(); + + ImmutableList matchedApks = + ExtractApksCommand.builder() + .setApksArchivePath(apksArchiveFile) + .setDeviceSpec(deviceSpec) + .setOutputDirectory(tmpDir) + .build() + .execute(); + + // Master and high tier splits for base and asset module. + assertThat(matchedApks) + .containsExactly(inOutputDirectory(baseMasterApk), inOutputDirectory(baseHighApk)); + for (Path matchedApk : matchedApks) { + checkFileExistsAndReadable(tmpDir.resolve(matchedApk)); + } + } + + @Test + public void bundleWithDeviceTierTargeting_noDeviceTierSpecifiedNorDefault_usesZeroAsDefault() + throws Exception { + ZipPath baseMasterApk = ZipPath.create("base-master.apk"); + ZipPath baseLowApk = ZipPath.create("base-tier_0.apk"); + ZipPath baseHighApk = ZipPath.create("base-tier_1.apk"); + BuildApksResult buildApksResult = + BuildApksResult.newBuilder() + .setBundletool( + Bundletool.newBuilder() + .setVersion(BundleToolVersion.getCurrentVersion().toString())) + .addVariant( + createVariant( + variantSdkTargeting( + sdkVersionFrom(21), ImmutableSet.of(SdkVersion.getDefaultInstance())), + createSplitApkSet( + "base", + createMasterApkDescription( + ApkTargeting.getDefaultInstance(), baseMasterApk), + splitApkDescription( + apkDeviceTierTargeting( + deviceTierTargeting( + /* value= */ 0, /* alternatives= */ ImmutableList.of(1))), + baseLowApk), + splitApkDescription( + apkDeviceTierTargeting( + deviceTierTargeting( + /* value= */ 1, /* alternatives= */ ImmutableList.of(0))), + baseHighApk)))) .build(); Path apksArchiveFile = createApksArchiveFile(buildApksResult, tmpDir.resolve("bundle.apks")); @@ -1777,11 +1863,11 @@ public void bundleWithDeviceTierTargeting_noDeviceTierSpecified_usesDefaults() t @Test public void bundleWithDeviceTierTargeting_deviceTierSet_filtersByTier() throws Exception { ZipPath baseMasterApk = ZipPath.create("base-master.apk"); - ZipPath baseLowApk = ZipPath.create("base-tier_low.apk"); - ZipPath baseHighApk = ZipPath.create("base-tier_high.apk"); + ZipPath baseLowApk = ZipPath.create("base-tier_0.apk"); + ZipPath baseHighApk = ZipPath.create("base-tier_1.apk"); ZipPath asset1MasterApk = ZipPath.create("asset1-master.apk"); - ZipPath asset1LowApk = ZipPath.create("asset1-tier_low.apk"); - ZipPath asset1HighApk = ZipPath.create("asset1-tier_high.apk"); + ZipPath asset1LowApk = ZipPath.create("asset1-tier_0.apk"); + ZipPath asset1HighApk = ZipPath.create("asset1-tier_1.apk"); BuildApksResult buildApksResult = BuildApksResult.newBuilder() .setBundletool( @@ -1798,14 +1884,12 @@ public void bundleWithDeviceTierTargeting_deviceTierSet_filtersByTier() throws E splitApkDescription( apkDeviceTierTargeting( deviceTierTargeting( - /* value= */ "low", - /* alternatives= */ ImmutableList.of("high"))), + /* value= */ 0, /* alternatives= */ ImmutableList.of(1))), baseLowApk), splitApkDescription( apkDeviceTierTargeting( deviceTierTargeting( - /* value= */ "high", - /* alternatives= */ ImmutableList.of("low"))), + /* value= */ 1, /* alternatives= */ ImmutableList.of(0))), baseHighApk)))) .addAssetSliceSet( AssetSliceSet.newBuilder() @@ -1820,25 +1904,23 @@ public void bundleWithDeviceTierTargeting_deviceTierSet_filtersByTier() throws E splitApkDescription( apkDeviceTierTargeting( deviceTierTargeting( - /* value= */ "low", - /* alternatives= */ ImmutableList.of("high"))), + /* value= */ 0, /* alternatives= */ ImmutableList.of(1))), asset1LowApk)) .addApkDescription( splitApkDescription( apkDeviceTierTargeting( deviceTierTargeting( - /* value= */ "high", - /* alternatives= */ ImmutableList.of("low"))), + /* value= */ 1, /* alternatives= */ ImmutableList.of(0))), asset1HighApk))) .addDefaultTargetingValue( DefaultTargetingValue.newBuilder() .setDimension(Value.DEVICE_TIER) - .setDefaultValue("low")) + .setDefaultValue("0")) .build(); Path apksArchiveFile = createApksArchiveFile(buildApksResult, tmpDir.resolve("bundle.apks")); - DeviceSpec deviceSpec = lDevice().toBuilder().setDeviceTier("high").build(); + DeviceSpec deviceSpec = lDevice().toBuilder().setDeviceTier(Int32Value.of(1)).build(); ImmutableList matchedApks = ExtractApksCommand.builder() diff --git a/src/test/java/com/android/tools/build/bundletool/commands/GetDeviceSpecCommandTest.java b/src/test/java/com/android/tools/build/bundletool/commands/GetDeviceSpecCommandTest.java index 4d5c6575..adddce72 100644 --- a/src/test/java/com/android/tools/build/bundletool/commands/GetDeviceSpecCommandTest.java +++ b/src/test/java/com/android/tools/build/bundletool/commands/GetDeviceSpecCommandTest.java @@ -63,7 +63,7 @@ public class GetDeviceSpecCommandTest { @Rule public TemporaryFolder tmp = new TemporaryFolder(); private Path tmpDir; private static final String DEVICE_ID = "id1"; - private static final String DEVICE_TIER = "low"; + private static final int DEVICE_TIER = 1; private static final ImmutableSet DEVICE_GROUPS = ImmutableSet.of("highRam", "googlePixel"); diff --git a/src/test/java/com/android/tools/build/bundletool/commands/GetSizeCommandTest.java b/src/test/java/com/android/tools/build/bundletool/commands/GetSizeCommandTest.java index 95bb50f7..13f0fe4f 100644 --- a/src/test/java/com/android/tools/build/bundletool/commands/GetSizeCommandTest.java +++ b/src/test/java/com/android/tools/build/bundletool/commands/GetSizeCommandTest.java @@ -90,6 +90,7 @@ import com.google.common.collect.Sets; import com.google.common.io.ByteSource; import com.google.common.io.ByteStreams; +import com.google.protobuf.Int32Value; import com.google.protobuf.util.JsonFormat; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; @@ -1359,13 +1360,13 @@ public void getSizeTotal_withDeviceTier() throws Exception { splitApkDescription( apkDeviceTierTargeting( deviceTierTargeting( - /* value= */ "low", /* alternatives= */ ImmutableList.of("high"))), - ZipPath.create("base-tier_low.apk")), + /* value= */ 0, /* alternatives= */ ImmutableList.of(1))), + ZipPath.create("base-tier_0.apk")), splitApkDescription( apkDeviceTierTargeting( deviceTierTargeting( - /* value= */ "high", /* alternatives= */ ImmutableList.of("low"))), - ZipPath.create("base-tier_high.apk")))); + /* value= */ 1, /* alternatives= */ ImmutableList.of(0))), + ZipPath.create("base-tier_1.apk")))); BuildApksResult tableOfContentsProto = BuildApksResult.newBuilder() @@ -1390,10 +1391,8 @@ public void getSizeTotal_withDeviceTier() throws Exception { .asList() .containsExactly( "SDK,DEVICE_TIER,MIN,MAX", - String.format( - "%s,%s,%d,%d", "21-", "low", 2 * compressedApkSize, 2 * compressedApkSize), - String.format( - "%s,%s,%d,%d", "21-", "high", 2 * compressedApkSize, 2 * compressedApkSize)); + String.format("%s,%s,%d,%d", "21-", "0", 2 * compressedApkSize, 2 * compressedApkSize), + String.format("%s,%s,%d,%d", "21-", "1", 2 * compressedApkSize, 2 * compressedApkSize)); } @Test @@ -1408,13 +1407,13 @@ public void getSizeTotal_withDeviceTier_withDeviceTier() throws Exception { splitApkDescription( apkDeviceTierTargeting( deviceTierTargeting( - /* value= */ "low", /* alternatives= */ ImmutableList.of("high"))), - ZipPath.create("base-tier_low.apk")), + /* value= */ 0, /* alternatives= */ ImmutableList.of(1))), + ZipPath.create("base-tier_0.apk")), splitApkDescription( apkDeviceTierTargeting( deviceTierTargeting( - /* value= */ "high", /* alternatives= */ ImmutableList.of("low"))), - ZipPath.create("base-tier_high.apk")))); + /* value= */ 1, /* alternatives= */ ImmutableList.of(0))), + ZipPath.create("base-tier_1.apk")))); BuildApksResult tableOfContentsProto = BuildApksResult.newBuilder() @@ -1432,7 +1431,8 @@ public void getSizeTotal_withDeviceTier_withDeviceTier() throws Exception { .setGetSizeSubCommand(GetSizeSubcommand.TOTAL) .setApksArchivePath(apksArchiveFile) .setDimensions(ImmutableSet.of(Dimension.SDK, Dimension.DEVICE_TIER)) - .setDeviceSpec(DeviceSpec.newBuilder().setSdkVersion(25).setDeviceTier("high").build()) + .setDeviceSpec( + DeviceSpec.newBuilder().setSdkVersion(25).setDeviceTier(Int32Value.of(1)).build()) .build() .getSizeTotal(new PrintStream(outputStream)); @@ -1440,8 +1440,7 @@ public void getSizeTotal_withDeviceTier_withDeviceTier() throws Exception { .asList() .containsExactly( "SDK,DEVICE_TIER,MIN,MAX", - String.format( - "%s,%s,%d,%d", "25", "high", 2 * compressedApkSize, 2 * compressedApkSize)); + String.format("%s,%s,%d,%d", "25", "1", 2 * compressedApkSize, 2 * compressedApkSize)); } /** Copies the testdata resource into the temporary directory. */ diff --git a/src/test/java/com/android/tools/build/bundletool/commands/InstallApksCommandTest.java b/src/test/java/com/android/tools/build/bundletool/commands/InstallApksCommandTest.java index 7a38bde5..39a046cf 100644 --- a/src/test/java/com/android/tools/build/bundletool/commands/InstallApksCommandTest.java +++ b/src/test/java/com/android/tools/build/bundletool/commands/InstallApksCommandTest.java @@ -244,7 +244,7 @@ public void fromFlagsEquivalentToBuilder_deviceTier() throws Exception { InstallApksCommand fromFlags = InstallApksCommand.fromFlags( new FlagParser() - .parse("--apks=" + simpleApksPath, "--adb=" + adbPath, "--device-tier=high"), + .parse("--apks=" + simpleApksPath, "--adb=" + adbPath, "--device-tier=1"), systemEnvironmentProvider, fakeServerOneDevice(lDeviceWithLocales("en-US"))); @@ -254,7 +254,7 @@ public void fromFlagsEquivalentToBuilder_deviceTier() throws Exception { .setAdbPath(adbPath) .setAdbServer(fromFlags.getAdbServer()) .setDeviceId(DEVICE_ID) - .setDeviceTier("high") + .setDeviceTier(1) .build(); assertThat(fromBuilder).isEqualTo(fromFlags); @@ -1183,8 +1183,8 @@ public void noLocalTesting_additionalLocalTestingFiles_throws() throws Exception public void bundleWithDeviceTierTargeting_noDeviceTierSpecified_usesDefaultValue() throws Exception { ZipPath baseMasterApk = ZipPath.create("base-master.apk"); - ZipPath baseLowApk = ZipPath.create("base-tier_low.apk"); - ZipPath baseHighApk = ZipPath.create("base-tier_high.apk"); + ZipPath baseLowApk = ZipPath.create("base-tier_0.apk"); + ZipPath baseHighApk = ZipPath.create("base-tier_1.apk"); BuildApksResult buildApksResult = BuildApksResult.newBuilder() .setPackageName(PKG_NAME) @@ -1202,19 +1202,17 @@ public void bundleWithDeviceTierTargeting_noDeviceTierSpecified_usesDefaultValue splitApkDescription( apkDeviceTierTargeting( deviceTierTargeting( - /* value= */ "low", - /* alternatives= */ ImmutableList.of("high"))), + /* value= */ 0, /* alternatives= */ ImmutableList.of(1))), baseLowApk), splitApkDescription( apkDeviceTierTargeting( deviceTierTargeting( - /* value= */ "high", - /* alternatives= */ ImmutableList.of("low"))), + /* value= */ 1, /* alternatives= */ ImmutableList.of(0))), baseHighApk)))) .addDefaultTargetingValue( DefaultTargetingValue.newBuilder() .setDimension(Value.DEVICE_TIER) - .setDefaultValue("low")) + .setDefaultValue("1")) .build(); Path apksFile = createApksArchiveFile(buildApksResult, tmpDir.resolve("bundle.apks")); @@ -1234,6 +1232,60 @@ public void bundleWithDeviceTierTargeting_noDeviceTierSpecified_usesDefaultValue .execute(); // Base only, the on demand asset is not installed. Low tier splits are filtered out. + assertThat(getFileNames(installedApks)) + .containsExactly(baseMasterApk.toString(), baseHighApk.toString()); + } + + @Test + public void bundleWithDeviceTierTargeting_noDeviceTierSpecifiedNorDefault_usesZeroAsDefault() + throws Exception { + ZipPath baseMasterApk = ZipPath.create("base-master.apk"); + ZipPath baseLowApk = ZipPath.create("base-tier_0.apk"); + ZipPath baseHighApk = ZipPath.create("base-tier_1.apk"); + BuildApksResult buildApksResult = + BuildApksResult.newBuilder() + .setPackageName(PKG_NAME) + .setBundletool( + Bundletool.newBuilder() + .setVersion(BundleToolVersion.getCurrentVersion().toString())) + .addVariant( + createVariant( + variantSdkTargeting( + sdkVersionFrom(21), ImmutableSet.of(SdkVersion.getDefaultInstance())), + createSplitApkSet( + "base", + createMasterApkDescription( + ApkTargeting.getDefaultInstance(), baseMasterApk), + splitApkDescription( + apkDeviceTierTargeting( + deviceTierTargeting( + /* value= */ 0, /* alternatives= */ ImmutableList.of(1))), + baseLowApk), + splitApkDescription( + apkDeviceTierTargeting( + deviceTierTargeting( + /* value= */ 1, /* alternatives= */ ImmutableList.of(0))), + baseHighApk)))) + .build(); + + Path apksFile = createApksArchiveFile(buildApksResult, tmpDir.resolve("bundle.apks")); + + List installedApks = new ArrayList<>(); + FakeDevice fakeDevice = + FakeDevice.fromDeviceSpec(DEVICE_ID, DeviceState.ONLINE, lDeviceWithLocales("en-US")); + fakeDevice.setInstallApksSideEffect((apks, installOptions) -> installedApks.addAll(apks)); + AdbServer adbServer = + new FakeAdbServer(/* hasInitialDeviceList= */ true, ImmutableList.of(fakeDevice)); + + InstallApksCommand.builder() + .setApksArchivePath(apksFile) + .setAdbPath(adbPath) + .setAdbServer(adbServer) + .build() + .execute(); + + // Base only, the on demand asset is not installed. Tier 0 splits are returned, since it is the + // default when unspecified. assertThat(getFileNames(installedApks)) .containsExactly(baseMasterApk.toString(), baseLowApk.toString()); } @@ -1243,11 +1295,11 @@ public void bundleWithDeviceTierTargeting_noDeviceTierSpecified_usesDefaultValue public void bundleWithDeviceTierTargeting_deviceTierSet_filtersByTier( @FromDataPoints("apksInDirectory") boolean apksInDirectory) throws Exception { ZipPath baseMasterApk = ZipPath.create("base-master.apk"); - ZipPath baseLowApk = ZipPath.create("base-tier_low.apk"); - ZipPath baseHighApk = ZipPath.create("base-tier_high.apk"); + ZipPath baseLowApk = ZipPath.create("base-tier_0.apk"); + ZipPath baseHighApk = ZipPath.create("base-tier_1.apk"); ZipPath asset1MasterApk = ZipPath.create("asset1-master.apk"); - ZipPath asset1LowApk = ZipPath.create("asset1-tier_low.apk"); - ZipPath asset1HighApk = ZipPath.create("asset1-tier_high.apk"); + ZipPath asset1LowApk = ZipPath.create("asset1-tier_0.apk"); + ZipPath asset1HighApk = ZipPath.create("asset1-tier_1.apk"); BuildApksResult tableOfContent = BuildApksResult.newBuilder() .setPackageName(PKG_NAME) @@ -1265,14 +1317,12 @@ public void bundleWithDeviceTierTargeting_deviceTierSet_filtersByTier( splitApkDescription( apkDeviceTierTargeting( deviceTierTargeting( - /* value= */ "low", - /* alternatives= */ ImmutableList.of("high"))), + /* value= */ 0, /* alternatives= */ ImmutableList.of(1))), baseLowApk), splitApkDescription( apkDeviceTierTargeting( deviceTierTargeting( - /* value= */ "high", - /* alternatives= */ ImmutableList.of("low"))), + /* value= */ 1, /* alternatives= */ ImmutableList.of(0))), baseHighApk)))) .addAssetSliceSet( AssetSliceSet.newBuilder() @@ -1287,15 +1337,13 @@ public void bundleWithDeviceTierTargeting_deviceTierSet_filtersByTier( splitApkDescription( apkDeviceTierTargeting( deviceTierTargeting( - /* value= */ "low", - /* alternatives= */ ImmutableList.of("high"))), + /* value= */ 0, /* alternatives= */ ImmutableList.of(1))), asset1LowApk)) .addApkDescription( splitApkDescription( apkDeviceTierTargeting( deviceTierTargeting( - /* value= */ "high", - /* alternatives= */ ImmutableList.of("low"))), + /* value= */ 1, /* alternatives= */ ImmutableList.of(0))), asset1HighApk))) // Add local testing info to check that pushed APKs are also filtered by tier. .setLocalTestingInfo( @@ -1303,7 +1351,7 @@ public void bundleWithDeviceTierTargeting_deviceTierSet_filtersByTier( .addDefaultTargetingValue( DefaultTargetingValue.newBuilder() .setDimension(Value.DEVICE_TIER) - .setDefaultValue("low")) + .setDefaultValue("0")) .build(); Path apksFile = createApks(tableOfContent, apksInDirectory); @@ -1321,7 +1369,7 @@ public void bundleWithDeviceTierTargeting_deviceTierSet_filtersByTier( .setApksArchivePath(apksFile) .setAdbPath(adbPath) .setAdbServer(adbServer) - .setDeviceTier("high") + .setDeviceTier(1) .build() .execute(); diff --git a/src/test/java/com/android/tools/build/bundletool/device/ApkMatcherTest.java b/src/test/java/com/android/tools/build/bundletool/device/ApkMatcherTest.java index 8b20d1e9..8b80f39b 100644 --- a/src/test/java/com/android/tools/build/bundletool/device/ApkMatcherTest.java +++ b/src/test/java/com/android/tools/build/bundletool/device/ApkMatcherTest.java @@ -80,6 +80,7 @@ import com.android.bundle.Commands.AssetSliceSet; import com.android.bundle.Commands.BuildApksResult; import com.android.bundle.Commands.DeliveryType; +import com.android.bundle.Commands.PermanentlyFusedModule; import com.android.bundle.Commands.Variant; import com.android.bundle.Config.Bundletool; import com.android.bundle.Devices.DeviceSpec; @@ -999,6 +1000,27 @@ public void apkMatch_withModuleNameFiltering_splitApks_checksModuleName() { .contains("The APK Set archive does not contain the following modules: [unknown_module]"); } + @Test + public void apkMatch_withModuleNameFiltering_splitApks_permanentlyMergedModule() { + DeviceSpec device = deviceWithSdk(21); + ZipPath apk = ZipPath.create("master-de-fr.apk"); + BuildApksResult buildApksResult = + buildApksResult( + createVariant( + VariantTargeting.getDefaultInstance(), + splitApkSet( + /* moduleName= */ "base", + splitApkDescription(ApkTargeting.getDefaultInstance(), apk)))) + .toBuilder() + .addPermanentlyFusedModules(PermanentlyFusedModule.newBuilder().setName("my-module")) + .build(); + + ImmutableList matchedApks = + createMatcher(device, Optional.of(ImmutableSet.of("my-module"))) + .getMatchingApks(buildApksResult); + assertThat(matchedApks).containsExactly(baseMatchedApk(apk)); + } + @Test public void apkMatch_withModuleNameFiltering_instantApks_checksModuleName_isEmpty() { DeviceSpec device = deviceWithSdk(21); diff --git a/src/test/java/com/android/tools/build/bundletool/device/DeviceSpecUtilsTest.java b/src/test/java/com/android/tools/build/bundletool/device/DeviceSpecUtilsTest.java index d7a1d543..31556b9b 100644 --- a/src/test/java/com/android/tools/build/bundletool/device/DeviceSpecUtilsTest.java +++ b/src/test/java/com/android/tools/build/bundletool/device/DeviceSpecUtilsTest.java @@ -168,11 +168,10 @@ public void deviceSpecFromTargetingBuilder_setDeviceTier() { DeviceSpec deviceSpec = new DeviceSpecFromTargetingBuilder(DeviceSpec.getDefaultInstance()) .setDeviceTier( - deviceTierTargeting( - /* value= */ "high", /* alternatives= */ ImmutableList.of("low"))) + deviceTierTargeting(/* value= */ 2, /* alternatives= */ ImmutableList.of(1))) .build(); - assertThat(deviceSpec.getDeviceTier()).isEqualTo("high"); + assertThat(deviceSpec.getDeviceTier().getValue()).isEqualTo(2); } @Test diff --git a/src/test/java/com/android/tools/build/bundletool/device/DeviceTierApkMatcherTest.java b/src/test/java/com/android/tools/build/bundletool/device/DeviceTierApkMatcherTest.java index 1aa1a2df..fb6d39e1 100644 --- a/src/test/java/com/android/tools/build/bundletool/device/DeviceTierApkMatcherTest.java +++ b/src/test/java/com/android/tools/build/bundletool/device/DeviceTierApkMatcherTest.java @@ -35,41 +35,36 @@ public class DeviceTierApkMatcherTest { @Test public void matchesTargeting_matches() { - DeviceTierApkMatcher matcher = new DeviceTierApkMatcher(deviceTier("low")); + DeviceTierApkMatcher matcher = new DeviceTierApkMatcher(deviceTier(1)); - assertThat(matcher.matchesTargeting(deviceTierTargeting("low"))).isTrue(); - assertThat( - matcher.matchesTargeting( - deviceTierTargeting("low", ImmutableList.of("medium", "high")))) + assertThat(matcher.matchesTargeting(deviceTierTargeting(1))).isTrue(); + assertThat(matcher.matchesTargeting(deviceTierTargeting(1, ImmutableList.of(0, 2, 3)))) .isTrue(); } @Test public void matchesTargeting_doesNotMatch() { - DeviceTierApkMatcher matcher = new DeviceTierApkMatcher(deviceTier("low")); + DeviceTierApkMatcher matcher = new DeviceTierApkMatcher(deviceTier(1)); - assertThat(matcher.matchesTargeting(deviceTierTargeting("medium"))).isFalse(); - assertThat( - matcher.matchesTargeting( - deviceTierTargeting("medium", ImmutableList.of("low", "high")))) + assertThat(matcher.matchesTargeting(deviceTierTargeting(2))).isFalse(); + assertThat(matcher.matchesTargeting(deviceTierTargeting(2, ImmutableList.of(0, 1, 3)))) .isFalse(); } @Test public void matchesTargeting_overlappingValuesAndAlternatives_throws() { - DeviceTierApkMatcher matcher = new DeviceTierApkMatcher(deviceTier("low")); + DeviceTierApkMatcher matcher = new DeviceTierApkMatcher(deviceTier(1)); assertThrows( IllegalArgumentException.class, - () -> - matcher.matchesTargeting(deviceTierTargeting("low", ImmutableList.of("low", "high")))); + () -> matcher.matchesTargeting(deviceTierTargeting(1, ImmutableList.of(1, 3)))); } @Test public void getTargetingValue() { - DeviceTierApkMatcher matcher = new DeviceTierApkMatcher(deviceTier("low")); + DeviceTierApkMatcher matcher = new DeviceTierApkMatcher(deviceTier(1)); - DeviceTierTargeting targeting = deviceTierTargeting("low", ImmutableList.of("medium")); + DeviceTierTargeting targeting = deviceTierTargeting(1, ImmutableList.of(0, 2)); assertThat( matcher.getTargetingValue( ApkTargeting.newBuilder().setDeviceTierTargeting(targeting).build())) @@ -78,27 +73,24 @@ public void getTargetingValue() { @Test public void isDimensionPresent() { - assertThat(new DeviceTierApkMatcher(deviceTier("low")).isDeviceDimensionPresent()).isTrue(); + assertThat(new DeviceTierApkMatcher(deviceTier(1)).isDeviceDimensionPresent()).isTrue(); assertThat(new DeviceTierApkMatcher(abis("x86")).isDeviceDimensionPresent()).isFalse(); } @Test public void checkDeviceCompatibleInternal_isCompatible() { - DeviceTierApkMatcher matcher = new DeviceTierApkMatcher(deviceTier("low")); + DeviceTierApkMatcher matcher = new DeviceTierApkMatcher(deviceTier(1)); - matcher.checkDeviceCompatibleInternal(deviceTierTargeting("low")); - matcher.checkDeviceCompatibleInternal( - deviceTierTargeting("medium", ImmutableList.of("low", "high"))); + matcher.checkDeviceCompatibleInternal(deviceTierTargeting(1)); + matcher.checkDeviceCompatibleInternal(deviceTierTargeting(2, ImmutableList.of(1, 3))); } @Test public void checkDeviceCompatibleInternal_isNotCompatible() { - DeviceTierApkMatcher matcher = new DeviceTierApkMatcher(deviceTier("low")); + DeviceTierApkMatcher matcher = new DeviceTierApkMatcher(deviceTier(1)); assertThrows( IllegalArgumentException.class, - () -> - matcher.checkDeviceCompatibleInternal( - deviceTierTargeting("medium", ImmutableList.of("high")))); + () -> matcher.checkDeviceCompatibleInternal(deviceTierTargeting(2, ImmutableList.of(3)))); } } diff --git a/src/test/java/com/android/tools/build/bundletool/flags/FlagTest.java b/src/test/java/com/android/tools/build/bundletool/flags/FlagTest.java index be254bb3..f75dfee9 100644 --- a/src/test/java/com/android/tools/build/bundletool/flags/FlagTest.java +++ b/src/test/java/com/android/tools/build/bundletool/flags/FlagTest.java @@ -240,7 +240,7 @@ public void positiveIntegerFlag_valid() throws Exception { } @Test - public void positiveIntegerFlag_NaN_throws() throws Exception { + public void positiveIntegerFlag_notANumber_throws() throws Exception { Flag flag = Flag.positiveInteger("testFlag"); ParsedFlags parsedFlags = new FlagParser().parse("--testFlag=blah"); FlagParseException exception = @@ -266,6 +266,38 @@ public void positiveIntegerFlag_negative_throws() throws Exception { assertThat(exception).hasMessageThat().contains("has illegal value"); } + @Test + public void nonNegativeIntegerFlag_valid() throws Exception { + Flag flag = Flag.nonNegativeInteger("testFlag"); + ParsedFlags parsedFlags = new FlagParser().parse("--testFlag=42"); + assertThat(flag.getRequiredValue(parsedFlags)).isEqualTo(42); + } + + @Test + public void nonNegativeIntegerFlag_notANumber_throws() throws Exception { + Flag flag = Flag.nonNegativeInteger("testFlag"); + ParsedFlags parsedFlags = new FlagParser().parse("--testFlag=blah"); + FlagParseException exception = + assertThrows(FlagParseException.class, () -> flag.getValue(parsedFlags)); + assertThat(exception).hasMessageThat().contains("Error while parsing"); + } + + @Test + public void nonNegativeIntegerFlag_zero_valid() throws Exception { + Flag flag = Flag.nonNegativeInteger("testFlag"); + ParsedFlags parsedFlags = new FlagParser().parse("--testFlag=0"); + assertThat(flag.getRequiredValue(parsedFlags)).isEqualTo(0); + } + + @Test + public void nonNegativeIntegerFlag_negative_throws() throws Exception { + Flag flag = Flag.nonNegativeInteger("testFlag"); + ParsedFlags parsedFlags = new FlagParser().parse("--testFlag=-1"); + FlagParseException exception = + assertThrows(FlagParseException.class, () -> flag.getValue(parsedFlags)); + assertThat(exception).hasMessageThat().contains("has illegal value"); + } + @Test public void setFlag_empty() throws Exception { Flag> flag = Flag.stringSet("testFlag"); diff --git a/src/test/java/com/android/tools/build/bundletool/mergers/BundleModuleMergerTest.java b/src/test/java/com/android/tools/build/bundletool/mergers/BundleModuleMergerTest.java index 75234836..e5a08b30 100644 --- a/src/test/java/com/android/tools/build/bundletool/mergers/BundleModuleMergerTest.java +++ b/src/test/java/com/android/tools/build/bundletool/mergers/BundleModuleMergerTest.java @@ -19,6 +19,7 @@ import static com.android.tools.build.bundletool.testing.ManifestProtoUtils.androidManifest; import static com.android.tools.build.bundletool.testing.ManifestProtoUtils.androidManifestForAssetModule; import static com.android.tools.build.bundletool.testing.ManifestProtoUtils.androidManifestForFeature; +import static com.android.tools.build.bundletool.testing.ManifestProtoUtils.androidManifestForMlModule; import static com.android.tools.build.bundletool.testing.ManifestProtoUtils.withFeatureCondition; import static com.android.tools.build.bundletool.testing.ManifestProtoUtils.withInstallTimeDelivery; import static com.android.tools.build.bundletool.testing.ManifestProtoUtils.withInstallTimeRemovableElement; @@ -268,6 +269,30 @@ public void testMultipleModulesWithInstallTime_notMergingAssetModules() throws E } } + @Test + public void testMultipleModulesWithInstallTime_notMergingMlModules() throws Exception { + XmlNode installTimeModuleManifest = + androidManifestForMlModule("com.test.app.detail", withInstallTimeDelivery()); + createBasicZipBuilder(BUNDLE_CONFIG_1_0_0) + .addFileWithProtoContent(ZipPath.create("base/manifest/AndroidManifest.xml"), MANIFEST) + .addFileWithContent(ZipPath.create("base/dex/classes.dex"), DUMMY_CONTENT) + .addFileWithContent(ZipPath.create("base/assets/baseAssetfile.txt"), DUMMY_CONTENT) + .addFileWithProtoContent( + ZipPath.create("detail/manifest/AndroidManifest.xml"), installTimeModuleManifest) + .addFileWithContent(ZipPath.create("detail/assets/detailsAssetfile.txt"), DUMMY_CONTENT) + .writeTo(bundleFile); + + try (ZipFile appBundleZip = new ZipFile(bundleFile.toFile())) { + AppBundle appBundle = + BundleModuleMerger.mergeNonRemovableInstallTimeModules( + AppBundle.buildFromZip(appBundleZip), /* overrideBundleToolVersion = */ false); + + assertThat(appBundle.getFeatureModules().keySet()) + .comparingElementsUsing(equalsBundleModuleName()) + .containsExactly("base", "detail"); + } + } + @Test public void testMultipleModulesWithInstallTime_explicitMerging() throws Exception { XmlNode installTimeModuleManifest = diff --git a/src/test/java/com/android/tools/build/bundletool/mergers/MergingUtilsTest.java b/src/test/java/com/android/tools/build/bundletool/mergers/MergingUtilsTest.java index cc5dcbbe..472ffc81 100644 --- a/src/test/java/com/android/tools/build/bundletool/mergers/MergingUtilsTest.java +++ b/src/test/java/com/android/tools/build/bundletool/mergers/MergingUtilsTest.java @@ -132,8 +132,7 @@ public void mergeShardTargetings_equalTextureCompressionFormat_ok() { @Test public void mergeShardTargetings_equalDeviceTier_ok() { - ApkTargeting targeting = - apkDeviceTierTargeting(deviceTierTargeting("low", ImmutableList.of("high"))); + ApkTargeting targeting = apkDeviceTierTargeting(deviceTierTargeting(0, ImmutableList.of(1))); assertThat(MergingUtils.mergeShardTargetings(targeting, targeting)).isEqualTo(targeting); } @@ -197,14 +196,11 @@ public void mergeShardTargetings_differentTextureCompressionFormats_ok() { @Test public void mergeShardTargetings_differentDeviceTiers_ok() { - ApkTargeting targeting1 = - apkDeviceTierTargeting(deviceTierTargeting("low", ImmutableList.of("high"))); - ApkTargeting targeting2 = - apkDeviceTierTargeting(deviceTierTargeting("low", ImmutableList.of("medium"))); + ApkTargeting targeting1 = apkDeviceTierTargeting(deviceTierTargeting(0, ImmutableList.of(1))); + ApkTargeting targeting2 = apkDeviceTierTargeting(deviceTierTargeting(0, ImmutableList.of(2))); assertThat(MergingUtils.mergeShardTargetings(targeting1, targeting2)) - .isEqualTo( - apkDeviceTierTargeting(deviceTierTargeting("low", ImmutableList.of("high", "medium")))); + .isEqualTo(apkDeviceTierTargeting(deviceTierTargeting(0, ImmutableList.of(1, 2)))); } @Test diff --git a/src/test/java/com/android/tools/build/bundletool/model/ModuleSplitTest.java b/src/test/java/com/android/tools/build/bundletool/model/ModuleSplitTest.java index 4e9cab2e..8e4e647b 100644 --- a/src/test/java/com/android/tools/build/bundletool/model/ModuleSplitTest.java +++ b/src/test/java/com/android/tools/build/bundletool/model/ModuleSplitTest.java @@ -235,14 +235,12 @@ public void moduleDeviceTierSplitSuffixAndName() { ModuleSplit.builder() .setModuleName(BundleModuleName.create("base")) .setVariantTargeting(lPlusVariantTargeting()) - .setApkTargeting( - apkDeviceTierTargeting( - deviceTierTargeting("low", ImmutableList.of("medium", "high")))) + .setApkTargeting(apkDeviceTierTargeting(deviceTierTargeting(0, ImmutableList.of(1, 2)))) .setMasterSplit(false) .setAndroidManifest(AndroidManifest.create(androidManifest("com.test.app"))) .build(); deviceTieredSplit = deviceTieredSplit.writeSplitIdInManifest(deviceTieredSplit.getSuffix()); - assertThat(deviceTieredSplit.getAndroidManifest().getSplitId()).hasValue("config.tier_low"); + assertThat(deviceTieredSplit.getAndroidManifest().getSplitId()).hasValue("config.tier_0"); } @Test diff --git a/src/test/java/com/android/tools/build/bundletool/model/SizeConfigurationTest.java b/src/test/java/com/android/tools/build/bundletool/model/SizeConfigurationTest.java index db9fd3fe..da573443 100644 --- a/src/test/java/com/android/tools/build/bundletool/model/SizeConfigurationTest.java +++ b/src/test/java/com/android/tools/build/bundletool/model/SizeConfigurationTest.java @@ -25,7 +25,7 @@ import static com.android.bundle.Targeting.TextureCompressionFormat.TextureCompressionFormatAlias.ETC2; import static com.android.bundle.Targeting.TextureCompressionFormat.TextureCompressionFormatAlias.PVRTC; import static com.android.tools.build.bundletool.model.SizeConfiguration.getAbiName; -import static com.android.tools.build.bundletool.model.SizeConfiguration.getDeviceTierName; +import static com.android.tools.build.bundletool.model.SizeConfiguration.getDeviceTierLevel; import static com.android.tools.build.bundletool.model.SizeConfiguration.getLocaleName; import static com.android.tools.build.bundletool.model.SizeConfiguration.getScreenDensityName; import static com.android.tools.build.bundletool.model.SizeConfiguration.getSdkName; @@ -151,11 +151,10 @@ public void getTextureCompressionFormat_multipleTextureCompressionFormatTargetin @Test public void getDeviceTier_singleDevicetierTargeting() { - assertThat(getDeviceTierName(deviceTierTargeting("low"))).hasValue("low"); + assertThat(getDeviceTierLevel(deviceTierTargeting(0))).hasValue(0); assertThat( - getDeviceTierName( - deviceTierTargeting( - /* value= */ "high", /* alternatives= */ ImmutableList.of("low")))) - .hasValue("high"); + getDeviceTierLevel( + deviceTierTargeting(/* value= */ 1, /* alternatives= */ ImmutableList.of(0)))) + .hasValue(1); } } diff --git a/src/test/java/com/android/tools/build/bundletool/model/targeting/TargetedDirectorySegmentTest.java b/src/test/java/com/android/tools/build/bundletool/model/targeting/TargetedDirectorySegmentTest.java index 0734ce38..2e7e8d20 100644 --- a/src/test/java/com/android/tools/build/bundletool/model/targeting/TargetedDirectorySegmentTest.java +++ b/src/test/java/com/android/tools/build/bundletool/model/targeting/TargetedDirectorySegmentTest.java @@ -177,18 +177,28 @@ public void testTargeting_badKey() { @Test public void testTargeting_deviceTier() { - TargetedDirectorySegment segment = TargetedDirectorySegment.parse("test#tier_low"); + TargetedDirectorySegment segment = TargetedDirectorySegment.parse("test#tier_1"); assertThat(segment.getName()).isEqualTo("test"); assertThat(segment.getTargetingDimension()).hasValue(TargetingDimension.DEVICE_TIER); - assertThat(segment.getTargeting()) - .isEqualTo(assetsDirectoryTargeting(deviceTierTargeting("low"))); + assertThat(segment.getTargeting()).isEqualTo(assetsDirectoryTargeting(deviceTierTargeting(1))); } @Test - public void testTargeting_deviceTier_invalidTierName() { + public void testTargeting_deviceTier_invalidTierName_notNumerical() { assertThrows( - InvalidBundleException.class, - () -> TargetedDirectorySegment.parse("test#tier_invalid+tier(name")); + InvalidBundleException.class, () -> TargetedDirectorySegment.parse("test#tier_invalid")); + } + + @Test + public void testTargeting_deviceTier_invalidTierName_negativeNumber() { + assertThrows( + InvalidBundleException.class, () -> TargetedDirectorySegment.parse("test#tier_-1")); + } + + @Test + public void testTargeting_deviceTier_invalidTierName_notAnInt() { + assertThrows( + InvalidBundleException.class, () -> TargetedDirectorySegment.parse("test#tier_1.5")); } @Test @@ -274,13 +284,13 @@ public void testTargeting_language_toPathIdempotent() { @Test public void testTargeting_deviceTier_toPathIdempotent() { - TargetedDirectorySegment segment = TargetedDirectorySegment.parse("test#tier_high"); - assertThat(segment.toPathSegment()).isEqualTo("test#tier_high"); + TargetedDirectorySegment segment = TargetedDirectorySegment.parse("test#tier_2"); + assertThat(segment.toPathSegment()).isEqualTo("test#tier_2"); - segment = TargetedDirectorySegment.parse("test#tier_medium"); - assertThat(segment.toPathSegment()).isEqualTo("test#tier_medium"); + segment = TargetedDirectorySegment.parse("test#tier_1"); + assertThat(segment.toPathSegment()).isEqualTo("test#tier_1"); - segment = TargetedDirectorySegment.parse("test#tier_low"); - assertThat(segment.toPathSegment()).isEqualTo("test#tier_low"); + segment = TargetedDirectorySegment.parse("test#tier_0"); + assertThat(segment.toPathSegment()).isEqualTo("test#tier_0"); } } diff --git a/src/test/java/com/android/tools/build/bundletool/model/targeting/TargetingGeneratorTest.java b/src/test/java/com/android/tools/build/bundletool/model/targeting/TargetingGeneratorTest.java index 4f92e2ba..49a64e45 100644 --- a/src/test/java/com/android/tools/build/bundletool/model/targeting/TargetingGeneratorTest.java +++ b/src/test/java/com/android/tools/build/bundletool/model/targeting/TargetingGeneratorTest.java @@ -586,37 +586,34 @@ public void generateTargetingForAssets_deviceTierTargeting() { new TargetingGenerator() .generateTargetingForAssets( ImmutableList.of( - ZipPath.create("assets/img#tier_low"), - ZipPath.create("assets/img#tier_medium"), - ZipPath.create("assets/img#tier_high"))); + ZipPath.create("assets/img#tier_0"), + ZipPath.create("assets/img#tier_1"), + ZipPath.create("assets/img#tier_2"))); assertThat(assetsConfig) .ignoringRepeatedFieldOrder() .isEqualTo( Assets.newBuilder() .addDirectory( TargetedAssetsDirectory.newBuilder() - .setPath("assets/img#tier_low") + .setPath("assets/img#tier_0") .setTargeting( assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "low", - /* alternatives= */ ImmutableList.of("high", "medium"))))) + /* value= */ 0, /* alternatives= */ ImmutableList.of(1, 2))))) .addDirectory( TargetedAssetsDirectory.newBuilder() - .setPath("assets/img#tier_medium") + .setPath("assets/img#tier_1") .setTargeting( assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "medium", - /* alternatives= */ ImmutableList.of("high", "low"))))) + /* value= */ 1, /* alternatives= */ ImmutableList.of(0, 2))))) .addDirectory( TargetedAssetsDirectory.newBuilder() - .setPath("assets/img#tier_high") + .setPath("assets/img#tier_2") .setTargeting( assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "high", - /* alternatives= */ ImmutableList.of("low", "medium"))))) + /* value= */ 2, /* alternatives= */ ImmutableList.of(0, 1))))) .build()); } diff --git a/src/test/java/com/android/tools/build/bundletool/model/targeting/TargetingUtilsTest.java b/src/test/java/com/android/tools/build/bundletool/model/targeting/TargetingUtilsTest.java index eeb6eb11..b8d9aae4 100644 --- a/src/test/java/com/android/tools/build/bundletool/model/targeting/TargetingUtilsTest.java +++ b/src/test/java/com/android/tools/build/bundletool/model/targeting/TargetingUtilsTest.java @@ -16,7 +16,6 @@ package com.android.tools.build.bundletool.model.targeting; -import static com.android.tools.build.bundletool.model.targeting.TargetingUtils.containsDeviceTierTargeting; import static com.android.tools.build.bundletool.model.targeting.TargetingUtils.getMaxSdk; import static com.android.tools.build.bundletool.model.targeting.TargetingUtils.getMinSdk; import static com.android.tools.build.bundletool.testing.TargetingUtils.abiTargeting; @@ -36,7 +35,6 @@ import com.android.bundle.Targeting.SdkVersionTargeting; import com.android.bundle.Targeting.TextureCompressionFormat.TextureCompressionFormatAlias; import com.android.bundle.Targeting.VariantTargeting; -import com.android.tools.build.bundletool.model.ZipPath; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Range; import org.junit.Test; @@ -80,8 +78,7 @@ public void getDimensions_abi() { @Test public void getDimensions_deviceTier() { assertThat( - TargetingUtils.getTargetingDimensions( - assetsDirectoryTargeting(deviceTierTargeting("low")))) + TargetingUtils.getTargetingDimensions(assetsDirectoryTargeting(deviceTierTargeting(0)))) .containsExactly(TargetingDimension.DEVICE_TIER); } @@ -206,26 +203,6 @@ public void maxSdk_nonEmptySdkTargeting() { .isEqualTo(Integer.MAX_VALUE); } - @Test - public void containsDeviceTierTargeting_noDeviceTierTargeting() { - assertThat( - containsDeviceTierTargeting( - ImmutableSet.of( - TargetedDirectory.parse(ZipPath.create("assets/world/texture#tcf_etc1")), - TargetedDirectory.parse(ZipPath.create("assets/world/texture#tcf_astc"))))) - .isFalse(); - } - - @Test - public void containsDeviceTierTargeting_withDeviceTierTargeting() { - assertThat( - containsDeviceTierTargeting( - ImmutableSet.of( - TargetedDirectory.parse(ZipPath.create("assets/world/texture#tier_medium")), - TargetedDirectory.parse(ZipPath.create("assets/world/texture#tier_high"))))) - .isTrue(); - } - private static VariantTargeting variantTargetingFromSdkVersion(SdkVersion values) { return variantTargetingFromSdkVersion(values, ImmutableSet.of()); } diff --git a/src/test/java/com/android/tools/build/bundletool/model/utils/GetSizeCsvUtilsTest.java b/src/test/java/com/android/tools/build/bundletool/model/utils/GetSizeCsvUtilsTest.java index f85fe2f9..d30952b8 100644 --- a/src/test/java/com/android/tools/build/bundletool/model/utils/GetSizeCsvUtilsTest.java +++ b/src/test/java/com/android/tools/build/bundletool/model/utils/GetSizeCsvUtilsTest.java @@ -102,7 +102,7 @@ public void getSizeTotalOutputInCsv_withDimensionsAndCommasInConfiguration() { .setScreenDensity("480") .setLocale("en,fr") .setTextureCompressionFormat("ASTC,ETC2") - .setDeviceTier("low") + .setDeviceTier(1) .build(), 1L), ImmutableMap.of( @@ -112,7 +112,7 @@ public void getSizeTotalOutputInCsv_withDimensionsAndCommasInConfiguration() { .setScreenDensity("480") .setLocale("en,fr") .setTextureCompressionFormat("ASTC,ETC2") - .setDeviceTier("low") + .setDeviceTier(1) .build(), 6L)), ImmutableSet.of( @@ -121,7 +121,7 @@ public void getSizeTotalOutputInCsv_withDimensionsAndCommasInConfiguration() { .isEqualTo( "SDK,ABI,SCREEN_DENSITY,LANGUAGE,TEXTURE_COMPRESSION_FORMAT,DEVICE_TIER,MIN,MAX" + CRLF - + "22,\"x86,armeabi-v7a\",480,\"en,fr\",\"ASTC,ETC2\",low,1,6" + + "22,\"x86,armeabi-v7a\",480,\"en,fr\",\"ASTC,ETC2\",1,1,6" + CRLF); } } diff --git a/src/test/java/com/android/tools/build/bundletool/model/utils/TargetingProtoUtilsTest.java b/src/test/java/com/android/tools/build/bundletool/model/utils/TargetingProtoUtilsTest.java index 70985deb..5c374d98 100644 --- a/src/test/java/com/android/tools/build/bundletool/model/utils/TargetingProtoUtilsTest.java +++ b/src/test/java/com/android/tools/build/bundletool/model/utils/TargetingProtoUtilsTest.java @@ -71,9 +71,8 @@ public void toAlternativeTargeting_abi() { public void toAlternativeTargeting_deviceTier() { assertThat( TargetingProtoUtils.toAlternativeTargeting( - assetsDirectoryTargeting(deviceTierTargeting("low")))) - .isEqualTo( - assetsDirectoryTargeting(alternativeDeviceTierTargeting(ImmutableList.of("low")))); + assetsDirectoryTargeting(deviceTierTargeting(1)))) + .isEqualTo(assetsDirectoryTargeting(alternativeDeviceTierTargeting(ImmutableList.of(1)))); } @Test diff --git a/src/test/java/com/android/tools/build/bundletool/shards/SuffixStripperTest.java b/src/test/java/com/android/tools/build/bundletool/shards/SuffixStripperTest.java index 1d24349b..0cb30193 100644 --- a/src/test/java/com/android/tools/build/bundletool/shards/SuffixStripperTest.java +++ b/src/test/java/com/android/tools/build/bundletool/shards/SuffixStripperTest.java @@ -217,29 +217,25 @@ public void applySuffixStripping_deviceTier_suffixStrippingEnabled() { .setMasterSplit(true) .setEntries( ImmutableList.of( + createModuleEntryForFile("assets/img#tier_0/low_res_image.dat", DUMMY_CONTENT), createModuleEntryForFile( - "assets/img#tier_low/low_res_image.dat", DUMMY_CONTENT), - createModuleEntryForFile( - "assets/img#tier_high/high_res_image.dat", DUMMY_CONTENT))) + "assets/img#tier_1/high_res_image.dat", DUMMY_CONTENT))) .setAssetsConfig( assets( targetedAssetsDirectory( - "assets/img#tier_low", + "assets/img#tier_0", assetsDirectoryTargeting( - deviceTierTargeting( - "low", /* alternatives= */ ImmutableList.of("high")))), + deviceTierTargeting(0, /* alternatives= */ ImmutableList.of(1)))), targetedAssetsDirectory( - "assets/img#tier_high", + "assets/img#tier_1", assetsDirectoryTargeting( - deviceTierTargeting( - "high", /* alternatives= */ ImmutableList.of("low")))))) + deviceTierTargeting(1, /* alternatives= */ ImmutableList.of(0)))))) .build(); ModuleSplit strippedSplit = SuffixStripper.createForDimension(TargetingDimension.DEVICE_TIER) .applySuffixStripping( - split, - SuffixStripping.newBuilder().setDefaultSuffix("low").setEnabled(true).build()); + split, SuffixStripping.newBuilder().setDefaultSuffix("0").setEnabled(true).build()); // Check that the high tier sibling folder has been excluded assertThat(strippedSplit.getEntries()).hasSize(1); @@ -252,7 +248,7 @@ public void applySuffixStripping_deviceTier_suffixStrippingEnabled() { // Check that the APK and Variant targeting were applied. assertThat(strippedSplit.getApkTargeting()) - .isEqualTo(apkDeviceTierTargeting(deviceTierTargeting("low"))); + .isEqualTo(apkDeviceTierTargeting(deviceTierTargeting(0))); assertThat(strippedSplit.getVariantTargeting()).isEqualToDefaultInstance(); } @@ -267,42 +263,39 @@ public void applySuffixStripping_deviceTier_suffixStrippingDisabled_nonDefaultVa .setMasterSplit(true) .setEntries( ImmutableList.of( + createModuleEntryForFile("assets/img#tier_0/low_res_image.dat", DUMMY_CONTENT), createModuleEntryForFile( - "assets/img#tier_low/low_res_image.dat", DUMMY_CONTENT), - createModuleEntryForFile( - "assets/img#tier_high/high_res_image.dat", DUMMY_CONTENT))) + "assets/img#tier_1/high_res_image.dat", DUMMY_CONTENT))) .setAssetsConfig( assets( targetedAssetsDirectory( - "assets/img#tier_low", + "assets/img#tier_0", assetsDirectoryTargeting( - deviceTierTargeting( - "low", /* alternatives= */ ImmutableList.of("high")))), + deviceTierTargeting(0, /* alternatives= */ ImmutableList.of(1)))), targetedAssetsDirectory( - "assets/img#tier_high", + "assets/img#tier_1", assetsDirectoryTargeting( - deviceTierTargeting( - "high", /* alternatives= */ ImmutableList.of("low")))))) + deviceTierTargeting(1, /* alternatives= */ ImmutableList.of(0)))))) .build(); ModuleSplit strippedSplit = SuffixStripper.createForDimension(TargetingDimension.DEVICE_TIER) .applySuffixStripping( split, - SuffixStripping.newBuilder().setDefaultSuffix("low").setEnabled(false).build()); + SuffixStripping.newBuilder().setDefaultSuffix("0").setEnabled(false).build()); // Check that the high tier sibling folder has been excluded assertThat(strippedSplit.getEntries()).hasSize(1); assertThat(strippedSplit.getEntries().get(0).getPath()) - .isEqualTo(ZipPath.create("assets/img#tier_low/low_res_image.dat")); + .isEqualTo(ZipPath.create("assets/img#tier_0/low_res_image.dat")); assertThat(strippedSplit.getAssetsConfig().get().getDirectoryCount()).isEqualTo(1); assertThat(strippedSplit.getAssetsConfig().get().getDirectory(0).getPath()) - .isEqualTo("assets/img#tier_low"); + .isEqualTo("assets/img#tier_0"); // Check that the APK and Variant targeting were applied. assertThat(strippedSplit.getApkTargeting()) - .isEqualTo(apkDeviceTierTargeting(deviceTierTargeting("low"))); + .isEqualTo(apkDeviceTierTargeting(deviceTierTargeting(0))); assertThat(strippedSplit.getVariantTargeting()).isEqualToDefaultInstance(); } diff --git a/src/test/java/com/android/tools/build/bundletool/splitters/DeviceTierAssetsSplitterTest.java b/src/test/java/com/android/tools/build/bundletool/splitters/DeviceTierAssetsSplitterTest.java index 26ae65b6..32ad993c 100644 --- a/src/test/java/com/android/tools/build/bundletool/splitters/DeviceTierAssetsSplitterTest.java +++ b/src/test/java/com/android/tools/build/bundletool/splitters/DeviceTierAssetsSplitterTest.java @@ -47,30 +47,27 @@ public class DeviceTierAssetsSplitterTest { public void multipleDeviceTiersAndUntargetedFile() { BundleModule testModule = new BundleModuleBuilder("testModule") - .addFile("assets/images#tier_low/image.jpg") - .addFile("assets/images#tier_medium/image.jpg") - .addFile("assets/images#tier_high/image.jpg") + .addFile("assets/images#tier_0/image.jpg") + .addFile("assets/images#tier_1/image.jpg") + .addFile("assets/images#tier_2/image.jpg") .addFile("assets/file.txt") .setAssetsConfig( assets( targetedAssetsDirectory( - "assets/images#tier_low", + "assets/images#tier_0", assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "low", - /* alternatives= */ ImmutableList.of("medium", "high")))), + /* value= */ 0, /* alternatives= */ ImmutableList.of(1, 2)))), targetedAssetsDirectory( - "assets/images#tier_medium", + "assets/images#tier_1", assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "medium", - /* alternatives= */ ImmutableList.of("low", "high")))), + /* value= */ 1, /* alternatives= */ ImmutableList.of(0, 2)))), targetedAssetsDirectory( - "assets/images#tier_high", + "assets/images#tier_2", assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "high", - /* alternatives= */ ImmutableList.of("low", "medium")))), + /* value= */ 2, /* alternatives= */ ImmutableList.of(0, 1)))), targetedAssetsDirectory( "assets", AssetsDirectoryTargeting.getDefaultInstance()))) .setManifest(androidManifest("com.test.app")) @@ -90,53 +87,48 @@ public void multipleDeviceTiersAndUntargetedFile() { getSplitsWithTargetingEqualTo( assetsSplits, apkDeviceTierTargeting( - deviceTierTargeting( - /* value= */ "low", /* alternatives= */ ImmutableList.of("high", "medium")))); + deviceTierTargeting(/* value= */ 0, /* alternatives= */ ImmutableList.of(1, 2)))); assertThat(lowSplits).hasSize(1); assertThat(extractPaths(lowSplits.get(0).findEntriesUnderPath(ASSETS_DIRECTORY))) - .containsExactly("assets/images#tier_low/image.jpg"); + .containsExactly("assets/images#tier_0/image.jpg"); List mediumSplits = getSplitsWithTargetingEqualTo( assetsSplits, apkDeviceTierTargeting( - deviceTierTargeting( - /* value= */ "medium", /* alternatives= */ ImmutableList.of("high", "low")))); + deviceTierTargeting(/* value= */ 1, /* alternatives= */ ImmutableList.of(0, 2)))); assertThat(mediumSplits).hasSize(1); assertThat(extractPaths(mediumSplits.get(0).findEntriesUnderPath(ASSETS_DIRECTORY))) - .containsExactly("assets/images#tier_medium/image.jpg"); + .containsExactly("assets/images#tier_1/image.jpg"); List highSplits = getSplitsWithTargetingEqualTo( assetsSplits, apkDeviceTierTargeting( - deviceTierTargeting( - /* value= */ "high", /* alternatives= */ ImmutableList.of("low", "medium")))); + deviceTierTargeting(/* value= */ 2, /* alternatives= */ ImmutableList.of(0, 1)))); assertThat(highSplits).hasSize(1); assertThat(extractPaths(highSplits.get(0).findEntriesUnderPath(ASSETS_DIRECTORY))) - .containsExactly("assets/images#tier_high/image.jpg"); + .containsExactly("assets/images#tier_2/image.jpg"); } @Test public void deviceTiers_lowTierMissing_ok() { BundleModule testModule = new BundleModuleBuilder("testModule") - .addFile("assets/images#tier_medium/image.jpg") - .addFile("assets/images#tier_high/image.jpg") + .addFile("assets/images#tier_1/image.jpg") + .addFile("assets/images#tier_2/image.jpg") .setAssetsConfig( assets( targetedAssetsDirectory( - "assets/images#tier_medium", + "assets/images#tier_1", assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "medium", - /* alternatives= */ ImmutableList.of("high")))), + /* value= */ 1, /* alternatives= */ ImmutableList.of(2)))), targetedAssetsDirectory( - "assets/images#tier_high", + "assets/images#tier_2", assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "high", - /* alternatives= */ ImmutableList.of("medium")))))) + /* value= */ 2, /* alternatives= */ ImmutableList.of(1)))))) .setManifest(androidManifest("com.test.app")) .build(); @@ -153,50 +145,45 @@ public void deviceTiers_lowTierMissing_ok() { getSplitsWithTargetingEqualTo( assetsSplits, apkDeviceTierTargeting( - deviceTierTargeting( - /* value= */ "medium", /* alternatives= */ ImmutableList.of("high")))); + deviceTierTargeting(/* value= */ 1, /* alternatives= */ ImmutableList.of(2)))); assertThat(mediumSplits).hasSize(1); assertThat(extractPaths(mediumSplits.get(0).findEntriesUnderPath(ASSETS_DIRECTORY))) - .containsExactly("assets/images#tier_medium/image.jpg"); + .containsExactly("assets/images#tier_1/image.jpg"); List highSplits = getSplitsWithTargetingEqualTo( assetsSplits, apkDeviceTierTargeting( - deviceTierTargeting( - /* value= */ "high", /* alternatives= */ ImmutableList.of("medium")))); + deviceTierTargeting(/* value= */ 2, /* alternatives= */ ImmutableList.of(1)))); assertThat(highSplits).hasSize(1); assertThat(extractPaths(highSplits.get(0).findEntriesUnderPath(ASSETS_DIRECTORY))) - .containsExactly("assets/images#tier_high/image.jpg"); + .containsExactly("assets/images#tier_2/image.jpg"); } @Test public void multipleDeviceTiers_withSuffixStripping() { BundleModule testModule = new BundleModuleBuilder("testModule") - .addFile("assets/images#tier_low/image.jpg") - .addFile("assets/images#tier_medium/image.jpg") - .addFile("assets/images#tier_high/image.jpg") + .addFile("assets/images#tier_0/image.jpg") + .addFile("assets/images#tier_1/image.jpg") + .addFile("assets/images#tier_2/image.jpg") .setAssetsConfig( assets( targetedAssetsDirectory( - "assets/images#tier_low", + "assets/images#tier_0", assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "low", - /* alternatives= */ ImmutableList.of("high", "medium")))), + /* value= */ 0, /* alternatives= */ ImmutableList.of(1, 2)))), targetedAssetsDirectory( - "assets/images#tier_medium", + "assets/images#tier_1", assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "medium", - /* alternatives= */ ImmutableList.of("high", "low")))), + /* value= */ 1, /* alternatives= */ ImmutableList.of(0, 2)))), targetedAssetsDirectory( - "assets/images#tier_high", + "assets/images#tier_2", assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "high", - /* alternatives= */ ImmutableList.of("low", "medium")))))) + /* value= */ 2, /* alternatives= */ ImmutableList.of(0, 1)))))) .setManifest(androidManifest("com.test.app")) .build(); @@ -213,8 +200,7 @@ public void multipleDeviceTiers_withSuffixStripping() { getSplitsWithTargetingEqualTo( assetsSplits, apkDeviceTierTargeting( - deviceTierTargeting( - /* value= */ "low", /* alternatives= */ ImmutableList.of("high", "medium")))); + deviceTierTargeting(/* value= */ 0, /* alternatives= */ ImmutableList.of(1, 2)))); assertThat(lowSplits).hasSize(1); assertThat(extractPaths(lowSplits.get(0).findEntriesUnderPath(ASSETS_DIRECTORY))) .containsExactly("assets/images/image.jpg"); @@ -223,8 +209,7 @@ public void multipleDeviceTiers_withSuffixStripping() { getSplitsWithTargetingEqualTo( assetsSplits, apkDeviceTierTargeting( - deviceTierTargeting( - /* value= */ "medium", /* alternatives= */ ImmutableList.of("high", "low")))); + deviceTierTargeting(/* value= */ 1, /* alternatives= */ ImmutableList.of(0, 2)))); assertThat(mediumSplits).hasSize(1); assertThat(extractPaths(mediumSplits.get(0).findEntriesUnderPath(ASSETS_DIRECTORY))) .containsExactly("assets/images/image.jpg"); @@ -233,8 +218,7 @@ public void multipleDeviceTiers_withSuffixStripping() { getSplitsWithTargetingEqualTo( assetsSplits, apkDeviceTierTargeting( - deviceTierTargeting( - /* value= */ "high", /* alternatives= */ ImmutableList.of("low", "medium")))); + deviceTierTargeting(/* value= */ 2, /* alternatives= */ ImmutableList.of(0, 1)))); assertThat(highSplits).hasSize(1); assertThat(extractPaths(highSplits.get(0).findEntriesUnderPath(ASSETS_DIRECTORY))) .containsExactly("assets/images/image.jpg"); diff --git a/src/test/java/com/android/tools/build/bundletool/splitters/ModuleSplitterTest.java b/src/test/java/com/android/tools/build/bundletool/splitters/ModuleSplitterTest.java index 33f0d35c..c15fb57f 100644 --- a/src/test/java/com/android/tools/build/bundletool/splitters/ModuleSplitterTest.java +++ b/src/test/java/com/android/tools/build/bundletool/splitters/ModuleSplitterTest.java @@ -1249,23 +1249,21 @@ private ResourceTable getWelcomeLabel(ConfigValue... values) { public void deviceTierAsset_splitting_and_merging() { BundleModule testModule = new BundleModuleBuilder("testModule") - .addFile("assets/main#tier_low/image.jpg") - .addFile("assets/main#tier_medium/image.jpg") + .addFile("assets/main#tier_0/image.jpg") + .addFile("assets/main#tier_1/image.jpg") .addFile("dex/classes.dex") .setAssetsConfig( assets( targetedAssetsDirectory( - "assets/main#tier_low", + "assets/main#tier_0", assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "low", - /* alternatives= */ ImmutableList.of("medium")))), + /* value= */ 0, /* alternatives= */ ImmutableList.of(1)))), targetedAssetsDirectory( - "assets/main#tier_medium", + "assets/main#tier_1", assetsDirectoryTargeting( deviceTierTargeting( - /* value= */ "medium", - /* alternatives= */ ImmutableList.of("low")))))) + /* value= */ 1, /* alternatives= */ ImmutableList.of(0)))))) .setManifest(androidManifest("com.test.app")) .build(); @@ -1293,11 +1291,10 @@ public void deviceTierAsset_splitting_and_merging() { mergeApkTargeting( DEFAULT_MASTER_SPLIT_SDK_TARGETING, apkDeviceTierTargeting( - deviceTierTargeting( - /* value= */ "low", /* alternatives= */ ImmutableList.of("medium"))))); + deviceTierTargeting(/* value= */ 0, /* alternatives= */ ImmutableList.of(1))))); assertThat(lowTierSplits).hasSize(1); assertThat(extractPaths(lowTierSplits.get(0).getEntries())) - .containsExactly("assets/main#tier_low/image.jpg"); + .containsExactly("assets/main#tier_0/image.jpg"); ImmutableList mediumTierSplits = getSplitsWithTargetingEqualTo( @@ -1305,11 +1302,10 @@ public void deviceTierAsset_splitting_and_merging() { mergeApkTargeting( DEFAULT_MASTER_SPLIT_SDK_TARGETING, apkDeviceTierTargeting( - deviceTierTargeting( - /* value= */ "medium", /* alternatives= */ ImmutableList.of("low"))))); + deviceTierTargeting(/* value= */ 1, /* alternatives= */ ImmutableList.of(0))))); assertThat(mediumTierSplits).hasSize(1); assertThat(extractPaths(mediumTierSplits.get(0).getEntries())) - .containsExactly("assets/main#tier_medium/image.jpg"); + .containsExactly("assets/main#tier_1/image.jpg"); } @Test diff --git a/src/test/java/com/android/tools/build/bundletool/testing/DeviceFactory.java b/src/test/java/com/android/tools/build/bundletool/testing/DeviceFactory.java index 44e7ef26..e0d2b15a 100644 --- a/src/test/java/com/android/tools/build/bundletool/testing/DeviceFactory.java +++ b/src/test/java/com/android/tools/build/bundletool/testing/DeviceFactory.java @@ -23,6 +23,7 @@ import com.android.bundle.Devices.DeviceSpec; import com.android.bundle.Targeting.ScreenDensity.DensityAlias; import com.android.tools.build.bundletool.model.utils.Versions; +import com.google.protobuf.Int32Value; import com.google.protobuf.util.JsonFormat; import java.nio.file.Files; import java.nio.file.Path; @@ -137,8 +138,8 @@ public static DeviceSpec glExtensions(String... glExtensions) { return DeviceSpec.newBuilder().addAllGlExtensions(Arrays.asList(glExtensions)).build(); } - public static DeviceSpec deviceTier(String deviceTier) { - return DeviceSpec.newBuilder().setDeviceTier(deviceTier).build(); + public static DeviceSpec deviceTier(int deviceTier) { + return DeviceSpec.newBuilder().setDeviceTier(Int32Value.of(deviceTier)).build(); } public static DeviceSpec deviceGroups(String... deviceGroups) { diff --git a/src/test/java/com/android/tools/build/bundletool/testing/ProtoFuzzerTest.java b/src/test/java/com/android/tools/build/bundletool/testing/ProtoFuzzerTest.java index 1de36759..5919a41b 100644 --- a/src/test/java/com/android/tools/build/bundletool/testing/ProtoFuzzerTest.java +++ b/src/test/java/com/android/tools/build/bundletool/testing/ProtoFuzzerTest.java @@ -18,6 +18,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat; import com.android.bundle.Targeting.AbiTargeting; import com.android.bundle.Targeting.ApkTargeting; @@ -60,7 +61,7 @@ public void shuffleRepeatedFields() { public void randomProtoMessage_messageFieldPopulated() { ApkTargeting randomProto = ProtoFuzzer.randomProtoMessage(ApkTargeting.class); - assertThat(randomProto.getAbiTargeting()).isNotEqualTo(ApkTargeting.getDefaultInstance()); + assertThat(randomProto.getAbiTargeting()).isNotEqualToDefaultInstance(); } @Test diff --git a/src/test/java/com/android/tools/build/bundletool/testing/TargetingUtils.java b/src/test/java/com/android/tools/build/bundletool/testing/TargetingUtils.java index b396bb1a..f9f39f00 100644 --- a/src/test/java/com/android/tools/build/bundletool/testing/TargetingUtils.java +++ b/src/test/java/com/android/tools/build/bundletool/testing/TargetingUtils.java @@ -843,21 +843,23 @@ public static TextureCompressionFormatTargeting alternativeTextureCompressionTar // Device Tier targeting. - public static DeviceTierTargeting deviceTierTargeting(String value) { - return DeviceTierTargeting.newBuilder().addValue(value).build(); + public static DeviceTierTargeting deviceTierTargeting(int value) { + return DeviceTierTargeting.newBuilder().addValue(Int32Value.of(value)).build(); } public static DeviceTierTargeting deviceTierTargeting( - String value, ImmutableList alternatives) { + int value, ImmutableList alternatives) { return DeviceTierTargeting.newBuilder() - .addValue(value) - .addAllAlternatives(alternatives) + .addValue(Int32Value.of(value)) + .addAllAlternatives(alternatives.stream().map(Int32Value::of).collect(toImmutableList())) .build(); } public static DeviceTierTargeting alternativeDeviceTierTargeting( - ImmutableList alternatives) { - return DeviceTierTargeting.newBuilder().addAllAlternatives(alternatives).build(); + ImmutableList alternatives) { + return DeviceTierTargeting.newBuilder() + .addAllAlternatives(alternatives.stream().map(Int32Value::of).collect(toImmutableList())) + .build(); } // Device Feature targeting. diff --git a/src/test/java/com/android/tools/build/bundletool/validation/BundleConfigValidatorTest.java b/src/test/java/com/android/tools/build/bundletool/validation/BundleConfigValidatorTest.java index 290d7daa..e3a02877 100644 --- a/src/test/java/com/android/tools/build/bundletool/validation/BundleConfigValidatorTest.java +++ b/src/test/java/com/android/tools/build/bundletool/validation/BundleConfigValidatorTest.java @@ -16,15 +16,11 @@ package com.android.tools.build.bundletool.validation; import static com.android.tools.build.bundletool.testing.ManifestProtoUtils.androidManifest; -import static com.android.tools.build.bundletool.testing.ManifestProtoUtils.withMinSdkVersion; import static com.google.common.truth.Truth.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import com.android.aapt.Resources.ResourceTable; import com.android.bundle.Config.BundleConfig; -import com.android.bundle.Config.Optimizations; -import com.android.bundle.Config.ResourceOptimizations; -import com.android.bundle.Config.ResourceOptimizations.SparseEncoding; import com.android.bundle.Config.SplitDimension; import com.android.bundle.Config.SplitDimension.Value; import com.android.bundle.Config.SuffixStripping; @@ -294,12 +290,12 @@ public void optimizations_defaultDeviceTier_targeted_specified() throws Exceptio new AppBundleBuilder() .setBundleConfig( BundleConfigBuilder.create() - .addSplitDimension(Value.DEVICE_TIER, false, false, "medium") + .addSplitDimension(Value.DEVICE_TIER, false, false, "1") .build()) .addModule( new BundleModuleBuilder("base") - .addFile("assets/textures#tier_medium/level1.assets") - .addFile("assets/textures#tier_high/level1.assets") + .addFile("assets/textures#tier_1/level1.assets") + .addFile("assets/textures#tier_2/level1.assets") .setManifest(androidManifest("com.test.app")) .build()); @@ -307,7 +303,7 @@ public void optimizations_defaultDeviceTier_targeted_specified() throws Exceptio } @Test - public void optimizations_defaultDeviceTier_targeted_unspecified_throws() throws Exception { + public void optimizations_defaultDeviceTier_targeted_unspecified_success() throws Exception { AppBundleBuilder appBundleBuilder = new AppBundleBuilder() .addModule( @@ -316,68 +312,14 @@ public void optimizations_defaultDeviceTier_targeted_unspecified_throws() throws .build()) .addModule( new BundleModuleBuilder("a") - .addFile("assets/textures#tier_medium/level1.assets") - .addFile("assets/textures#tier_high/level1.assets") + .addFile("assets/textures#tier_1/level1.assets") + .addFile("assets/textures#tier_2/level1.assets") .setManifest(androidManifest("com.test.app")) .build()); - InvalidBundleException e = - assertThrows( - InvalidBundleException.class, - () -> new BundleConfigValidator().validateBundle(appBundleBuilder.build())); - assertThat(e) - .hasMessageThat() - .contains( - "Module 'a' contains assets targeted by device tier but default device tier value has" - + " not been specified for the bundle."); - } - - @Test - public void optimizations_sparseEncodingEnforced_minSdk26_ok() { - AppBundleBuilder appBundleBuilder = - new AppBundleBuilder() - .addModule( - new BundleModuleBuilder("base") - .setManifest(androidManifest("com.test.app", withMinSdkVersion(26))) - .build()) - .setBundleConfig( - BundleConfig.newBuilder() - .setOptimizations( - Optimizations.newBuilder() - .setResourceOptimizations( - ResourceOptimizations.newBuilder() - .setSparseEncoding(SparseEncoding.ENFORCED))) - .build()); - new BundleConfigValidator().validateBundle(appBundleBuilder.build()); } - @Test - public void optimizations_sparseEncodingEnforced_minSdk21_throws() { - AppBundleBuilder appBundleBuilder = - new AppBundleBuilder() - .addModule( - new BundleModuleBuilder("base") - .setManifest(androidManifest("com.test.app", withMinSdkVersion(21))) - .build()) - .setBundleConfig( - BundleConfig.newBuilder() - .setOptimizations( - Optimizations.newBuilder() - .setResourceOptimizations( - ResourceOptimizations.newBuilder() - .setSparseEncoding(SparseEncoding.ENFORCED))) - .build()); - - InvalidBundleException e = - assertThrows( - InvalidBundleException.class, - () -> new BundleConfigValidator().validateBundle(appBundleBuilder.build())); - assertThat(e) - .hasMessageThat() - .contains("Sparse encoding is available for applications with minSdk >= 26."); - } - @Test public void version_valid_ok() throws Exception { AppBundle appBundle = createAppBundle(BundleConfigBuilder.create().setVersion(CURRENT_VERSION)); diff --git a/src/test/java/com/android/tools/build/bundletool/validation/DeviceTierParityValidatorTest.java b/src/test/java/com/android/tools/build/bundletool/validation/DeviceTierParityValidatorTest.java index bec0a9b3..6d45dad1 100644 --- a/src/test/java/com/android/tools/build/bundletool/validation/DeviceTierParityValidatorTest.java +++ b/src/test/java/com/android/tools/build/bundletool/validation/DeviceTierParityValidatorTest.java @@ -49,14 +49,14 @@ public void noTiers_ok() { public void sameTiers_ok() { BundleModule moduleA = new BundleModuleBuilder("a") - .addFile("assets/img#tier_low/image.jpg") - .addFile("assets/img#tier_high/image.jpg") + .addFile("assets/img#tier_0/image.jpg") + .addFile("assets/img#tier_1/image.jpg") .setManifest(androidManifest("com.test.app")) .build(); BundleModule moduleB = new BundleModuleBuilder("b") - .addFile("assets/img#tier_low/image.jpg") - .addFile("assets/img#tier_high/image.jpg") + .addFile("assets/img#tier_0/image.jpg") + .addFile("assets/img#tier_1/image.jpg") .setManifest(androidManifest("com.test.app")) .build(); @@ -67,14 +67,14 @@ public void sameTiers_ok() { public void sameTiersAndNoTier_ok() { BundleModule moduleA = new BundleModuleBuilder("a") - .addFile("assets/img#tier_low/image.jpg") - .addFile("assets/img#tier_high/image.jpg") + .addFile("assets/img#tier_0/image.jpg") + .addFile("assets/img#tier_1/image.jpg") .setManifest(androidManifest("com.test.app")) .build(); BundleModule moduleB = new BundleModuleBuilder("b") - .addFile("assets/img#tier_low/image.jpg") - .addFile("assets/img#tier_high/image.jpg") + .addFile("assets/img#tier_0/image.jpg") + .addFile("assets/img#tier_1/image.jpg") .setManifest(androidManifest("com.test.app")) .build(); BundleModule moduleC = @@ -87,14 +87,14 @@ public void sameTiersAndNoTier_ok() { public void differentTiers_throws() { BundleModule moduleA = new BundleModuleBuilder("a") - .addFile("assets/img#tier_low/image.jpg") - .addFile("assets/img#tier_high/image.jpg") + .addFile("assets/img#tier_0/image.jpg") + .addFile("assets/img#tier_2/image.jpg") .setManifest(androidManifest("com.test.app")) .build(); BundleModule moduleB = new BundleModuleBuilder("b") - .addFile("assets/img#tier_low/image.jpg") - .addFile("assets/img#tier_medium/image.jpg") + .addFile("assets/img#tier_0/image.jpg") + .addFile("assets/img#tier_1/image.jpg") .setManifest(androidManifest("com.test.app")) .build(); @@ -109,6 +109,6 @@ public void differentTiers_throws() { .hasMessageThat() .contains( "All modules with device tier targeting must support the same set of tiers, but module" - + " 'a' supports [low, high] and module 'b' supports [low, medium]."); + + " 'a' supports [0, 2] and module 'b' supports [0, 1]."); } }