Search in sources :

Example 1 with XmlProtoElement

use of com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoElement in project bundletool by google.

the class ManifestDeliveryElementTest method moduleConditions_missingNameOfFeature_throws.

@Test
public void moduleConditions_missingNameOfFeature_throws() throws Exception {
    // Name attribute doesn't use distribution namespace.
    XmlProtoElement badCondition = XmlProtoElementBuilder.create(DISTRIBUTION_NAMESPACE_URI, "device-feature").addAttribute(XmlProtoAttributeBuilder.create("name").setValueAsString("android.hardware.camera.ar")).build();
    Optional<ManifestDeliveryElement> manifestDeliveryElement = ManifestDeliveryElement.fromManifestRootNode(createAndroidManifestWithConditions(badCondition), /* isFastFollowAllowed= */
    false);
    assertThat(manifestDeliveryElement).isPresent();
    Throwable exception = assertThrows(InvalidBundleException.class, () -> manifestDeliveryElement.get().getModuleConditions());
    assertThat(exception).hasMessageThat().contains("Missing required 'dist:name' attribute in the 'device-feature' condition element.");
}
Also used : XmlProtoElement(com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoElement) Test(org.junit.Test)

Example 2 with XmlProtoElement

use of com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoElement in project bundletool by google.

the class ManifestDeliveryElementTest method moduleConditions_wrongElementInsideDeviceGroupsCondition_throws.

@Test
public void moduleConditions_wrongElementInsideDeviceGroupsCondition_throws() {
    XmlProtoElement badDeviceGroupCondition = XmlProtoElementBuilder.create(DISTRIBUTION_NAMESPACE_URI, "device-groups").addChildElement(XmlProtoElementBuilder.create(DISTRIBUTION_NAMESPACE_URI, "wrong")).build();
    Optional<ManifestDeliveryElement> element = ManifestDeliveryElement.fromManifestRootNode(createAndroidManifestWithConditions(badDeviceGroupCondition), /* isFastFollowAllowed= */
    false);
    assertThat(element).isPresent();
    InvalidBundleException exception = assertThrows(InvalidBundleException.class, () -> element.get().getModuleConditions());
    assertThat(exception).hasMessageThat().contains("Expected only '<dist:device-group>' elements inside '<dist:device-groups>', but found" + " 'wrong'");
}
Also used : InvalidBundleException(com.android.tools.build.bundletool.model.exceptions.InvalidBundleException) XmlProtoElement(com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoElement) Test(org.junit.Test)

Example 3 with XmlProtoElement

use of com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoElement in project bundletool by google.

the class ManifestDeliveryElement method getModuleConditions.

/**
 * Returns all module conditions.
 *
 * <p>We support <dist:min-sdk-version>, <dist:device-feature> and <dist:user-countries>
 * conditions today. Any other conditions types are not supported and will result in {@link
 * InvalidBundleException}.
 */
@Memoized
public ModuleConditions getModuleConditions() {
    ImmutableList<XmlProtoElement> conditionElements = getModuleConditionElements();
    ImmutableMap<String, Long> conditionCounts = conditionElements.stream().collect(groupingByDeterministic(XmlProtoElement::getName, counting()));
    for (String conditionName : CONDITIONS_ALLOWED_ONLY_ONCE) {
        if (conditionCounts.getOrDefault(conditionName, 0L) > 1) {
            throw InvalidBundleException.builder().withUserMessage("Multiple '<dist:%s>' conditions are not supported.", conditionName).build();
        }
    }
    ModuleConditions.Builder moduleConditions = ModuleConditions.builder();
    for (XmlProtoElement conditionElement : conditionElements) {
        if (!conditionElement.getNamespaceUri().equals(DISTRIBUTION_NAMESPACE_URI)) {
            throw InvalidBundleException.builder().withUserMessage("Invalid namespace found in the module condition element. " + "Expected '%s'; found '%s'.", DISTRIBUTION_NAMESPACE_URI, conditionElement.getNamespaceUri()).build();
        }
        switch(conditionElement.getName()) {
            case CONDITION_DEVICE_FEATURE_NAME:
                moduleConditions.addDeviceFeatureCondition(parseDeviceFeatureCondition(conditionElement));
                break;
            case CONDITION_MIN_SDK_VERSION_NAME:
                moduleConditions.setMinSdkVersion(parseMinSdkVersionCondition(conditionElement));
                break;
            case CONDITION_MAX_SDK_VERSION_NAME:
                moduleConditions.setMaxSdkVersion(parseMaxSdkVersionCondition(conditionElement));
                break;
            case CONDITION_USER_COUNTRIES_NAME:
                moduleConditions.setUserCountriesCondition(parseUserCountriesCondition(conditionElement));
                break;
            case CONDITION_DEVICE_GROUPS_NAME:
                moduleConditions.setDeviceGroupsCondition(parseDeviceGroupsCondition(conditionElement));
                break;
            default:
                throw InvalidBundleException.builder().withUserMessage("Unrecognized module condition: '%s'", conditionElement.getName()).build();
        }
    }
    ModuleConditions processedModuleConditions = moduleConditions.build();
    if (processedModuleConditions.getMinSdkVersion().isPresent() && processedModuleConditions.getMaxSdkVersion().isPresent()) {
        if (processedModuleConditions.getMinSdkVersion().get() > processedModuleConditions.getMaxSdkVersion().get()) {
            throw InvalidBundleException.builder().withUserMessage("Illegal SDK-based conditional module targeting (min SDK must be less than or" + " equal to max SD). Provided min and max values, respectively, are %s and %s", processedModuleConditions.getMinSdkVersion(), processedModuleConditions.getMaxSdkVersion()).build();
        }
    }
    return processedModuleConditions;
}
Also used : XmlProtoElement(com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoElement) Memoized(com.google.auto.value.extension.memoized.Memoized)

