Skip to content

Commit

Permalink
Prepare for release 1.13.2.
Browse files Browse the repository at this point in the history
  • Loading branch information
sopo-c committed Dec 15, 2022
1 parent db27934 commit 9e554ac
Show file tree
Hide file tree
Showing 47 changed files with 1,449 additions and 113 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -46,4 +46,4 @@ https://developer.android.com/studio/command-line/bundletool

## Releases

Latest release: [1.13.1](https://github.com/google/bundletool/releases)
Latest release: [1.13.2](https://github.com/google/bundletool/releases)
1 change: 1 addition & 0 deletions build.gradle
Expand Up @@ -201,6 +201,7 @@ shadowJar {
exclude 'com.android.vending.splits'
exclude 'com.android.vending.splits.required'
exclude 'com.android.dynamic.apk.fused.modules'
exclude 'com.android.tools.build.profiles'
}

// Exclude ddmlib protos from maven jar
Expand Down
1 change: 0 additions & 1 deletion dex_src/README.md

This file was deleted.

2 changes: 1 addition & 1 deletion gradle.properties
@@ -1 +1 @@
release_version = 1.13.1
release_version = 1.13.2
Expand Up @@ -33,12 +33,15 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.joining;

import com.android.apksig.SigningCertificateLineage;
import com.android.apksig.apk.ApkFormatException;
import com.android.apksig.util.DataSource;
import com.android.apksig.util.DataSources;
import com.android.bundle.Devices.DeviceSpec;
import com.android.bundle.RuntimeEnabledSdkConfigProto.LocalDeploymentRuntimeEnabledSdkConfig;
import com.android.bundle.RuntimeEnabledSdkConfigProto.RuntimeEnabledSdk;
import com.android.tools.build.bundletool.androidtools.Aapt2Command;
import com.android.tools.build.bundletool.androidtools.P7ZipCommand;
Expand Down Expand Up @@ -67,6 +70,7 @@
import com.android.tools.build.bundletool.model.exceptions.InvalidCommandException;
import com.android.tools.build.bundletool.model.utils.DefaultSystemEnvironmentProvider;
import com.android.tools.build.bundletool.model.utils.SystemEnvironmentProvider;
import com.android.tools.build.bundletool.model.utils.files.BufferedIo;
import com.android.tools.build.bundletool.model.utils.files.FileUtils;
import com.android.tools.build.bundletool.preprocessors.AppBundlePreprocessorManager;
import com.android.tools.build.bundletool.preprocessors.DaggerAppBundlePreprocessorComponent;
Expand All @@ -88,21 +92,21 @@
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.protobuf.util.JsonFormat;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

Expand Down Expand Up @@ -211,6 +215,8 @@ public enum OutputFormat {
Flag.pathSet("sdk-bundles");
private static final Flag<ImmutableSet<Path>> RUNTIME_ENABLED_SDK_ARCHIVE_LOCATIONS_FLAG =
Flag.pathSet("sdk-archives");
private static final Flag<Path> LOCAL_DEPLOYMENT_RUNTIME_ENABLED_SDK_CONFIG_FLAG =
Flag.path("local-runtime-enabled-sdk-config");

// Archive APK related flags.
private static final Flag<String> APP_STORE_PACKAGE_NAME_FLAG = Flag.string("store-package");
Expand Down Expand Up @@ -300,6 +306,9 @@ ListeningExecutorService getExecutorService() {

public abstract ImmutableSet<Path> getRuntimeEnabledSdkArchivePaths();

public abstract Optional<LocalDeploymentRuntimeEnabledSdkConfig>
getLocalDeploymentRuntimeEnabledSdkConfig();

public abstract Optional<String> getAppStorePackageName();

public static Builder builder() {
Expand Down Expand Up @@ -547,6 +556,13 @@ public Builder setCreateApkSetArchive(boolean createApkSetArchive) {
*/
public abstract Builder setRuntimeEnabledSdkArchivePaths(ImmutableSet<Path> sdkArchivePaths);

/**
* Options for overriding parts of runtime-enabled SDK dependency configuration of the app for
* local deployment and testing.
*/
public abstract Builder setLocalDeploymentRuntimeEnabledSdkConfig(
LocalDeploymentRuntimeEnabledSdkConfig localDeploymentRuntimeEnabledSdkConfig);

/**
* Sets package name of an app store that will be called by archived app to redownload the
* application.
Expand Down Expand Up @@ -709,6 +725,16 @@ public BuildApksCommand build() {
+ " archives, but both were set.")
.build();
}

if (command.getLocalDeploymentRuntimeEnabledSdkConfig().isPresent()
&& command.getRuntimeEnabledSdkBundlePaths().isEmpty()
&& command.getRuntimeEnabledSdkArchivePaths().isEmpty()) {
throw InvalidCommandException.builder()
.withInternalMessage(
"Using --local-deployment-runtime-enabled-sdk-config flag requires either"
+ " --sdk-bundles or --sdk-archives flag to be also present.")
.build();
}
return command;
}
}
Expand Down Expand Up @@ -823,6 +849,25 @@ static BuildApksCommand fromFlags(
.getValue(flags)
.ifPresent(buildApksCommand::setRuntimeEnabledSdkArchivePaths);

if (LOCAL_DEPLOYMENT_RUNTIME_ENABLED_SDK_CONFIG_FLAG.getValue(flags).isPresent()
&& !RUNTIME_ENABLED_SDK_BUNDLE_LOCATIONS_FLAG.getValue(flags).isPresent()
&& !RUNTIME_ENABLED_SDK_ARCHIVE_LOCATIONS_FLAG.getValue(flags).isPresent()) {
throw InvalidCommandException.builder()
.withInternalMessage(
"'%s' flag can only be set together with '%s' or '%s' flags.",
LOCAL_DEPLOYMENT_RUNTIME_ENABLED_SDK_CONFIG_FLAG.getName(),
RUNTIME_ENABLED_SDK_BUNDLE_LOCATIONS_FLAG.getName(),
RUNTIME_ENABLED_SDK_ARCHIVE_LOCATIONS_FLAG.getName())
.build();
}

LOCAL_DEPLOYMENT_RUNTIME_ENABLED_SDK_CONFIG_FLAG
.getValue(flags)
.ifPresent(
configPath ->
buildApksCommand.setLocalDeploymentRuntimeEnabledSdkConfig(
parseLocalRuntimeEnabledSdkConfig(configPath)));

APP_STORE_PACKAGE_NAME_FLAG.getValue(flags).ifPresent(buildApksCommand::setAppStorePackageName);

flags.checkNoUnknownFlags();
Expand Down Expand Up @@ -914,6 +959,17 @@ private void validateInput() {
checkFileExistsAndExecutable(getAdbPath().get());
}

if (!shouldGenerateSdkRuntimeVariant(getApkBuildMode())) {
checkArgument(
getRuntimeEnabledSdkBundlePaths().isEmpty(),
"runtimeEnabledSdkBundlePaths can not be present in '%s' mode.",
getApkBuildMode().name());
checkArgument(
getRuntimeEnabledSdkArchivePaths().isEmpty(),
"runtimeEnabledSdkArchivePaths can not be present in '%s' mode.",
getApkBuildMode().name());
}

getRuntimeEnabledSdkBundlePaths()
.forEach(
path -> {
Expand All @@ -930,6 +986,9 @@ private void validateInput() {

private ImmutableMap<String, BundleModule> getValidatedSdkModules(
Closer closer, TempDirectory tempDir, AppBundle appBundle) throws IOException {
if (!shouldGenerateSdkRuntimeVariant(getApkBuildMode())) {
return ImmutableMap.of();
}
if (!getRuntimeEnabledSdkArchivePaths().isEmpty()) {
ImmutableMap<String, SdkAsar> sdkAsars = getValidatedSdkAsarsByPackageName(closer, tempDir);
validateSdkAsarsMatchAppBundleDependencies(appBundle, sdkAsars);
Expand Down Expand Up @@ -1579,6 +1638,18 @@ public static CommandHelp help() {
+ " .asar. Can not be used together with the '%s' flag.",
RUNTIME_ENABLED_SDK_BUNDLE_LOCATIONS_FLAG.getName())
.build())
.addFlag(
FlagDescription.builder()
.setFlagName(LOCAL_DEPLOYMENT_RUNTIME_ENABLED_SDK_CONFIG_FLAG.getName())
.setExampleValue("path/to/config.json")
.setOptional(true)
.setDescription(
"Path to config file that contains options for overriding parts of"
+ " runtime-enabled SDK dependency configuration of the app bundle. Can"
+ " only be used if either '%s' or '%s' is set.",
RUNTIME_ENABLED_SDK_BUNDLE_LOCATIONS_FLAG.getName(),
RUNTIME_ENABLED_SDK_ARCHIVE_LOCATIONS_FLAG.getName())
.build())
.addFlag(
FlagDescription.builder()
.setFlagName(APP_STORE_PACKAGE_NAME_FLAG.getName())
Expand All @@ -1593,10 +1664,7 @@ public static CommandHelp help() {
}

private static String joinFlagOptions(Enum<?>... flagOptions) {
return Arrays.stream(flagOptions)
.map(Enum::name)
.map(String::toLowerCase)
.collect(Collectors.joining("|"));
return stream(flagOptions).map(Enum::name).map(String::toLowerCase).collect(joining("|"));
}

private static void populateSigningConfigurationFromFlags(
Expand Down Expand Up @@ -1787,4 +1855,30 @@ private static SigningConfiguration getStampSigningConfiguration(
return SigningConfiguration.extractFromKeystore(
keystorePath, keyAlias, keystorePassword, keyPassword);
}

private static LocalDeploymentRuntimeEnabledSdkConfig parseLocalRuntimeEnabledSdkConfig(
Path configPath) {
try (Reader configReader = BufferedIo.reader(configPath)) {
LocalDeploymentRuntimeEnabledSdkConfig.Builder configBuilder =
LocalDeploymentRuntimeEnabledSdkConfig.newBuilder();
JsonFormat.parser().merge(configReader, configBuilder);
return configBuilder.build();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

private static boolean shouldGenerateSdkRuntimeVariant(ApkBuildMode mode) {
switch (mode) {
case DEFAULT:
case UNIVERSAL:
case SYSTEM:
case PERSISTENT:
return true;
case INSTANT:
case ARCHIVE:
return false;
}
throw new IllegalStateException();
}
}
Expand Up @@ -20,6 +20,7 @@

import com.android.bundle.Config.BundleConfig;
import com.android.bundle.Devices.DeviceSpec;
import com.android.bundle.RuntimeEnabledSdkConfigProto.LocalDeploymentRuntimeEnabledSdkConfig;
import com.android.tools.build.bundletool.androidtools.P7ZipCommand;
import com.android.tools.build.bundletool.commands.BuildApksCommand.ApkBuildMode;
import com.android.tools.build.bundletool.device.AdbServer;
Expand Down Expand Up @@ -173,6 +174,13 @@ static boolean provideVerbose(BuildApksCommand command) {
return command.getVerbose();
}

@CommandScoped
@Provides
static Optional<LocalDeploymentRuntimeEnabledSdkConfig> provideLocalRuntimeEnabledSdkConfig(
BuildApksCommand command) {
return command.getLocalDeploymentRuntimeEnabledSdkConfig();
}

/**
* Qualifying annotation of an {@code Optional<Integer>} for the first variant number to use when
* numbering the generated variants.
Expand Down
Expand Up @@ -124,7 +124,7 @@ protected ImmutableList<GeneratedApk> getMatchingApks(
countrySetTargeting,
sdkRuntimeTargeting),
getSizeRequest.getModules(),
/* includeInstallTimeAssetModules= */ true,
/* includeInstallTimeAssetModules= */ !getSizeRequest.getModules().isPresent(),
getSizeRequest.getInstant(),
/* ensureDensityAndAbiApksMatched= */ false)
.getMatchingApksFromAssetModules(assetModules);
Expand Down
Expand Up @@ -92,7 +92,7 @@ protected ImmutableList<GeneratedApk> getMatchingApks(
countrySetTargeting,
sdkRuntimeTargeting),
getSizeRequest.getModules(),
/* includeInstallTimeAssetModules= */ true,
/* includeInstallTimeAssetModules= */ false,
getSizeRequest.getInstant(),
/* ensureDensityAndAbiApksMatched= */ false)
.getMatchingApksFromVariant(variant, bundleVersion);
Expand Down
Expand Up @@ -40,6 +40,7 @@ public interface ApkSetWriter {

void writeApkSet(BuildSdkApksResult toc) throws IOException;


/** Creates ApkSet writer which stores all splits uncompressed inside output directory. */
static ApkSetWriter directory(Path outputDirectory) {
return new ApkSetWriter() {
Expand All @@ -57,6 +58,7 @@ public void writeApkSet(BuildApksResult toc) throws IOException {
public void writeApkSet(BuildSdkApksResult toc) throws IOException {
Files.write(getSplitsDirectory().resolve(TABLE_OF_CONTENTS_FILE), toc.toByteArray());
}

};
}

Expand Down Expand Up @@ -100,6 +102,7 @@ public void writeApkSet(BuildSdkApksResult toc) throws IOException {
zipApkSet(apkRelativePaths, toc.toByteArray());
}


private void zipApkSet(ImmutableSet<String> apkRelativePaths, byte[] tocBytes)
throws IOException {
try (ZipArchive zipArchive = new ZipArchive(outputFile)) {
Expand Down
Expand Up @@ -40,8 +40,16 @@ public static <T> ImmutableList<T> waitForAll(Iterable<ListenableFuture<T>> futu
// reason that we don't use Futures#allAsList() directly is it will fail-fast and will cause the
// other existing futures to continue running until they fail (e.g. if they depend on the
// temporary file which gets deleted then it will produce NoFileFound exception).
waitFor(Futures.whenAllComplete(futures).call(() -> null, directExecutor()));
return ImmutableList.copyOf(waitFor(Futures.allAsList(futures)));
try {
return ImmutableList.copyOf(waitFor(Futures.allAsList(futures)));
} catch (RuntimeException e) {
try {
waitFor(Futures.whenAllComplete(futures).call(() -> null, directExecutor()));
} catch (RuntimeException ignoredException) {
// Silently ignored - only report the very first Exception encountered.
}
throw e;
}
}

public static <K, V> ImmutableMap<K, V> waitForAll(Map<K, ListenableFuture<V>> futures) {
Expand Down
Expand Up @@ -158,6 +158,7 @@ public abstract class AndroidManifest {
public static final String FITS_SYSTEM_WINDOWS_ATTRIBUTE_NAME = "fitsSystemWindows";
public static final String SRC_ATTRIBUTE_NAME = "src";
public static final String APP_COMPONENT_FACTORY_ATTRIBUTE_NAME = "appComponentFactory";
public static final String AUTHORITIES_ATTRIBUTE_NAME = "authorities";

public static final String LEANBACK_FEATURE_NAME = "android.software.leanback";
public static final String TOUCHSCREEN_FEATURE_NAME = "android.hardware.touchscreen";
Expand Down Expand Up @@ -219,6 +220,7 @@ public abstract class AndroidManifest {
public static final int FITS_SYSTEM_WINDOWS_RESOURCE_ID = 0x010100dd;
public static final int SRC_RESOURCE_ID = 0x01010119;
public static final int APP_COMPONENT_FACTORY_RESOURCE_ID = 0x0101057a;
public static final int AUTHORITIES_RESOURCE_ID = 0x01010018;

// Matches the value of android.os.Build.VERSION_CODES.CUR_DEVELOPMENT, used when turning
// a manifest attribute which references a prerelease API version (e.g., "Q") into an integer.
Expand Down Expand Up @@ -929,6 +931,12 @@ public Optional<String> getAppComponentFactoryAttribute() {
.map(XmlProtoAttribute::getValueAsString);
}

/** Gets the Authorities class name if it is set in the AndroidManifest. */
public Optional<String> getAuthoritiesAttribute() {
return getApplicationAttribute(AUTHORITIES_RESOURCE_ID)
.map(XmlProtoAttribute::getValueAsString);
}

private Optional<XmlProtoAttribute> getPropertyValue(String attributeName) {
return getApplicationElementChildElements(PROPERTY_ELEMENT_NAME).stream()
.filter(element -> element.getAndroidAttribute(NAME_RESOURCE_ID).isPresent())
Expand Down
Expand Up @@ -80,6 +80,7 @@
import static java.util.stream.Collectors.joining;

import com.android.tools.build.bundletool.model.manifestelements.Activity;
import com.android.tools.build.bundletool.model.manifestelements.Provider;
import com.android.tools.build.bundletool.model.manifestelements.Receiver;
import com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoAttribute;
import com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoAttributeBuilder;
Expand Down Expand Up @@ -393,6 +394,14 @@ public ManifestEditor addReceiver(Receiver receiver) {
return this;
}

@CanIgnoreReturnValue
public ManifestEditor addProvider(Provider provider) {
manifestElement
.getOrCreateChildElement(APPLICATION_ELEMENT_NAME)
.addChildElement(provider.asXmlProtoElement().toBuilder());
return this;
}

/** Adds uses-sdk-library tag to the manifest. */
@CanIgnoreReturnValue
public ManifestEditor addUsesSdkLibraryElement(
Expand Down Expand Up @@ -511,6 +520,24 @@ public ManifestEditor setDeliveryOptionsForRuntimeEnabledSdkModule() {
return this;
}

/** Sets distribution module that is required for a recovery module. */
@CanIgnoreReturnValue
public ManifestEditor setDistributionModuleForRecoveryModule() {
manifestElement
.getOrCreateChildElement(DISTRIBUTION_NAMESPACE_URI, MODULE_ELEMENT_NAME)
.addChildElement(
XmlProtoElementBuilder.create(DISTRIBUTION_NAMESPACE_URI, DELIVERY_ELEMENT_NAME)
.addChildElement(
XmlProtoElementBuilder.create(DISTRIBUTION_NAMESPACE_URI, "on-demand")))
.addChildElement(
XmlProtoElementBuilder.create(DISTRIBUTION_NAMESPACE_URI, FUSING_ELEMENT_NAME)
.addAttribute(
XmlProtoAttributeBuilder.create(
DISTRIBUTION_NAMESPACE_URI, INCLUDE_ATTRIBUTE_NAME)
.setValueAsBoolean(false)));
return this;
}

@CanIgnoreReturnValue
public ManifestEditor removeUsesSdkElement() {
manifestElement.removeChildrenElementsIf(
Expand Down

0 comments on commit 9e554ac

Please sign in to comment.