private void uninstall(
     @NonNull File apkFile, @Nullable String packageName, @NonNull String deviceName)
     throws DeviceException {
   if (packageName != null) {
     logger.verbose("DeviceConnector '%s': uninstalling %s", deviceName, packageName);
     device.uninstallPackage(packageName, timeoutInMs, logger);
   } else {
     logger.verbose(
         "DeviceConnector '%s': unable to uninstall %s: unable to get package name",
         deviceName, apkFile);
   }
 }
  /**
   * Check if any error occurred during initialization. If it did, display an error message.
   *
   * @return True if an error occurred, false if we should continue.
   */
  public boolean checkIfInitFailed() {
    if (mAvdManagerInitError != null) {
      String example;
      if (SdkConstants.currentPlatform() == SdkConstants.PLATFORM_WINDOWS) {
        example = "%USERPROFILE%"; // $NON-NLS-1$
      } else {
        example = "~"; // $NON-NLS-1$
      }

      String error =
          String.format(
              "The AVD manager normally uses the user's profile directory to store "
                  + "AVD files. However it failed to find the default profile directory. "
                  + "\n"
                  + "To fix this, please set the environment variable ANDROID_SDK_HOME to "
                  + "a valid path such as \"%s\".",
              example);

      // We may not have any UI. Only display a dialog if there's a window shell available.
      if (mWindowShell != null && !mWindowShell.isDisposed()) {
        MessageDialog.openError(mWindowShell, "Android Virtual Devices Manager", error);
      } else {
        mSdkLog.error(null /* Throwable */, "%s", error); // $NON-NLS-1$
      }

      return true;
    }
    return false;
  }
 /**
  * Reloads the AVDs.
  *
  * <p>This does not notify the listeners.
  */
 public void reloadAvds() {
   // reload AVDs
   if (mAvdManager != null) {
     try {
       mAvdManager.reloadAvds(mSdkLog);
     } catch (AndroidLocationException e) {
       mSdkLog.error(e, null);
     }
   }
 }
  private List<LoadedManifestInfo> loadLibraries(
      SelectorResolver selectors, MergingReport.Builder mergingReportBuilder)
      throws MergeFailureException {

    ImmutableList.Builder<LoadedManifestInfo> loadedLibraryDocuments = ImmutableList.builder();
    for (Pair<String, File> libraryFile : mLibraryFiles) {
      mLogger.info("Loading library manifest " + libraryFile.getSecond().getPath());
      ManifestInfo manifestInfo =
          new ManifestInfo(
              libraryFile.getFirst(),
              libraryFile.getSecond(),
              XmlDocument.Type.LIBRARY,
              Optional.<String>absent());
      XmlDocument libraryDocument;
      try {
        libraryDocument =
            XmlLoader.load(
                selectors,
                mSystemPropertyResolver,
                manifestInfo.mName,
                manifestInfo.mLocation,
                XmlDocument.Type.LIBRARY,
                Optional.<String>absent() /* mainManifestPackageName */);
      } catch (Exception e) {
        throw new MergeFailureException(e);
      }
      // extract the package name...
      String libraryPackage = libraryDocument.getRootNode().getXml().getAttribute("package");
      // save it in the selector instance.
      if (!Strings.isNullOrEmpty(libraryPackage)) {
        selectors.addSelector(libraryPackage, libraryFile.getFirst());
      }

      // perform placeholder substitution, this is useful when the library is using
      // a placeholder in a key element, we however do not need to record these
      // substitutions so feed it with a fake merging report.
      MergingReport.Builder builder = new MergingReport.Builder(mergingReportBuilder.getLogger());
      builder.getActionRecorder().recordDefaultNodeAction(libraryDocument.getRootNode());
      performPlaceHolderSubstitution(manifestInfo, libraryDocument, builder);
      if (builder.hasErrors()) {
        // we log the errors but continue, in case the error is of no consequence
        // to the application consuming the library.
        builder.build().log(mLogger);
      }

      loadedLibraryDocuments.add(
          new LoadedManifestInfo(
              manifestInfo,
              Optional.fromNullable(libraryDocument.getPackageName()),
              libraryDocument));
    }
    return loadedLibraryDocuments.build();
  }
  /**
   * Lists remote packages available for install using {@link UpdaterData#updateOrInstallAll_NoGUI}.
   *
   * @param includeAll True to list and install all packages, including obsolete ones.
   * @param extendedOutput True to display more details on each package.
   */
  public void listRemotePackages_NoGUI(boolean includeAll, boolean extendedOutput) {

    List<ArchiveInfo> archives = getRemoteArchives_NoGUI(includeAll);

    mSdkLog.info("Packages available for installation or update: %1$d\n", archives.size());

    int index = 1;
    for (ArchiveInfo ai : archives) {
      Archive a = ai.getNewArchive();
      if (a != null) {
        Package p = a.getParentPackage();
        if (p != null) {
          if (extendedOutput) {
            mSdkLog.info("----------\n");
            mSdkLog.info("id: %1$d or \"%2$s\"\n", index, p.installId());
            mSdkLog.info(
                "     Type: %1$s\n",
                p.getClass()
                    .getSimpleName()
                    .replaceAll("Package", "")); // $NON-NLS-1$ //$NON-NLS-2$
            String desc = LineUtil.reformatLine("     Desc: %s\n", p.getLongDescription());
            mSdkLog.info("%s", desc); // $NON-NLS-1$
          } else {
            mSdkLog.info("%1$ 4d- %2$s\n", index, p.getShortDescription());
          }
          index++;
        }
      }
    }
  }
 /**
  * Creates the merging report file.
  *
  * @param mergingReport the merging activities report to serialize.
  */
 private void writeReport(MergingReport mergingReport) {
   FileWriter fileWriter = null;
   try {
     if (!mReportFile.get().getParentFile().exists()
         && !mReportFile.get().getParentFile().mkdirs()) {
       mLogger.warning(
           String.format(
               "Cannot create %1$s manifest merger report file,"
                   + "build will continue but merging activities "
                   + "will not be documented",
               mReportFile.get().getAbsolutePath()));
     } else {
       fileWriter = new FileWriter(mReportFile.get());
       mergingReport.getActions().log(fileWriter);
     }
   } catch (IOException e) {
     mLogger.warning(
         String.format(
             "Error '%1$s' while writing the merger report file, "
                 + "build can continue but merging activities "
                 + "will not be documented ",
             e.getMessage()));
   } finally {
     if (fileWriter != null) {
       try {
         fileWriter.close();
       } catch (IOException e) {
         mLogger.warning(
             String.format(
                 "Error '%1$s' while closing the merger report file, "
                     + "build can continue but merging activities "
                     + "will not be documented ",
                 e.getMessage()));
       }
     }
   }
 }