Example 4 with XmlProtoElement

use of com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoElement in project bundletool by google.

the class FusingAndroidManifestMerger method mergeElements.

/**
 * Merges element with the same name and type from different manifests. {@code elements} list in
 * this method contains data from feature modules first and element from the base module is the
 * last one in the list.
 *
 * <p>If element is presented in more than one feature module elements are sorted by name of
 * feature module. Example: if service with name 'myService' is defined in base module and
 * features 'a' and 'b', {@code elements} list will contain its declaration in the following
 * order: 'a' feature, 'b' feature, base.
 */
private XmlProtoElement mergeElements(ApplicationElementId elementId, List<XmlProtoElement> elements) {
    // we just take the first element from the list.
    if (mode.equals(Mode.REPLACE) || elements.size() == 1) {
        return elements.get(0);
    }
    // Remove source data from nested elements as this data is meaningless for functionality and
    // just contains information about line/column where element appeared in the original xml.
    List<XmlProtoElement> elementsNoSource = elements.stream().map(element -> element.toBuilder().removeSourceDataRecursive().build()).collect(toImmutableList());
    // For intent filters we gather all distinct filters defined in all modules.
    Set<XmlProtoElement> intentFilters = elementsNoSource.stream().flatMap(element -> element.getChildrenElements(AndroidManifest.INTENT_FILTER_ELEMENT_NAME)).collect(toImmutableSet());
    // For meta-data we group them by name and take one per each name.
    ImmutableMap<String, XmlProtoElement> metadataByName = elementsNoSource.stream().flatMap(element -> element.getChildrenElements(AndroidManifest.META_DATA_ELEMENT_NAME)).filter(meta -> meta.getAndroidAttribute(AndroidManifest.NAME_RESOURCE_ID).isPresent()).collect(toImmutableMap(meta -> meta.getAndroidAttribute(AndroidManifest.NAME_RESOURCE_ID).get().getValueAsString(), Function.identity(), (a, b) -> {
        // different modules throw conflict exception.
        if (!a.equals(b)) {
            throw CommandExecutionException.builder().withInternalMessage("Multiple meta-data entries with the same name are found inside" + " %s:%s: %s, %s", elementId.getType(), elementId.getName(), a, b).build();
        }
        return a;
    }));
    // Take element declaration from feature module and add all intent filters and meta data to it.
    XmlProtoElementBuilder builder = elementsNoSource.get(0).toBuilder();
    builder.removeChildrenElementsIf(child -> {
        if (!child.isElement()) {
            return false;
        }
        XmlProtoElementBuilder childElement = child.getElement();
        String tag = childElement.getName();
        Optional<String> name = childElement.getAndroidAttribute(AndroidManifest.NAME_RESOURCE_ID).map(XmlProtoAttributeBuilder::getValueAsString);
        return tag.equals(AndroidManifest.INTENT_FILTER_ELEMENT_NAME) || (tag.equals(AndroidManifest.META_DATA_ELEMENT_NAME) && name.map(metadataByName::containsKey).orElse(false));
    });
    intentFilters.forEach(e -> builder.addChildElement(e.toBuilder()));
    metadataByName.values().forEach(e -> builder.addChildElement(e.toBuilder()));
    return builder.build();
}
Also used : Iterables(com.google.common.collect.Iterables) BundleModuleName(com.android.tools.build.bundletool.model.BundleModuleName) AndroidManifest(com.android.tools.build.bundletool.model.AndroidManifest) XmlProtoAttributeBuilder(com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoAttributeBuilder) BASE_MODULE_NAME(com.android.tools.build.bundletool.model.BundleModuleName.BASE_MODULE_NAME) Function(java.util.function.Function) Predicates.not(com.google.common.base.Predicates.not) ImmutableList(com.google.common.collect.ImmutableList) Map(java.util.Map) ImmutableSet.toImmutableSet(com.google.common.collect.ImmutableSet.toImmutableSet) XmlProtoNodeBuilder(com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoNodeBuilder) XmlProtoElement(com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoElement) MoreCollectors.toOptional(com.google.common.collect.MoreCollectors.toOptional) XmlProtoElementBuilder(com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoElementBuilder) ImmutableSet(com.google.common.collect.ImmutableSet) ImmutableMap(com.google.common.collect.ImmutableMap) CommandExecutionException(com.android.tools.build.bundletool.model.exceptions.CommandExecutionException) ImmutableList.toImmutableList(com.google.common.collect.ImmutableList.toImmutableList) Set(java.util.Set) SetMultimap(com.google.common.collect.SetMultimap) Sets(com.google.common.collect.Sets) Streams.stream(com.google.common.collect.Streams.stream) ImmutableMap.toImmutableMap(com.google.common.collect.ImmutableMap.toImmutableMap) List(java.util.List) ImmutableListMultimap(com.google.common.collect.ImmutableListMultimap) AutoValue(com.google.auto.value.AutoValue) Optional(java.util.Optional) ManifestEditor(com.android.tools.build.bundletool.model.ManifestEditor) Comparator(java.util.Comparator) XmlProtoElementBuilder(com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoElementBuilder) XmlProtoElement(com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoElement) XmlProtoAttributeBuilder(com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoAttributeBuilder)

