/**
  * Remove a package from cache. If it was installed, the package is uninstalled then removed.
  *
  * @param pkgId Package ID or Name
  * @return The removed LocalPackage or null if failed
  */
 public LocalPackage pkgRemove(String pkgId) {
   CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_REMOVE);
   cmdInfo.param = pkgId;
   try {
     LocalPackage pkg = service.getPackage(pkgId);
     if (pkg == null) {
       // Check whether this is the name of a local package
       String realPkgId = getLocalPackageIdFromName(pkgId);
       if (realPkgId != null) {
         pkgId = realPkgId;
         pkg = service.getPackage(realPkgId);
       }
     }
     if (pkg == null) {
       throw new PackageException("Package not found: " + pkgId);
     }
     if (pkg.getState() == PackageState.STARTED.getValue()
         || pkg.getState() == PackageState.INSTALLED.getValue()) {
       pkgUninstall(pkgId);
       // Refresh state
       pkg = service.getPackage(pkgId);
     }
     if (pkg.getState() != PackageState.DOWNLOADED.getValue()) {
       throw new PackageException(
           "Can only remove packages in DOWNLOADED, INSTALLED or STARTED state");
     }
     log.info("Removing " + pkgId);
     service.removePackage(pkgId);
     newPackageInfo(cmdInfo, pkg).state = PackageState.REMOTE;
     return pkg;
   } catch (Exception e) {
     log.error("Failed to remove package: " + pkgId, e);
     cmdInfo.exitCode = 1;
     return null;
   }
 }
 @SuppressWarnings("unused")
 protected boolean downloadPackages(List<String> packagesToDownload) {
   if (packagesToDownload == null) {
     return true;
   }
   List<String> packagesAlreadyDownloaded = new ArrayList<String>();
   for (String pkg : packagesToDownload) {
     try {
       if (getLocalPackage(pkg) != null) {
         log.info(String.format("Package %s is already in local cache", pkg));
         packagesAlreadyDownloaded.add(pkg);
       }
     } catch (PackageException e) {
       log.error(
           String.format("Looking for package %s in local cache raised an error. Aborting.", pkg),
           e);
       return false;
     }
   }
   packagesToDownload.removeAll(packagesAlreadyDownloaded);
   if (packagesToDownload.isEmpty()) {
     return true;
   }
   List<DownloadingPackage> pkgs = new ArrayList<DownloadingPackage>();
   // Queue downloads
   log.info("Downloading " + packagesToDownload + "...");
   for (String pkg : packagesToDownload) {
     try {
       pkgs.add(getPackageManager().download(pkg));
     } catch (Exception e) {
       log.error("Cannot download packages", e);
       return false;
     }
   }
   // Check progress
   boolean downloadOk = true;
   long startTime = new Date().getTime();
   long deltaTime = 0;
   do {
     List<DownloadingPackage> pkgsCompleted = new ArrayList<DownloadingPackage>();
     for (DownloadingPackage pkg : pkgs) {
       if (pkg.isCompleted()) {
         pkgsCompleted.add(pkg);
         CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_DOWNLOAD);
         cmdInfo.param = pkg.getId();
         // Digest check not correctly implemented
         if (false && !pkg.isDigestOk()) {
           downloadOk = false;
           cmdInfo.exitCode = 1;
           cmdInfo.newMessage(
               SimpleLog.LOG_LEVEL_ERROR, "Wrong digest for package " + pkg.getName());
         } else if (pkg.getState() == PackageState.DOWNLOADED.getValue()) {
           cmdInfo.newMessage(SimpleLog.LOG_LEVEL_DEBUG, "Downloaded " + pkg);
         } else {
           downloadOk = false;
           cmdInfo.exitCode = 1;
           cmdInfo.newMessage(
               SimpleLog.LOG_LEVEL_ERROR,
               String.format("Download failed for %s. %s", pkg, pkg.getErrorMessage()));
         }
       }
     }
     pkgs.removeAll(pkgsCompleted);
     deltaTime = (new Date().getTime() - startTime) / 1000;
   } while (deltaTime < PACKAGES_DOWNLOAD_TIMEOUT_SECONDS && pkgs.size() > 0);
   // Timeout (not everything get downloaded)?
   if (pkgs.size() > 0) {
     downloadOk = false;
     log.error("Timeout while trying to download packages");
     for (DownloadingPackage pkg : pkgs) {
       CommandInfo cmdInfo = cset.newCommandInfo(CommandInfo.CMD_ADD);
       cmdInfo.param = pkg.getId();
       cmdInfo.exitCode = 1;
       cmdInfo.newMessage(SimpleLog.LOG_LEVEL_ERROR, "Download timeout for " + pkg);
     }
   }
   return downloadOk;
 }