Example #7
0
  protected void notifyToolsNeedsToBeRestarted(int flags) {

    String msg = null;
    if ((flags & TOOLS_MSG_UPDATED_FROM_ADT) == TOOLS_MSG_UPDATED_FROM_ADT) {
      msg =
          "The Android SDK and AVD Manager that you are currently using has been updated. "
              + "Please also run Eclipse > Help > Check for Updates to see if the Android "
              + "plug-in needs to be updated.";

    } else if ((flags & TOOLS_MSG_UPDATED_FROM_SDKMAN) == TOOLS_MSG_UPDATED_FROM_SDKMAN) {
      msg =
          "The Android SDK and AVD Manager that you are currently using has been updated. "
              + "It is recommended that you now close the manager window and re-open it. "
              + "If you use Eclipse, please run Help > Check for Updates to see if the Android "
              + "plug-in needs to be updated.";
    } else if ((flags & NO_TOOLS_MSG) == NO_TOOLS_MSG) {
      return;
    }
    mSdkLog.info("%s", msg); // $NON-NLS-1$
  }
  /**
   * Initializes the {@link SdkManager} and the {@link AvdManager}. Extracted so that we can
   * override this in unit tests.
   */
  @VisibleForTesting(visibility = Visibility.PRIVATE)
  protected void initSdk() {
    setSdkManager(SdkManager.createManager(mOsSdkRoot, mSdkLog));
    try {
      mAvdManager = null;
      mAvdManager = AvdManager.getInstance(mSdkManager, mSdkLog);
    } catch (AndroidLocationException e) {
      mSdkLog.error(e, "Unable to read AVDs: " + e.getMessage()); // $NON-NLS-1$

      // Note: we used to continue here, but the thing is that
      // mAvdManager==null so nothing is really going to work as
      // expected. Let's just display an error later in checkIfInitFailed()
      // and abort right there. This step is just too early in the SWT
      // setup process to display a message box yet.

      mAvdManagerInitError = e;
    }

    // notify listeners.
    broadcastOnSdkReload();
  }
  /**
   * Tries to update all the *existing* local packages. This version is intended to run without a
   * GUI and only outputs to the current {@link ILogger}.
   *
   * @param pkgFilter A list of {@link SdkRepoConstants#NODES} or {@link Package#installId()} or
   *     package indexes to limit the packages we can update or install. A null or empty list means
   *     to update everything possible.
   * @param includeAll True to list and install all packages, including obsolete ones.
   * @param dryMode True to check what would be updated/installed but do not actually download or
   *     install anything.
   * @return A list of archives that have been installed. Can be null if nothing was done.
   */
  public List<Archive> updateOrInstallAll_NoGUI(
      Collection<String> pkgFilter, boolean includeAll, boolean dryMode) {

    List<ArchiveInfo> archives = getRemoteArchives_NoGUI(includeAll);

    // Filter the selected archives to only keep the ones matching the filter
    if (pkgFilter != null && pkgFilter.size() > 0 && archives != null && archives.size() > 0) {
      // Map filter types to an SdkRepository Package type,
      // e.g. create a map "platform" => PlatformPackage.class
      HashMap<String, Class<? extends Package>> pkgMap =
          new HashMap<String, Class<? extends Package>>();

      mapFilterToPackageClass(pkgMap, SdkRepoConstants.NODES);
      mapFilterToPackageClass(pkgMap, SdkAddonConstants.NODES);

      // Prepare a map install-id => package instance
      HashMap<String, Package> installIdMap = new HashMap<String, Package>();
      for (ArchiveInfo ai : archives) {
        Archive a = ai.getNewArchive();
        if (a != null) {
          Package p = a.getParentPackage();
          if (p != null) {
            String id = p.installId();
            if (id != null && id.length() > 0 && !installIdMap.containsKey(id)) {
              installIdMap.put(id, p);
            }
          }
        }
      }

      // Now intersect this with the pkgFilter requested by the user, in order to
      // only keep the classes that the user wants to install.
      // We also create a set with the package indices requested by the user
      // and a set of install-ids requested by the user.

      HashSet<Class<? extends Package>> userFilteredClasses =
          new HashSet<Class<? extends Package>>();
      SparseIntArray userFilteredIndices = new SparseIntArray();
      Set<String> userFilteredInstallIds = new HashSet<String>();

      for (String type : pkgFilter) {
        if (installIdMap.containsKey(type)) {
          userFilteredInstallIds.add(type);

        } else if (type.replaceAll("[0-9]+", "").length() == 0) { // $NON-NLS-1$ //$NON-NLS-2$
          // An all-digit number is a package index requested by the user.
          int index = Integer.parseInt(type);
          userFilteredIndices.put(index, index);

        } else if (pkgMap.containsKey(type)) {
          userFilteredClasses.add(pkgMap.get(type));

        } else {
          // This should not happen unless there's a mismatch in the package map.
          mSdkLog.error(null, "Ignoring unknown package filter '%1$s'", type);
        }
      }

      // we don't need the maps anymore
      pkgMap = null;
      installIdMap = null;

      // Now filter the remote archives list to keep:
      // - any package which class matches userFilteredClasses
      // - any package index which matches userFilteredIndices
      // - any package install id which matches userFilteredInstallIds

      int index = 1;
      for (Iterator<ArchiveInfo> it = archives.iterator(); it.hasNext(); ) {
        boolean keep = false;
        ArchiveInfo ai = it.next();
        Archive a = ai.getNewArchive();
        if (a != null) {
          Package p = a.getParentPackage();
          if (p != null) {
            if (userFilteredInstallIds.contains(p.installId())
                || userFilteredClasses.contains(p.getClass())
                || userFilteredIndices.get(index) > 0) {
              keep = true;
            }

            index++;
          }
        }

        if (!keep) {
          it.remove();
        }
      }

      if (archives.size() == 0) {
        mSdkLog.info(
            LineUtil.reflowLine(
                "Warning: The package filter removed all packages. There is nothing to install.\nPlease consider trying to update again without a package filter.\n"));
        return null;
      }
    }

    if (archives != null && archives.size() > 0) {
      if (dryMode) {
        mSdkLog.info("Packages selected for install:\n");
        for (ArchiveInfo ai : archives) {
          Archive a = ai.getNewArchive();
          if (a != null) {
            Package p = a.getParentPackage();
            if (p != null) {
              mSdkLog.info("- %1$s\n", p.getShortDescription());
            }
          }
        }
        mSdkLog.info("\nDry mode is on so nothing is actually being installed.\n");
      } else {
        return installArchives(archives, NO_TOOLS_MSG);
      }
    } else {
      mSdkLog.info("There is nothing to install or update.\n");
    }

    return null;
  }
  /**
   * Perform high level ordering of files merging and delegates actual merging to {@link
   * XmlDocument#merge(XmlDocument, com.android.manifmerger.MergingReport.Builder)}
   *
   * @return the merging activity report.
   * @throws MergeFailureException if the merging cannot be completed (for instance, if xml files
   *     cannot be loaded).
   */
  private MergingReport merge() throws MergeFailureException {
    // initiate a new merging report
    MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);

    SelectorResolver selectors = new SelectorResolver();
    // load all the libraries xml files up front to have a list of all possible node:selector
    // values.
    List<LoadedManifestInfo> loadedLibraryDocuments =
        loadLibraries(selectors, mergingReportBuilder);

    // load the main manifest file to do some checking along the way.
    LoadedManifestInfo loadedMainManifestInfo =
        load(
            new ManifestInfo(
                mManifestFile.getName(),
                mManifestFile,
                XmlDocument.Type.MAIN,
                Optional.<String>absent() /* mainManifestPackageName */),
            selectors,
            mergingReportBuilder);

    // first do we have a package declaration in the main manifest ?
    Optional<XmlAttribute> mainPackageAttribute =
        loadedMainManifestInfo.getXmlDocument().getPackage();
    if (!mainPackageAttribute.isPresent()) {
      mergingReportBuilder.addMessage(
          loadedMainManifestInfo.getXmlDocument().getSourceLocation(),
          0,
          0,
          MergingReport.Record.Severity.ERROR,
          String.format(
              "Main AndroidManifest.xml at %1$s manifest:package attribute " + "is not declared",
              loadedMainManifestInfo.getXmlDocument().getSourceLocation().print(true)));
      return mergingReportBuilder.build();
    }

    // check for placeholders presence.
    Map<String, Object> finalPlaceHolderValues = mPlaceHolderValues;
    if (!mPlaceHolderValues.containsKey(APPLICATION_ID)) {
      finalPlaceHolderValues =
          ImmutableMap.<String, Object>builder()
              .putAll(mPlaceHolderValues)
              .put(PACKAGE_NAME, mainPackageAttribute.get().getValue())
              .put(APPLICATION_ID, mainPackageAttribute.get().getValue())
              .build();
    }

    // perform system property injection
    performSystemPropertiesInjection(mergingReportBuilder, loadedMainManifestInfo.getXmlDocument());

    // force the re-parsing of the xml as elements may have been added through system
    // property injection.
    loadedMainManifestInfo =
        new LoadedManifestInfo(
            loadedMainManifestInfo,
            loadedMainManifestInfo.getOriginalPackageName(),
            loadedMainManifestInfo.getXmlDocument().reparse());

    // invariant : xmlDocumentOptional holds the higher priority document and we try to
    // merge in lower priority documents.
    Optional<XmlDocument> xmlDocumentOptional = Optional.absent();
    for (File inputFile : mFlavorsAndBuildTypeFiles) {
      mLogger.info("Merging flavors and build manifest %s \n", inputFile.getPath());
      LoadedManifestInfo overlayDocument =
          load(
              new ManifestInfo(
                  null,
                  inputFile,
                  XmlDocument.Type.OVERLAY,
                  Optional.of(mainPackageAttribute.get().getValue())),
              selectors,
              mergingReportBuilder);

      // check package declaration.
      Optional<XmlAttribute> packageAttribute = overlayDocument.getXmlDocument().getPackage();
      // if both files declare a package name, it should be the same.
      if (loadedMainManifestInfo.getOriginalPackageName().isPresent()
          && packageAttribute.isPresent()
          && !loadedMainManifestInfo
              .getOriginalPackageName()
              .get()
              .equals(packageAttribute.get().getValue())) {
        // no suggestion for library since this is actually forbidden to change the
        // the package name per flavor.
        String message =
            mMergeType == MergeType.APPLICATION
                ? String.format(
                    "Overlay manifest:package attribute declared at %1$s value=(%2$s)\n"
                        + "\thas a different value=(%3$s) "
                        + "declared in main manifest at %4$s\n"
                        + "\tSuggestion: remove the overlay declaration at %5$s "
                        + "\tand place it in the build.gradle:\n"
                        + "\t\tflavorName {\n"
                        + "\t\t\tapplicationId = \"%2$s\"\n"
                        + "\t\t}",
                    packageAttribute.get().printPosition(),
                    packageAttribute.get().getValue(),
                    mainPackageAttribute.get().getValue(),
                    mainPackageAttribute.get().printPosition(),
                    packageAttribute.get().getSourceLocation().print(true))
                : String.format(
                    "Overlay manifest:package attribute declared at %1$s value=(%2$s)\n"
                        + "\thas a different value=(%3$s) "
                        + "declared in main manifest at %4$s",
                    packageAttribute.get().printPosition(),
                    packageAttribute.get().getValue(),
                    mainPackageAttribute.get().getValue(),
                    mainPackageAttribute.get().printPosition());
        mergingReportBuilder.addMessage(
            overlayDocument.getXmlDocument().getSourceLocation(),
            0,
            0,
            MergingReport.Record.Severity.ERROR,
            message);
        return mergingReportBuilder.build();
      }

      overlayDocument
          .getXmlDocument()
          .getRootNode()
          .getXml()
          .setAttribute("package", mainPackageAttribute.get().getValue());
      xmlDocumentOptional = merge(xmlDocumentOptional, overlayDocument, mergingReportBuilder);

      if (!xmlDocumentOptional.isPresent()) {
        return mergingReportBuilder.build();
      }
    }

    mLogger.info("Merging main manifest %s\n", mManifestFile.getPath());
    xmlDocumentOptional = merge(xmlDocumentOptional, loadedMainManifestInfo, mergingReportBuilder);

    if (!xmlDocumentOptional.isPresent()) {
      return mergingReportBuilder.build();
    }

    // force main manifest package into resulting merged file when creating a library manifest.
    if (mMergeType == MergeType.LIBRARY) {
      // extract the package name...
      String mainManifestPackageName =
          loadedMainManifestInfo.getXmlDocument().getRootNode().getXml().getAttribute("package");
      // save it in the selector instance.
      if (!Strings.isNullOrEmpty(mainManifestPackageName)) {
        xmlDocumentOptional
            .get()
            .getRootNode()
            .getXml()
            .setAttribute("package", mainManifestPackageName);
      }
    }
    for (LoadedManifestInfo libraryDocument : loadedLibraryDocuments) {
      mLogger.info("Merging library manifest " + libraryDocument.getLocation());
      xmlDocumentOptional = merge(xmlDocumentOptional, libraryDocument, mergingReportBuilder);
      if (!xmlDocumentOptional.isPresent()) {
        return mergingReportBuilder.build();
      }
    }

    // done with proper merging phase, now we need to trim unwanted elements, placeholder
    // substitution and system properties injection.
    ElementsTrimmer.trim(xmlDocumentOptional.get(), mergingReportBuilder);
    if (mergingReportBuilder.hasErrors()) {
      return mergingReportBuilder.build();
    }

    // do one last placeholder substitution, this is useful as we don't stop the build
    // when a library failed a placeholder substitution, but the element might have
    // been overridden so the problem was transient. However, with the final document
    // ready, all placeholders values must have been provided.
    KeyBasedValueResolver<String> placeHolderValueResolver =
        new MapBasedKeyBasedValueResolver<String>(finalPlaceHolderValues);
    PlaceholderHandler placeholderHandler = new PlaceholderHandler();
    placeholderHandler.visit(
        mMergeType, xmlDocumentOptional.get(), placeHolderValueResolver, mergingReportBuilder);
    if (mergingReportBuilder.hasErrors()) {
      return mergingReportBuilder.build();
    }

    // perform system property injection.
    performSystemPropertiesInjection(mergingReportBuilder, xmlDocumentOptional.get());

    XmlDocument finalMergedDocument = xmlDocumentOptional.get();
    PostValidator.validate(finalMergedDocument, mergingReportBuilder);
    if (mergingReportBuilder.hasErrors()) {
      finalMergedDocument
          .getRootNode()
          .addMessage(
              mergingReportBuilder,
              MergingReport.Record.Severity.WARNING,
              "Post merge validation failed");
    }

    // only remove tools annotations if we are packaging an application.
    if (mOptionalFeatures.contains(Invoker.Feature.REMOVE_TOOLS_DECLARATIONS)) {
      finalMergedDocument =
          ToolsInstructionsCleaner.cleanToolsReferences(finalMergedDocument, mLogger);
    }

    if (mOptionalFeatures.contains(Invoker.Feature.EXTRACT_FQCNS)) {
      extractFcqns(finalMergedDocument);
    }

    if (finalMergedDocument != null) {
      mergingReportBuilder.setMergedDocument(finalMergedDocument);
    }

    MergingReport mergingReport = mergingReportBuilder.build();
    StdLogger stdLogger = new StdLogger(StdLogger.Level.INFO);
    mergingReport.log(stdLogger);
    stdLogger.verbose(mergingReport.getMergedDocument().get().prettyPrint());

    if (mReportFile.isPresent()) {
      writeReport(mergingReport);
    }

    return mergingReport;
  }
