// /* // * Copyright (C) 2016 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.fd.client; // // import com.android.annotations.NonNull; // import com.android.annotations.Nullable; // import com.android.utils.XmlUtils; // import com.google.common.base.Strings; // import com.google.common.collect.Lists; // // import org.w3c.dom.Document; // import org.w3c.dom.Element; // import org.w3c.dom.Node; // import org.w3c.dom.NodeList; // // import java.io.File; // import java.util.List; // import java.util.ListIterator; // // /** // * {@link InstantRunBuildInfo} models the build-info.xml file that is generated by an instant-run // * aware Gradle build. // */ // @SuppressWarnings({"WeakerAccess", "unused"}) // Used in studio and in integration tests. // public class InstantRunBuildInfo { // /** // * Right now Gradle plugin doesn't sort the build id's, but when it does we can rely on element // * order in the doc // */ // private static final boolean BUILDS_ARE_SORTED = false; // // private static final String ATTR_TIMESTAMP = "timestamp"; // // public static final String ATTR_API_LEVEL = "api-level"; // // private static final String ATTR_FORMAT = "format"; // // private static final String ATTR_VERIFIER_STATUS = "verifier"; // // // Note: The verifier status can be any number of values (See InstantRunVerifierStatus enum in gradle). // // Currently, the only contract between gradle and the IDE is that the value is set to COMPATIBLE if the build can be hotswapped // public static final String VALUE_VERIFIER_STATUS_COMPATIBLE = "COMPATIBLE"; // // private static final String TAG_ARTIFACT = "artifact"; // // private static final String TAG_BUILD = "build"; // // private static final String ATTR_ARTIFACT_LOCATION = "location"; // // private static final String ATTR_ARTIFACT_TYPE = "type"; // // private static final String ATTR_TOKEN = "token"; // // @NonNull // private final Element mRoot; // // @Nullable // private List<InstantRunArtifact> mArtifacts; // // public InstantRunBuildInfo(@NonNull Element root) { // mRoot = root; // } // // @NonNull // public String getTimeStamp() { // return mRoot.getAttribute(ATTR_TIMESTAMP); // } // // public long getSecretToken() { // String tokenString = mRoot.getAttribute(ATTR_TOKEN); // assert !Strings.isNullOrEmpty(tokenString) : "Application authorization token was not generated"; // return Long.parseLong(tokenString); // } // // @NonNull // public String getVerifierStatus() { // return mRoot.getAttribute(ATTR_VERIFIER_STATUS); // } // // public boolean canHotswap() { // String verifierStatus = getVerifierStatus(); // if (VALUE_VERIFIER_STATUS_COMPATIBLE.equals(verifierStatus)) { // return true; // } else if (verifierStatus.isEmpty()) { // // build-info.xml doesn't currently specify a verifier status if there is *only* a resource // // change! // List<InstantRunArtifact> artifacts = getArtifacts(); // if (artifacts.size() == 1 // && artifacts.get(0).type == InstantRunArtifactType.RESOURCES) { // return true; // } // } // // return false; // } // // /** Returns whether there were NO changes from the previous build. */ // public boolean hasNoChanges() { // return getArtifacts().isEmpty(); // } // // public int getFeatureLevel() { // The build info calls it as the API level, but we always pass the feature level to Gradle.. // String attribute = mRoot.getAttribute(ATTR_API_LEVEL); // if (attribute != null && !attribute.isEmpty()) { // try { // return Integer.parseInt(attribute); // } catch (NumberFormatException ignore) { // } // } // return -1; // unknown // } // // @NonNull // public List<InstantRunArtifact> getArtifacts() { // if (mArtifacts == null) { // List<InstantRunArtifact> artifacts = Lists.newArrayList(); // // Element oldestBuild = null; // long oldestTimeStamp = Long.MAX_VALUE; // // NodeList children = mRoot.getChildNodes(); // String currentBuildTimestamp = mRoot.getAttribute(ATTR_TIMESTAMP); // for (int i = 0, n = children.getLength(); i < n; i++) { // Node child = children.item(i); // if (child.getNodeType() == Node.ELEMENT_NODE) { // Element element = (Element) child; // String tagName = element.getTagName(); // if (!TAG_ARTIFACT.equals(tagName)) { // if (!BUILDS_ARE_SORTED && TAG_BUILD.equals(tagName)) { // currentBuildTimestamp = element.getAttribute(ATTR_TIMESTAMP); // if (!Strings.isNullOrEmpty(currentBuildTimestamp)) { // try { // long time = Long.parseLong(currentBuildTimestamp); // if (time < oldestTimeStamp) { // oldestTimeStamp = time; // oldestBuild = element; // } // } catch (NumberFormatException ignore) { // } // } // } // continue; // } // // String location = element.getAttribute(ATTR_ARTIFACT_LOCATION); // String typeAttribute = element.getAttribute(ATTR_ARTIFACT_TYPE); // InstantRunArtifactType type = InstantRunArtifactType.valueOf(typeAttribute); // artifacts.add( // new InstantRunArtifact( // type, new File(location), currentBuildTimestamp)); // } // } // // mArtifacts = artifacts; // // if (hasOneOf(InstantRunArtifactType.SPLIT_MAIN)) { // // If main has changed, we need ALL the slices. Look in the history for these. // // This is always available from the LAST build history. // if (BUILDS_ARE_SORTED) { // for (int i = children.getLength() - 1; i >= 0; i--) { // Node child = children.item(i); // if (child.getNodeType() == Node.ELEMENT_NODE) { // Element element = (Element) child; // if (!TAG_BUILD.equals(element.getTagName())) { // continue; // } // oldestBuild = element; // break; // } // } // } // // if (oldestBuild != null) { // ListIterator<InstantRunArtifact> iterator = artifacts.listIterator(); // while (iterator.hasNext()) { // InstantRunArtifact artifact = iterator.next(); // if (artifact.type == InstantRunArtifactType.SPLIT) { // iterator.remove(); // } // } // // NodeList nestedChildren = oldestBuild.getChildNodes(); // String buildTimestamp = oldestBuild.getAttribute(ATTR_TIMESTAMP); // for (int j = 0, n = nestedChildren.getLength(); j < n; j++) { // Node nestedChild = nestedChildren.item(j); // if (nestedChild.getNodeType() == Node.ELEMENT_NODE) { // Element artifactElement = (Element) nestedChild; // if (!TAG_ARTIFACT.equals(artifactElement.getTagName())) { // continue; // } // // String typeAttribute = artifactElement.getAttribute(ATTR_ARTIFACT_TYPE); // InstantRunArtifactType type = InstantRunArtifactType // .valueOf(typeAttribute); // if (type == InstantRunArtifactType.SPLIT) { // String location = artifactElement // .getAttribute(ATTR_ARTIFACT_LOCATION); // artifacts.add( // new InstantRunArtifact( // type, new File(location), buildTimestamp)); // } // } // } // } // } // } // // return mArtifacts; // } // // /** // * Returns true if the given list of artifacts contains at least one artifact of any of the // * given types // * // * @param types the types to look for // * @return true if and only if the list of artifacts contains an artifact of any of the given // * types // */ // public boolean hasOneOf(@NonNull InstantRunArtifactType... types) { // for (InstantRunArtifact artifact : getArtifacts()) { // for (InstantRunArtifactType type : types) { // if (artifact.type == type) { // return true; // } // } // } // return false; // } // // public boolean hasMainApk() { // return hasOneOf(InstantRunArtifactType.MAIN) || hasOneOf(InstantRunArtifactType.SPLIT_MAIN); // } // // @Nullable // public static InstantRunBuildInfo get(@NonNull String xml) { // Document doc = XmlUtils.parseDocumentSilently(xml, false); // if (doc == null) { // return null; // } // // return new InstantRunBuildInfo(doc.getDocumentElement()); // } // // // Keep roughly in sync with InstantRunBuildContext#CURRENT_FORMAT. // // // // See longer comment on that field for why they're separate fields rather // // than this code just referencing that field. // public boolean isCompatibleFormat() { // // Right don't accept older versions; due to bugs we want to force everyone to use the latest or no instant run at all. // // In the future we'll probably accept a range of values here. // return getFormat() == 8; // } // // public int getFormat() { // String attribute = mRoot.getAttribute(ATTR_FORMAT); // if (Strings.isNullOrEmpty(attribute)) { // return -1; // } // // try { // return Integer.parseInt(attribute); // } catch (NumberFormatException nfe) { // return -1; // } // } // }