Example 5 with XmlProtoElement

use of com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoElement in project bundletool by google.

the class WearApkLocator method extractWearApkName.

/**
 * Parses the XML description file for the name of the wear APK.
 *
 * <p>According to
 * https://developer.android.com/training/wearables/apps/packaging#PackageManually, it is the
 * value inside the tag <rawPathResId>.
 */
private static Optional<String> extractWearApkName(ModuleEntry wearApkDescriptionXmlEntry) {
    XmlProtoNode root;
    try (InputStream content = wearApkDescriptionXmlEntry.getContent().openStream()) {
        root = new XmlProtoNode(XmlNode.parseFrom(content));
    } catch (InvalidProtocolBufferException e) {
        throw InvalidBundleException.builder().withCause(e).withUserMessage("The wear APK description file '%s' could not be parsed.", wearApkDescriptionXmlEntry.getPath()).build();
    } catch (IOException e) {
        throw new UncheckedIOException(String.format("An unexpected error occurred while reading APK description file '%s'.", wearApkDescriptionXmlEntry.getPath()), e);
    }
    // If the Wear APK is unbundled, there is nothing to find.
    if (root.getElement().getOptionalChildElement("unbundled").isPresent()) {
        return Optional.empty();
    }
    // If 'unbundled' is not present, then 'rawPathResId' must be.
    Optional<XmlProtoElement> rawPathResId = root.getElement().getOptionalChildElement("rawPathResId");
    if (!rawPathResId.isPresent()) {
        throw InvalidBundleException.builder().withUserMessage("The wear APK description file '%s' does not contain 'unbundled' or 'rawPathResId'.", wearApkDescriptionXmlEntry.getPath()).build();
    }
    return Optional.of(rawPathResId.get().getChildText().get().getText());
}
Also used : InputStream(java.io.InputStream) InvalidProtocolBufferException(com.google.protobuf.InvalidProtocolBufferException) UncheckedIOException(java.io.UncheckedIOException) IOException(java.io.IOException) UncheckedIOException(java.io.UncheckedIOException) XmlProtoElement(com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoElement) XmlProtoNode(com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoNode)

Aggregations

XmlProtoElement (com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoElement)14 Test (org.junit.Test)7 XmlProtoNode (com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoNode)3 ImmutableList (com.google.common.collect.ImmutableList)3 ImmutableList.toImmutableList (com.google.common.collect.ImmutableList.toImmutableList)3 ImmutableSet (com.google.common.collect.ImmutableSet)3 AndroidManifest (com.android.tools.build.bundletool.model.AndroidManifest)2 BundleModuleName (com.android.tools.build.bundletool.model.BundleModuleName)2 BASE_MODULE_NAME (com.android.tools.build.bundletool.model.BundleModuleName.BASE_MODULE_NAME)2 ManifestEditor (com.android.tools.build.bundletool.model.ManifestEditor)2 CommandExecutionException (com.android.tools.build.bundletool.model.exceptions.CommandExecutionException)2 InvalidBundleException (com.android.tools.build.bundletool.model.exceptions.InvalidBundleException)2 XmlProtoAttributeBuilder (com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoAttributeBuilder)2 XmlProtoElementBuilder (com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoElementBuilder)2 XmlProtoNodeBuilder (com.android.tools.build.bundletool.model.utils.xmlproto.XmlProtoNodeBuilder)2 AutoValue (com.google.auto.value.AutoValue)2 Predicates.not (com.google.common.base.Predicates.not)2 ImmutableListMultimap (com.google.common.collect.ImmutableListMultimap)2 ImmutableMap (com.google.common.collect.ImmutableMap)2 ImmutableMap.toImmutableMap (com.google.common.collect.ImmutableMap.toImmutableMap)2