Example #11
0
 protected void displayInitError(String error) {
   mSdkLog.error(null /* Throwable */, "%s", error); // $NON-NLS-1$
 }
  /**
   * Reads the public.xml file in data/res/values/ for a given resource folder and builds up a map
   * of public resources.
   *
   * <p>This map is a subset of the full resource map that only contains framework resources that
   * are public.
   *
   * @param logger a logger to report issues to
   */
  public void loadPublicResources(@Nullable ILogger logger) {
    IAbstractFolder valueFolder = getResFolder().getFolder(SdkConstants.FD_RES_VALUES);
    if (valueFolder.exists() == false) {
      return;
    }

    IAbstractFile publicXmlFile = valueFolder.getFile("public.xml"); // $NON-NLS-1$
    if (publicXmlFile.exists()) {
      Reader reader = null;
      try {
        reader =
            new BufferedReader(new InputStreamReader(publicXmlFile.getContents(), Charsets.UTF_8));
        KXmlParser parser = new KXmlParser();
        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
        parser.setInput(reader);

        ResourceType lastType = null;
        String lastTypeName = "";
        while (true) {
          int event = parser.next();
          if (event == XmlPullParser.START_TAG) {
            // As of API 15 there are a number of "java-symbol" entries here
            if (!parser.getName().equals("public")) { // $NON-NLS-1$
              continue;
            }

            String name = null;
            String typeName = null;
            for (int i = 0, n = parser.getAttributeCount(); i < n; i++) {
              String attribute = parser.getAttributeName(i);

              if (attribute.equals("name")) { // $NON-NLS-1$
                name = parser.getAttributeValue(i);
                if (typeName != null) {
                  // Skip id attribute processing
                  break;
                }
              } else if (attribute.equals("type")) { // $NON-NLS-1$
                typeName = parser.getAttributeValue(i);
              }
            }

            if (name != null && typeName != null) {
              ResourceType type = null;
              if (typeName.equals(lastTypeName)) {
                type = lastType;
              } else {
                type = ResourceType.getEnum(typeName);
                lastType = type;
                lastTypeName = typeName;
              }
              if (type != null) {
                ResourceItem match = null;
                Map<String, ResourceItem> map = mResourceMap.get(type);
                if (map != null) {
                  match = map.get(name);
                }

                if (match != null) {
                  List<ResourceItem> publicList = mPublicResourceMap.get(type);
                  if (publicList == null) {
                    // Pick initial size for the list to hold the public
                    // resources. We could just use map.size() here,
                    // but they're usually much bigger; for example,
                    // in one platform version, there are 1500 drawables
                    // and 1200 strings but only 175 and 25 public ones
                    // respectively.
                    int size;
                    switch (type) {
                      case STYLE:
                        size = 500;
                        break;
                      case ATTR:
                        size = 1000;
                        break;
                      case DRAWABLE:
                        size = 200;
                        break;
                      case ID:
                        size = 50;
                        break;
                      case LAYOUT:
                      case COLOR:
                      case STRING:
                      case ANIM:
                      case INTERPOLATOR:
                        size = 30;
                        break;
                      default:
                        size = 10;
                        break;
                    }
                    publicList = new ArrayList<ResourceItem>(size);
                    mPublicResourceMap.put(type, publicList);
                  }

                  publicList.add(match);
                } else {
                  // log that there's a public resource that doesn't actually
                  // exist?
                }
              } else {
                // log that there was a reference to a typo that doesn't actually
                // exist?
              }
            }
          } else if (event == XmlPullParser.END_DOCUMENT) {
            break;
          }
        }
      } catch (Exception e) {
        if (logger != null) {
          logger.error(e, "Can't read and parse public attribute list");
        }
      } finally {
        if (reader != null) {
          try {
            reader.close();
          } catch (IOException e) {
            // Nothing to be done here - we don't care if it closed or not.
          }
        }
      }
    }

    // put unmodifiable list for all res type in the public resource map
    // this will simplify access
    for (ResourceType type : ResourceType.values()) {
      List<ResourceItem> list = mPublicResourceMap.get(type);
      if (list == null) {
        list = Collections.emptyList();
      } else {
        list = Collections.unmodifiableList(list);
      }

      // put the new list in the map
      mPublicResourceMap.put(type, list);
    }
  }
 @Override
 public void verbose(@NonNull String msgFormat, Object... args) {
   mLogger.verbose(msgFormat, args);
 }
 @Override
 public void info(@NonNull String msgFormat, Object... args) {
   mLogger.info(msgFormat, args);
 }
 @Override
 public void warning(@NonNull String msgFormat, Object... args) {
   mLogger.warning(mGradleMessageRewriter.rewriteMessages(msgFormat), args);
 }
 @Override
 public void error(@Nullable Throwable t, @Nullable String msgFormat, Object... args) {
   mLogger.error(t, mGradleMessageRewriter.rewriteMessages(Strings.nullToEmpty(msgFormat)), args);
 }
  @Override
  public Boolean call() throws Exception {
    String deviceName = device.getName();
    boolean isInstalled = false;

    CustomTestRunListener runListener =
        new CustomTestRunListener(deviceName, projectName, flavorName, logger);
    runListener.setReportDir(resultsDir);

    long time = System.currentTimeMillis();
    boolean success = false;

    String coverageFile =
        "/data/data/" + testData.getTestedApplicationId() + "/" + FILE_COVERAGE_EC;

    try {
      device.connect(timeoutInMs, logger);

      if (!testedApks.isEmpty()) {
        logger.verbose(
            "DeviceConnector '%s': installing %s", deviceName, Joiner.on(',').join(testedApks));
        if (testedApks.size() > 1 && device.getApiLevel() < 21) {
          throw new InstallException(
              "Internal error, file a bug, multi-apk applications"
                  + " require a device with API level 21+");
        }
        if (device.getApiLevel() >= 21) {
          device.installPackages(
              testedApks, ImmutableList.<String>of() /* installOptions */, timeoutInMs, logger);
        } else {
          device.installPackage(
              testedApks.get(0),
              ImmutableList.<String>of() /* installOptions */,
              timeoutInMs,
              logger);
        }
      }

      logger.verbose("DeviceConnector '%s': installing %s", deviceName, testApk);
      if (device.getApiLevel() >= 21) {
        device.installPackages(
            ImmutableList.of(testApk),
            ImmutableList.<String>of() /* installOptions */,
            timeoutInMs,
            logger);
      } else {
        device.installPackage(
            testApk, ImmutableList.<String>of() /* installOptions */, timeoutInMs, logger);
      }
      isInstalled = true;

      RemoteAndroidTestRunner runner =
          new RemoteAndroidTestRunner(
              testData.getApplicationId(), testData.getInstrumentationRunner(), device);

      for (Map.Entry<String, String> argument :
          testData.getInstrumentationRunnerArguments().entrySet()) {
        runner.addInstrumentationArg(argument.getKey(), argument.getValue());
      }

      if (testData.isTestCoverageEnabled()) {
        runner.addInstrumentationArg("coverage", "true");
        runner.addInstrumentationArg("coverageFile", coverageFile);
      }

      runner.setRunName(deviceName);
      runner.setMaxtimeToOutputResponse(timeoutInMs);

      runner.run(runListener);

      TestRunResult testRunResult = runListener.getRunResult();

      success = true;

      // for now throw an exception if no tests.
      // TODO return a status instead of allow merging of multi-variants/multi-device reports.
      if (testRunResult.getNumTests() == 0) {
        CustomTestRunListener fakeRunListener =
            new CustomTestRunListener(deviceName, projectName, flavorName, logger);
        fakeRunListener.setReportDir(resultsDir);

        // create a fake test output
        Map<String, String> emptyMetrics = Collections.emptyMap();
        TestIdentifier fakeTest =
            new TestIdentifier(device.getClass().getName(), "No tests found.");
        fakeRunListener.testStarted(fakeTest);
        fakeRunListener.testFailed(
            fakeTest,
            "No tests found. This usually means that your test classes are"
                + " not in the form that your test runner expects (e.g. don't inherit from"
                + " TestCase or lack @Test annotations).");
        fakeRunListener.testEnded(fakeTest, emptyMetrics);

        // end the run to generate the XML file.
        fakeRunListener.testRunEnded(System.currentTimeMillis() - time, emptyMetrics);
        return false;
      }

      return !testRunResult.hasFailedTests();
    } catch (Exception e) {
      Map<String, String> emptyMetrics = Collections.emptyMap();

      // create a fake test output
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      PrintWriter pw = new PrintWriter(baos, true);
      e.printStackTrace(pw);
      TestIdentifier fakeTest = new TestIdentifier(device.getClass().getName(), "runTests");
      runListener.testStarted(fakeTest);
      runListener.testFailed(fakeTest, baos.toString());
      runListener.testEnded(fakeTest, emptyMetrics);

      // end the run to generate the XML file.
      runListener.testRunEnded(System.currentTimeMillis() - time, emptyMetrics);

      // and throw
      throw e;
    } finally {
      if (isInstalled) {
        // Get the coverage if needed.
        if (success && testData.isTestCoverageEnabled()) {
          String temporaryCoverageCopy =
              "/data/local/tmp/" + testData.getTestedApplicationId() + "." + FILE_COVERAGE_EC;

          MultiLineReceiver outputReceiver =
              new MultiLineReceiver() {
                @Override
                public void processNewLines(String[] lines) {
                  for (String line : lines) {
                    logger.info(line);
                  }
                }

                @Override
                public boolean isCancelled() {
                  return false;
                }
              };

          logger.verbose(
              "DeviceConnector '%s': fetching coverage data from %s", deviceName, coverageFile);
          device.executeShellCommand(
              "run-as "
                  + testData.getTestedApplicationId()
                  + " cat "
                  + coverageFile
                  + " | cat > "
                  + temporaryCoverageCopy,
              outputReceiver,
              30,
              TimeUnit.SECONDS);
          device.pullFile(temporaryCoverageCopy, new File(coverageDir, FILE_COVERAGE_EC).getPath());
          device.executeShellCommand(
              "rm " + temporaryCoverageCopy, outputReceiver, 30, TimeUnit.SECONDS);
        }

        // uninstall the apps
        // This should really not be null, because if it was the build
        // would have broken before.
        uninstall(testApk, testData.getApplicationId(), deviceName);

        if (!testedApks.isEmpty()) {
          for (File testedApk : testedApks) {
            uninstall(testedApk, testData.getTestedApplicationId(), deviceName);
          }
        }
      }

      device.disconnect(timeoutInMs, logger);
    }
  }
