@Override
	public FREObject call(FREContext c, final FREObject[] args) {
		
		final InAppPurchaseExtensionContext context = (InAppPurchaseExtensionContext) c;
		context.executeWithService(new Runnable() {
			@Override
			public void run() {
				InAppPurchaseExtension.logToAS("Getting products from the native store ...");
				
				// The local variables have to be final, so it can be used in the async task.
				final Activity activity = context.getActivity();
				final ArrayList<String> productsIds = FREArrayToArrayList((FREArray) args[0]);
				final IInAppBillingService iapService = context.getInAppBillingService();
				
				InAppPurchaseExtension.logToAS("Executing in background ... Activity : " + activity + " (activity package name : " + activity.getPackageName() + ") ; Service : " + iapService);
				
				// The getSkuDetails method is synchronous, so it has to be executed in an asynchronous task to avoid blocking the main thread.
				(new AsyncTask<Void, Void, Void>() {
					@Override
					protected Void doInBackground(Void... params) {

						// Converts the given data to a bundle of products IDs.
						Bundle products = new Bundle();
						products.putStringArrayList(ITEM_ID_LIST, productsIds);
						InAppPurchaseExtension.logToAS("Requesting the store for the products " + productsIds.toString());
						
						// Retrieves the products details.
						Bundle skuDetails = null;
						try {
							skuDetails = iapService.getSkuDetails(InAppPurchaseExtension.API_VERSION, activity.getPackageName(), "inapp", products);
						}
						catch(Exception e) {
							InAppPurchaseExtension.logToAS("Error while retrieving the products details : " + e.toString());
							return null;
						}
						
						if(skuDetails == null) {
							InAppPurchaseExtension.logToAS("Error while retrieving the products details : The returned products bundle is null!");
							return null;
						}
						
						InAppPurchaseExtension.logToAS("Processing the received products bundle from the store ...");
						
						
						// Parsing the received JSON if the response code is success.
						int responseCode = skuDetails.getInt(RESPONSE_CODE);
						InAppPurchaseExtension.logToAS("Response code : " + ERRORS_MESSAGES.get(responseCode));
						ArrayList<String> detailsJson;
						String finalJSON = null;
						if(responseCode == 0) {
							detailsJson = skuDetails.getStringArrayList(DETAILS_LIST);
							
							if(detailsJson == null || detailsJson.size() == 0) {
								InAppPurchaseExtension.logToAS("No products details retrieved!");
								
								if(productsIds.size() > 0)
									dispatchInvalidProducts(productsIds, context);
								
								return null;
							}
								
							int i, length = detailsJson.size();
							ArrayList<JSONObject> details = new ArrayList<JSONObject>();
							JSONObject currentObject;
							JSONObject currentJsonObject;
							
							// Number formatter used to format the localized price returned by Google to a normal double.
							NumberFormat format = NumberFormat.getInstance();
							format.setMinimumFractionDigits(2);
							Number number;
							
							for(i = 0 ; i < length ; i++) {
								try {
									currentJsonObject = new JSONObject(detailsJson.get(i));
									currentObject = new JSONObject();
									currentObject.put("id", currentJsonObject.get("productId"));
									currentObject.put("title", currentJsonObject.get("title"));
									currentObject.put("description", currentJsonObject.get("description"));
									
									// Formats the price to an amount rounded to 2 decimals.
									number = format.parse(currentJsonObject.get("price_amount_micros").toString());
									currentObject.put("price", format.parse(String.format("%.2f", number.doubleValue() / 1000000.0)).doubleValue());
									
									currentObject.put("priceCurrencyCode", currentJsonObject.get("price_currency_code"));
									currentObject.put("priceCurrencySymbol", currentJsonObject.get("price").toString().replaceAll("[0-9.,\\s]", ""));
									
									// The fully formated price to display in your app, with the currency symbol.
									currentObject.put("displayPrice", currentJsonObject.get("price").toString());
									
									details.add(currentObject);
									
									// removes the current product ID from the ids received as parameters.
									productsIds.remove(currentObject.get("id"));
								}
								catch (Exception e) {
									InAppPurchaseExtension.logToAS("Error while parsing the products JSON! " + e.toString());
									return null;
								}
							}
							
							InAppPurchaseExtension.logToAS("Processed.");
							InAppPurchaseExtension.logToAS("Found " + details.size() + " products.");
							JSONArray data = new JSONArray(details);
							finalJSON = data.toString();
							
							InAppPurchaseExtension.logToAS("Returning " + finalJSON + " to the app.");
							
							// Check if there is IDs left in productIds. If this is the case, there were invalid products in the parameters.
							if(productsIds.size() > 0) {
								dispatchInvalidProducts(productsIds, context);
							}
						}
						else {
							InAppPurchaseExtension.logToAS("Error while loading the products : " + ERRORS_MESSAGES.get(responseCode));
							return null;
						}
						
						context.dispatchStatusEventAsync(InAppPurchaseMessages.PRODUCTS_LOADED, finalJSON);
						
						return null;
					}
				}).execute();
			}
		});
		
		return null;
	}