Skip to content

Commit

Permalink
Prepare for release 1.9.1.
Browse files Browse the repository at this point in the history
  • Loading branch information
Iurii Makhno committed Apr 1, 2022
1 parent 9502d28 commit 791d6cc
Show file tree
Hide file tree
Showing 103 changed files with 4,787 additions and 232 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ https://developer.android.com/studio/command-line/bundletool

## Releases

Latest release: [1.9.0](https://github.com/google/bundletool/releases)
Latest release: [1.9.1](https://github.com/google/bundletool/releases)
29 changes: 24 additions & 5 deletions archive/com/google/android/archive/ReactivateActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,28 @@

package com.google.android.archive;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;

/** Activity that triggers the reactivation of an app through the app store. */
public class ReactivateActivity extends Activity implements DialogInterface.OnClickListener {

public static final String STORE_PACKAGE_NAME = "com.android.vending";

private String appStorePackageName;
private boolean processingError;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
appStorePackageName = getAppStorePackageName();
}

@Override
public void onResume() {
super.onResume();
Expand All @@ -41,7 +48,7 @@ public void onResume() {
}
Intent intent = new Intent();
intent.setAction("com.google.android.STORE_ARCHIVE");
intent.setPackage(STORE_PACKAGE_NAME);
intent.setPackage(appStorePackageName);
try {
startActivityForResult(intent, /* flags= */ 0);
} catch (ActivityNotFoundException e) {
Expand All @@ -52,7 +59,7 @@ public void onResume() {
/** Returns true if the targeted Store is installed and enabled. */
private boolean isStoreInstalled() {
try {
return getPackageManager().getApplicationInfo(STORE_PACKAGE_NAME, 0).enabled;
return getPackageManager().getApplicationInfo(appStorePackageName, 0).enabled;
} catch (NameNotFoundException e) {
return false;
}
Expand Down Expand Up @@ -99,7 +106,7 @@ public void onClick(DialogInterface ignored, int buttonType) {
private void openStorePageForApp() {
Intent intent =
new Intent(Intent.ACTION_VIEW)
.setPackage(STORE_PACKAGE_NAME)
.setPackage(appStorePackageName)
.setData(Uri.parse(String.format("market://details?id=%s", getPackageName())));

startActivity(intent);
Expand All @@ -116,4 +123,16 @@ public void onActivityResult(int ignored1, int resultCode, Intent ignored2) {
finish();
}
}

/**
* Getting resource by id does not work because classes.dex is prebuild and
* reactivation_app_store_package_name resource is added dynamically with the next available id.
*/
@SuppressLint("DiscouragedApi")
private String getAppStorePackageName() {
return getResources()
.getString(
getResources()
.getIdentifier("reactivation_app_store_package_name", "string", getPackageName()));
}
}
10 changes: 5 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ dependencies {
implementation "com.android.tools.ddms:ddmlib:30.1.0-alpha07"
implementation "com.android:zipflinger:7.1.0-alpha07"

shadow "com.android.tools.build:aapt2-proto:7.0.0-beta04-7396180"
shadow "com.android.tools.build:aapt2-proto:7.3.0-alpha07-8248216"
shadow "com.google.auto.value:auto-value-annotations:1.6.2"
annotationProcessor "com.google.auto.value:auto-value:1.6.2"
shadow "com.google.errorprone:error_prone_annotations:2.3.1"
Expand All @@ -52,15 +52,15 @@ dependencies {
}
shadow "org.slf4j:slf4j-api:1.7.30"

implementationWindows "com.android.tools.build:aapt2:7.0.0-beta04-7396180:windows"
implementationMacOs "com.android.tools.build:aapt2:7.0.0-beta04-7396180:osx"
implementationLinux "com.android.tools.build:aapt2:7.0.0-beta04-7396180:linux"
implementationWindows "com.android.tools.build:aapt2:7.3.0-alpha07-8248216:windows"
implementationMacOs "com.android.tools.build:aapt2:7.3.0-alpha07-8248216:osx"
implementationLinux "com.android.tools.build:aapt2:7.3.0-alpha07-8248216:linux"

compileOnly "org.bouncycastle:bcprov-jdk15on:1.56"
compileOnly "org.bouncycastle:bcpkix-jdk15on:1.56"
runtimeOnly "org.slf4j:slf4j-jdk14:1.7.30"

testImplementation "com.android.tools.build:aapt2-proto:7.0.0-beta04-7396180"
testImplementation "com.android.tools.build:aapt2-proto:7.3.0-alpha07-8248216"
testImplementation "com.google.auto.value:auto-value-annotations:1.6.2"
testAnnotationProcessor "com.google.auto.value:auto-value:1.6.2"
testImplementation "com.google.errorprone:error_prone_annotations:2.3.1"
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
release_version = 1.9.0
release_version = 1.9.1
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public static AndroidManifest createArchivedManifest(AndroidManifest manifest) {
manifest.getMinSdkVersion().ifPresent(editor::setMinSdkVersion);
manifest.getMaxSdkVersion().ifPresent(editor::setMaxSdkVersion);
manifest.getTargetSdkVersion().ifPresent(editor::setTargetSdkVersion);
manifest.getTargetSandboxVersion().ifPresent(editor::setTargetSandboxVersion);

if (manifest.hasApplicationElement()) {
manifest.getDescription().ifPresent(editor::setDescription);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.android.tools.build.bundletool.model.BundleModule;
import com.android.tools.build.bundletool.model.ModuleSplit;
import com.android.tools.build.bundletool.model.ResourceId;
import com.android.tools.build.bundletool.model.ResourceInjector;
import com.android.tools.build.bundletool.model.ResourceTableEntry;
import com.android.tools.build.bundletool.model.exceptions.InvalidCommandException;
import com.android.tools.build.bundletool.model.utils.ResourcesUtils;
Expand All @@ -45,6 +46,10 @@
* resources and two custom actions to clear app cache and to wake up an app.
*/
public final class ArchivedApksGenerator {
public static final String APP_STORE_PACKAGE_NAME_RESOURCE_NAME =
"reactivation_app_store_package_name";
public static final String PLAY_STORE_PACKAGE_NAME = "com.android.vending";

private static final String ARCHIVED_CLASSES_DEX_PATH = "dex/classes.dex";

private final TempDirectory globalTempDir;
Expand All @@ -54,15 +59,17 @@ public final class ArchivedApksGenerator {
this.globalTempDir = globalTempDir;
}

public ModuleSplit generateArchivedApk(AppBundle appBundle) throws IOException {
public ModuleSplit generateArchivedApk(
AppBundle appBundle, Optional<String> customAppStorePackageName) throws IOException {
validateRequest(appBundle);

BundleModule baseModule = appBundle.getBaseModule();

AndroidManifest archivedManifest =
ArchivedAndroidManifestUtils.createArchivedManifest(baseModule.getAndroidManifest());
Optional<ResourceTable> archivedResourceTable =
getArchivedResourceTable(appBundle, baseModule, archivedManifest);
ResourceTable archivedResourceTable =
getArchivedResourceTable(
appBundle, baseModule, archivedManifest, customAppStorePackageName);
Path archivedClassesDexFile = getArchivedClassesDexFile();

return ModuleSplit.forArchive(
Expand Down Expand Up @@ -90,22 +97,33 @@ private void validateRequest(AppBundle appBundle) {
}
}

private Optional<ResourceTable> getArchivedResourceTable(
AppBundle appBundle, BundleModule bundleModule, AndroidManifest archivedManifest)
private ResourceTable getArchivedResourceTable(
AppBundle appBundle,
BundleModule bundleModule,
AndroidManifest archivedManifest,
Optional<String> customAppStorePackageName)
throws IOException {
if (!bundleModule.getResourceTable().isPresent()) {
return Optional.empty();
}

ResourceTable.Builder archivedResourceTable = ResourceTable.newBuilder();
if (bundleModule.getResourceTable().isPresent()) {
ImmutableSet<ResourceId> referredResources =
new ResourceAnalyzer(appBundle).findAllAppResourcesReachableFromManifest(archivedManifest);
ResourceTable archivedResourceTable =
ResourcesUtils.filterResourceTable(
bundleModule.getResourceTable().get(),
/* removeEntryPredicate= */ entry -> !referredResources.contains(entry.getResourceId()),
/* configValuesFilterFn= */ ResourceTableEntry::getEntry);
archivedResourceTable =
ResourcesUtils.filterResourceTable(
bundleModule.getResourceTable().get(),
/* removeEntryPredicate= */ entry ->
!referredResources.contains(entry.getResourceId()),
/* configValuesFilterFn= */ ResourceTableEntry::getEntry)
.toBuilder();
}
ResourceInjector resourceInjector =
new ResourceInjector(archivedResourceTable, appBundle.getPackageName());
resourceInjector.addStringResource(
APP_STORE_PACKAGE_NAME_RESOURCE_NAME, getAppStorePackageName(customAppStorePackageName));
return resourceInjector.build();
}

return Optional.of(archivedResourceTable);
private static String getAppStorePackageName(Optional<String> customAppStorePackageName) {
return customAppStorePackageName.orElse(PLAY_STORE_PACKAGE_NAME);
}

private Path getArchivedClassesDexFile() throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
package com.android.tools.build.bundletool.commands;

import static com.android.tools.build.bundletool.transparency.CodeTransparencyCryptoUtils.getX509Certificate;
import static com.android.tools.build.bundletool.transparency.CodeTransparencyCryptoUtils.getX509Certificates;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.joining;
import static org.jose4j.jws.AlgorithmIdentifiers.RSA_USING_SHA256;
Expand All @@ -40,6 +40,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Ascii;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.io.BaseEncoding;
import com.google.common.io.ByteSource;
import com.google.common.io.CharSource;
Expand All @@ -53,6 +54,7 @@
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.List;
import java.util.Optional;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
Expand Down Expand Up @@ -127,14 +129,15 @@ final String getLowerCaseName() {

public abstract Optional<SignerConfig> getSignerConfig();

public abstract Optional<X509Certificate> getTransparencyKeyCertificate();
public abstract ImmutableList<X509Certificate> getTransparencyKeyCertificates();

public abstract Optional<Path> getTransparencySignaturePath();

public static AddTransparencyCommand.Builder builder() {
return new AutoValue_AddTransparencyCommand.Builder()
.setMode(Mode.DEFAULT)
.setDexMergingChoice(DexMergingChoice.ASK_IN_CONSOLE);
.setDexMergingChoice(DexMergingChoice.ASK_IN_CONSOLE)
.setTransparencyKeyCertificates(ImmutableList.of());
}

/** Builder for the {@link AddTransparencyCommand}. */
Expand All @@ -157,8 +160,8 @@ public abstract static class Builder {
public abstract Builder setSignerConfig(SignerConfig signerConfig);

/** Sets the public key certificate of the code transparency key. */
public abstract Builder setTransparencyKeyCertificate(
X509Certificate transparencyKeyCertificate);
public abstract Builder setTransparencyKeyCertificates(
List<X509Certificate> transparencyKeyCertificates);

/** Sets path to the file containing code transparency signature. */
public abstract Builder setTransparencySignaturePath(Path transparencySignaturePath);
Expand Down Expand Up @@ -211,8 +214,8 @@ private static AddTransparencyCommand fromFlagsInGenerateGenerateCodeTransparenc
.setMode(Mode.GENERATE_CODE_TRANSPARENCY_FILE)
.setBundlePath(BUNDLE_LOCATION_FLAG.getRequiredValue(flags))
.setOutputPath(OUTPUT_FLAG.getRequiredValue(flags))
.setTransparencyKeyCertificate(
getX509Certificate(
.setTransparencyKeyCertificates(
getX509Certificates(
TRANSPARENCY_KEY_CERTIFICATE_LOCATION_FLAG.getRequiredValue(flags)));
flags.checkNoUnknownFlags();
return addTransparencyCommandBuilder.build();
Expand All @@ -226,8 +229,8 @@ private static AddTransparencyCommand fromFlagsInInjectSignatureMode(ParsedFlags
.setOutputPath(OUTPUT_FLAG.getRequiredValue(flags))
.setTransparencySignaturePath(
TRANSPARENCY_SIGNATURE_LOCATION_FLAG.getRequiredValue(flags))
.setTransparencyKeyCertificate(
getX509Certificate(
.setTransparencyKeyCertificates(
getX509Certificates(
TRANSPARENCY_KEY_CERTIFICATE_LOCATION_FLAG.getRequiredValue(flags)));
flags.checkNoUnknownFlags();
return addTransparencyCommandBuilder.build();
Expand Down Expand Up @@ -312,8 +315,7 @@ private void executeDefaultMode(AppBundle inputBundle) throws IOException, JoseE
.addFile(
BundleMetadata.BUNDLETOOL_NAMESPACE,
BundleMetadata.TRANSPARENCY_SIGNED_FILE_NAME,
toBytes(
createSignedJwt(jsonText, getSignerConfig().get().getCertificates().get(0))))
toBytes(createSignedJwt(jsonText, getSignerConfig().get().getCertificates())))
.build());
new AppBundleSerializer().writeToDisk(bundleBuilder.build(), getOutputPath());
}
Expand All @@ -326,7 +328,7 @@ private void executeGenerateCodeTransparencyFileMode(AppBundle inputBundle) thro
getOutputPath(),
toBytes(
createJwtWithoutSignature(
codeTransparencyMetadata, getTransparencyKeyCertificate().get()))
codeTransparencyMetadata, getTransparencyKeyCertificates()))
.read());
}

Expand All @@ -337,7 +339,7 @@ private void executeInjectSignatureMode(AppBundle inputBundle) throws IOExceptio
String codeTransparencyMetadata =
toJsonText(CodeTransparencyFactory.createCodeTransparencyMetadata(inputBundle));
String transparencyFileWithoutSignature =
createJwtWithoutSignature(codeTransparencyMetadata, getTransparencyKeyCertificate().get());
createJwtWithoutSignature(codeTransparencyMetadata, getTransparencyKeyCertificates());
AppBundle bundleWithTransparency =
inputBundle.toBuilder()
.setBundleMetadata(
Expand Down Expand Up @@ -486,22 +488,24 @@ public static CommandHelp help() {
.build();
}

private String createSignedJwt(String payload, X509Certificate certificate) throws JoseException {
JsonWebSignature jws = createJwsCommon(payload, certificate);
private String createSignedJwt(String payload, List<X509Certificate> certificates)
throws JoseException {
JsonWebSignature jws = createJwsCommon(payload, certificates);
jws.setKey(getSignerConfig().get().getPrivateKey());
return jws.getCompactSerialization();
}

@VisibleForTesting
static String createJwtWithoutSignature(String payload, X509Certificate certificate) {
JsonWebSignature jws = createJwsCommon(payload, certificate);
static String createJwtWithoutSignature(String payload, List<X509Certificate> certificates) {
JsonWebSignature jws = createJwsCommon(payload, certificates);
return jws.getHeaders().getEncodedHeader() + "." + jws.getEncodedPayload();
}

private static JsonWebSignature createJwsCommon(String payload, X509Certificate certificate) {
private static JsonWebSignature createJwsCommon(
String payload, List<X509Certificate> certificates) {
JsonWebSignature jws = new JsonWebSignature();
jws.setAlgorithmHeaderValue(RSA_USING_SHA256);
jws.setCertificateChainHeaderValue(certificate);
jws.setCertificateChainHeaderValue(certificates.toArray(new X509Certificate[0]));
jws.setPayload(payload);
return jws;
}
Expand Down Expand Up @@ -550,13 +554,14 @@ private void validateInjectSignatureModeInputs() {

private void validateTransparencyKeyCertificate() {
Preconditions.checkArgument(
getTransparencyKeyCertificate().get().getPublicKey().getAlgorithm().equals(RsaKeyUtil.RSA),
!getTransparencyKeyCertificates().isEmpty(),
"Transparency signing key certificates must be provided.");
X509Certificate leafCertificate = getTransparencyKeyCertificates().get(0);
Preconditions.checkArgument(
leafCertificate.getPublicKey().getAlgorithm().equals(RsaKeyUtil.RSA),
"Transparency signing key must be an RSA key, but %s key was provided.",
getTransparencyKeyCertificate().get().getPublicKey().getAlgorithm());
int keyLength =
((RSAPublicKey) getTransparencyKeyCertificate().get().getPublicKey())
.getModulus()
.bitLength();
leafCertificate.getPublicKey().getAlgorithm());
int keyLength = ((RSAPublicKey) leafCertificate.getPublicKey()).getModulus().bitLength();
Preconditions.checkArgument(
keyLength >= MIN_RSA_KEY_LENGTH,
"Minimum required key length is %s bits, but %s bit key was provided.",
Expand Down
Loading

0 comments on commit 791d6cc

Please sign in to comment.