private List<AppInfo> fetchAppInfos() throws DevConsoleException {
		List<AppInfo> result = new ArrayList<AppInfo>();
		for (DeveloperConsoleAccount consoleAccount : protocol.getSessionCredentials()
				.getDeveloperConsoleAccounts()) {
			String developerId = consoleAccount.getDeveloperId();
			Logging.d(TAG, "Getting apps for " + developerId);
			String response = post(protocol.createFetchAppsUrl(developerId),
					protocol.createFetchAppInfosRequest(), developerId);

			// don't skip incomplete apps, so we can get the package list
			List<AppInfo> apps = protocol.parseAppInfosResponse(response, accountName, false);
			if (apps.isEmpty()) {
				continue;
			}

			for (AppInfo appInfo : apps) {
				appInfo.setDeveloperId(developerId);
				appInfo.setDeveloperName(consoleAccount.getName());
			}

			result.addAll(apps);
			List<String> incompletePackages = new ArrayList<String>();
			for (AppInfo app : apps) {
				if (app.isIncomplete()) {
					result.remove(app);
					incompletePackages.add(app.getPackageName());
				}
			}
			Logging.d(TAG, String.format("Found %d apps for %s", apps.size(), developerId));
			Logging.d(TAG, String.format("Incomplete packages: %d", incompletePackages.size()));

			if (incompletePackages.isEmpty()) {
				continue;
			}

			Logging.d(TAG, String.format("Got %d incomplete apps, issuing details request",
					incompletePackages.size()));
			response = post(protocol.createFetchAppsUrl(developerId),
					protocol.createFetchAppInfosRequest(incompletePackages), developerId);
			// if info is not here, not much to do, skip
			List<AppInfo> extraApps = protocol.parseAppInfosResponse(response, accountName, true);
			Logging.d(TAG, String.format("Got %d extra apps from details request", extraApps.size()));
			for (AppInfo appInfo : extraApps) {
				appInfo.setDeveloperId(developerId);
				appInfo.setDeveloperName(consoleAccount.getName());
			}
			result.addAll(extraApps);
		}

		return result;
	}