Example #18
0
  /**
   * Validates that all archive licenses are accepted.
   *
   * <p>There are 2 cases: <br>
   * - When {@code acceptLicenses} is given, the licenses specified are automatically accepted and
   * all those not specified are automatically rejected. <br>
   * - When {@code acceptLicenses} is empty or null, licenses are collected and there's an input
   * prompt on StdOut to ask a yes/no question. To output, this uses the current {@link #mSdkLog}
   * which should be configured to send {@link ILogger#info(String, Object...)} directly to {@link
   * System#out}. <br>
   * Finally only accepted licenses are kept in the archive list.
   *
   * @param archives The archives to validate.
   * @param acceptLicenseIds A comma-separated list of licenses ids already approved.
   * @param numRetries The number of times the command-line will ask to accept a given license when
   *     the input doesn't match the expected y/n/yes/no answer. Use 0 for infinite. Useful for
   *     unit-tests. Once the number of retries is reached, the license is assumed as rejected.
   * @return True if there are any archives left to install.
   */
  @VisibleForTesting(visibility = Visibility.PRIVATE)
  boolean acceptLicense(List<ArchiveInfo> archives, String acceptLicenseIds, final int numRetries) {
    TreeSet<String> acceptedLids = new TreeSet<String>();
    if (acceptLicenseIds != null) {
      acceptedLids.addAll(Arrays.asList(acceptLicenseIds.split(","))); // $NON-NLS-1$
    }
    boolean automated = !acceptedLids.isEmpty();

    TreeSet<String> rejectedLids = new TreeSet<String>();
    TreeMap<String, License> lidToAccept = new TreeMap<String, License>();
    TreeMap<String, List<String>> lidPkgNames = new TreeMap<String, List<String>>();

    // Find the licenses needed. Include those already accepted.
    for (ArchiveInfo ai : archives) {
      License lic = getArchiveInfoLicense(ai);
      if (lic == null) {
        continue;
      }
      String lid = getLicenseId(lic);
      if (!acceptedLids.contains(lid)) {
        if (automated) {
          // Automatically reject those not already accepted
          rejectedLids.add(lid);
        } else {
          // Queue it to ask for it to be accepted
          lidToAccept.put(lid, lic);
          List<String> list = lidPkgNames.get(lid);
          if (list == null) {
            list = new ArrayList<String>();
            lidPkgNames.put(lid, list);
          }
          list.add(ai.getShortDescription());
        }
      }
    }

    // Ask for each license that needs to be asked manually for confirmation
    nextEntry:
    for (Map.Entry<String, License> entry : lidToAccept.entrySet()) {
      String lid = entry.getKey();
      License lic = entry.getValue();
      mSdkLog.info("-------------------------------\n");
      mSdkLog.info("License id: %1$s\n", lid);
      mSdkLog.info(
          "Used by: \n - %1$s\n", Joiner.on("\n  - ").skipNulls().join(lidPkgNames.get(lid)));
      mSdkLog.info("-------------------------------\n\n");
      mSdkLog.info("%1$s\n", lic.getLicense());

      int retries = numRetries;
      tryAgain:
      while (true) {
        try {
          mSdkLog.info("Do you accept the license '%1$s' [y/n]: ", lid);

          byte[] buffer = new byte[256];
          if (mSdkLog instanceof IReaderLogger) {
            ((IReaderLogger) mSdkLog).readLine(buffer);
          } else {
            System.in.read(buffer);
          }
          mSdkLog.info("\n");

          String reply = new String(buffer, Charsets.UTF_8);
          reply = reply.trim().toLowerCase(Locale.US);

          if ("y".equals(reply) || "yes".equals(reply)) {
            acceptedLids.add(lid);
            continue nextEntry;

          } else if ("n".equals(reply) || "no".equals(reply)) {
            break tryAgain;

          } else {
            mSdkLog.info("Unknown response '%1$s'.\n", reply);
            if (--retries == 0) {
              mSdkLog.info("Max number of retries exceeded. Rejecting '%1$s'\n", lid);
              break tryAgain;
            }
            continue tryAgain;
          }

        } catch (IOException e) {
          // Panic. Don't install anything.
          e.printStackTrace();
          return false;
        }
      }
      rejectedLids.add(lid);
    }

    // Finally remove all archive which license is rejected or not accepted.
    for (Iterator<ArchiveInfo> it = archives.iterator(); it.hasNext(); ) {
      ArchiveInfo ai = it.next();
      License lic = getArchiveInfoLicense(ai);
      if (lic == null) {
        continue;
      }
      String lid = getLicenseId(lic);
      if (rejectedLids.contains(lid) || !acceptedLids.contains(lid)) {
        mSdkLog.info(
            "Package %1$s not installed due to rejected license '%2$s'.\n",
            ai.getShortDescription(), lid);
        it.remove();
      }
    }

    return !archives.isEmpty();
  }