NullPointerException in IabHelper.queryPurchases - java

Today I discovered an application crash report for my Android app involving the following stack trace:
java.lang.NullPointerException: Attempt to invoke interface method 'android.os.Bundle com.android.vending.billing.IInAppBillingService.getPurchases(int, java.lang.String, java.lang.String, java.lang.String)' on a null object reference
at com.myapp.utils.IabHelper.queryPurchases(IabHelper.java:878)
at com.myapp.utils.IabHelper.queryInventory(IabHelper.java:572)
at com.myapp.utils.IabHelper.queryInventory(IabHelper.java:545)
at com.myapp.utils.IabHelper$2.run(IabHelper.java:645)
at java.lang.Thread.run(Thread.java:818)
(line numbers are changed from the original -or what looks like to be the original, because of custom reformatting)
Normally, one would modify his own code to check for unassigned class members. The problem is that this code is copied&pasted right from Android SDK, because IabHelper is a class that Android SDK provides as a good starting point for implementing In-app Billing v3
The guilty line is the second
logDebug("Calling getPurchases with continuation token: " + continueToken);
Bundle ownedItems = mService.getPurchases(3, mContext.getPackageName(), itemType, continueToken);
It seems that the service is not connected at the time the method is invoked. This error occurred on a Nexus 5 device (as per Developer Console)
Is this a known problem with Android 5?
Is there an up-to-date version of the IAB Helper?
What can I do rather than manually editing the code to handle NPE someway?

I modified this code ...
do {
logDebug("Calling getPurchases with continuation token: " + continueToken);
Bundle ownedItems = mService.getPurchases(3, mContext.getPackageName(), itemType, continueToken);
// ...
}
To be this ...
do {
logDebug("Calling getPurchases with continuation token: " + continueToken);
if (mService == null || mContext == null) {
logError("Our service and/or our context are null. Exiting.");
return IABHELPER_UNKNOWN_ERROR;
}
Bundle ownedItems = mService.getPurchases(3, mContext.getPackageName(), itemType, continueToken);
// ...
}
This is almost certainly because the process is being run asynchronously and the activity/app has (or is being) closed when the result returns.

Related

problem of associating a data source associated with Word mail merge from a java application (OLE communication)

I'm working on a java application that interacts with Word through an OLE library (org.eclipse.swt.ole.win32) to merge documents (mail merge).
the java method which makes it possible to merge has been working for several years without any particular problem.
but recently the data source can no longer be associated with the merge document.
This problem is random (on some workstations it works and on others it doesn't, yet same system configuration)
I have no explicit error reported on the java side
Here is the method that communicates with Word:
public void mergeDocument(File model, File source) throws Exception {
OleAutomation autoMailMerge = null;
LOGGER.log(new Status(IStatus.INFO, pluginID, "Merge d un document"));
LOGGER.log(new Status(IStatus.INFO, pluginID, "fichier modele: " + model.getCanonicalPath()));
LOGGER.log(new Status(IStatus.INFO, pluginID, "fichier source: " + source.getPath()));
openDocumentReadOnly(model);
autoMailMerge = OLEHelper.getAutomationProperty(autoDocument, "MailMerge");
if ((source != null) && (source.exists()) && (!source.isDirectory())) {
OLEHelper.invoke(autoMailMerge, "OpenDataSource", source.getPath());
} else {
throw new MSWordOleInterfaceException(MSWordOleInterfaceCst.MSG_ERROR_EMPTY_SOURCE_PATH
+ ((source == null) ? "null" : source.getPath()));
}
OLEHelper.invoke(autoMailMerge, "Execute");
OleAutomation autoDocumentMerged = getActiveDocument();
closeDocument(autoDocument);
activateDocument(autoDocumentMerged);
autoDocument = autoDocumentMerged;
autoMailMerge.dispose();
}
Merging by hand from Word (associating the data source and merging) works on workstations where the java application does not work.
thanks to the OLE command I validated that it is the data source which is not passed (on a workstation which works I have a return with the name of the source, on one or it does not work the return is empty)
LOGGER.log(new Status(IStatus.INFO, pluginID, "data source name: "
+ OLEHelper.getVariantProperty(autoDataSource, "Name").getString()));
-a temporary solution has been found, by deleting the registry key related to office:
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Word\DocumentTemplateCache
but this is only a temporary solution, the problem comes back.

Can't use "Handler" approach to adding a URLStreamHandler in AWS Lambda

I'm currently trying to add a URLStreamHandler so I can handle URLs with custom protocols. This works fine when run locally. When deployed to AWS Lambda I get:
java.net.MalformedURLException: unknown protocol: baas
I'm following the "Handler" approach to registering the URLStreamHandler.
I even went as far as copying the code from URL.getURLStreamHandler(String) and added logging into my own code that is run by Lambda:
(Note: this is from the Java 8 source - I realise now that this might not be representative because AWS Lambda uses a Java 11 runtime).
URLStreamHandler handler = null;
String packagePrefixList = null;
packagePrefixList
= java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
"java.protocol.handler.pkgs",""));
if (packagePrefixList != "") {
packagePrefixList += "|";
}
// REMIND: decide whether to allow the "null" class prefix
// or not.
packagePrefixList += "sun.net.www.protocol";
LOG.debug("packagePrefixList: " + packagePrefixList);
StringTokenizer packagePrefixIter =
new StringTokenizer(packagePrefixList, "|");
while (handler == null &&
packagePrefixIter.hasMoreTokens()) {
String packagePrefix =
packagePrefixIter.nextToken().trim();
try {
String clsName = packagePrefix + "." + "baas" +
".Handler";
Class<?> cls = null;
LOG.debug("Try " + clsName);
try {
cls = Class.forName(clsName);
} catch (ClassNotFoundException e) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
if (cl != null) {
cls = cl.loadClass(clsName);
}
}
if (cls != null) {
LOG.debug("Instantiate " + clsName);
handler =
(URLStreamHandler)cls.newInstance();
}
} catch (Exception e) {
// any number of exceptions can get thrown here
LOG.debug(e);
}
}
This prints (in Cloudwatch logs):
packagePrefixList: com.elsten.bliss|sun.net.www.protocol (BaasDriver.java:94, thread main)
Try com.elsten.bliss.baas.Handler (BaasDriver.java:108, thread main)
Instantiate com.elsten.bliss.baas.Handler (BaasDriver.java:118, thread main)
com.elsten.bliss.baas.Handler constructor (Handler.java:55, thread main)
So, when run from my own code, in Lambda, it works.
However, the very next line of logging:
java.lang.IllegalArgumentException: URL is malformed: baas://folder: java.lang.RuntimeException
java.lang.RuntimeException: java.lang.IllegalArgumentException: URL is malformed: baas://folder
...
Caused by: java.net.MalformedURLException: unknown protocol: baas
at java.base/java.net.URL.<init>(Unknown Source)
at java.base/java.net.URL.<init>(Unknown Source)
at java.base/java.net.URL.<init>(Unknown Source)
So it seems odd the same code is failing when run in URL. The main difference I can think of is the parent classloader used to load URL and my code are different, and so there's some sort of class loading issue.
The SPI approach can't be used because Lambda doesn't extract META-INF folders!
Initially I thought the old URL.setURLStreamHandlerFactory(URLStreamHandlerFactory) approach was to be avoided, but it turns out this has been improved in recent Java versions, and so I have fallen back to that.
Specifically, a default fallback URLStreamHandlerFactory which is capable of handling streams to http, https, file et al is used as a fallback if the custom one provided cannot handle a stream.
This is a workaround though - it would be interesting to know why the class cannot be loaded.

Unable to call DOC_INFO_BY_NAME from a Custom WCC Java Service

I'm creating a Custom Java Component for oracle-ucm to do some processing based on passed in Excel Files. The files are keyed off of Content ID.
one of the first things the application does after reading the excel file is call DOC_INFO_BY_NAME.
In short: Why can't DOC_INFO_BY_NAME by name find dProcessingState and how do I fix it?
Full Details Below
However, when I do I get the following Error Message:
Error getting DOC_INFO for 'LOCALHOST16200000001'. Unable to retrieve information for 'LOCALHOST16200000001'. Unable to retrieve file format information. Cannot find the parameter 'dProcessingState'. [ Details ]
An error has occurred. The stack trace below shows more information.
!$Error getting DOC_INFO for 'LOCALHOST16200000001'.!csUnableToGetRevInfo2,LOCALHOST16200000001!csUnableToGetFileFormatInfo!syParameterNotFound,dProcessingState
intradoc.common.ServiceException: !csUnableToGetRevInfo2,LOCALHOST16200000001!csUnableToGetFileFormatInfo
at intradoc.server.ServiceRequestImplementor.buildServiceException(ServiceRequestImplementor.java:2176)
at intradoc.server.Service.buildServiceException(Service.java:2404)
at intradoc.server.Service.createServiceExceptionEx(Service.java:2398)
at intradoc.server.Service.createServiceException(Service.java:2393)
at intradoc.server.DocCommonHandler.getDocFormats(DocCommonHandler.java:271)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at intradoc.common.IdcMethodHolder.invokeMethod(IdcMethodHolder.java:86)
at intradoc.common.ClassHelperUtils.executeMethodReportStatus(ClassHelperUtils.java:324)
at intradoc.server.ServiceHandler.executeAction(ServiceHandler.java:79)
at intradoc.server.Service.doCodeEx(Service.java:622)
at intradoc.server.Service.doCode(Service.java:594)
at intradoc.server.ServiceRequestImplementor.doAction(ServiceRequestImplementor.java:1693)
at intradoc.server.Service.doAction(Service.java:566)
at intradoc.server.ServiceRequestImplementor.doActions(ServiceRequestImplementor.java:1483)
at intradoc.server.Service.doActions(Service.java:561)
at intradoc.server.ServiceRequestImplementor.executeActions(ServiceRequestImplementor.java:1415)
at intradoc.server.Service.executeActions(Service.java:547)
at intradoc.server.ServiceRequestImplementor.doRequestInternalEx(ServiceRequestImplementor.java:958)
at intradoc.server.ServiceRequestImplementor.executeServiceTopLevelSimple(ServiceRequestImplementor.java:1070)
at com.lowes.content.edam.massMetaDataUpdate.service.types.ServicesWrapper.executeService(ServicesWrapper.java:139)
at com.lowes.content.edam.massMetaDataUpdate.service.file.GetFileService.getDocInfo(GetFileService.java:478)
at com.lowes.content.edam.massMetaDataUpdate.service.ServiceFieldMapper.getMappings(ServiceFieldMapper.java:76)
at com.lowes.content.edam.massMetaDataUpdate.file.mapper.impl.FieldMapWorker.doInBackground(FieldMapWorker.java:107)
at com.lowes.content.edam.massMetaDataUpdate.file.mapper.impl.FieldMapWorker.doInBackground(FieldMapWorker.java:37)
at javax.swing.SwingWorker$1.call(SwingWorker.java:277)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at javax.swing.SwingWorker.run(SwingWorker.java:316)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
at java.lang.Thread.run(Thread.java:662)
Caused by: intradoc.data.DataException: !syParameterNotFound,dProcessingState
at intradoc.data.DataBinder.getEx(DataBinder.java:1258)
at intradoc.data.DataBinder.get(DataBinder.java:1057)
at intradoc.server.DocCommonHandler.getProcessingState(DocCommonHandler.java:277)
at intradoc.server.DocCommonHandler.getDocFormats(DocCommonHandler.java:184)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at intradoc.common.IdcMethodHolder.invokeMethod(IdcMethodHolder.java:87)
at intradoc.common.ClassHelperUtils.executeMethodReportStatus(ClassHelperUtils.java:324)
at intradoc.server.ServiceHandler.executeAction(ServiceHandler.java:79)
at intradoc.server.Service.doCodeEx(Service.java:622)
at intradoc.server.Service.doCode(Service.java:595)
... 20 more
I've tried multiple ways of calling the additional service.
currently I'm using:
public DataBinder executeService(DataBinder binder, Workspace workspace)
throws DataException, ServiceException
{
final String serviceName = binder.getLocal(Services.IdcService);
//check for REMOTE_USER
String username = binder.getEnvironmentValue("REMOTE_USER");
if ( null == username || "".equals(username.trim()))
{
log.warn("username not found, searching the binder.");
try { username = binder.get("dUser"); }
catch (DataException de) { log.error("Unable to get Username.", de); }
if ( null != username && !"".equals(username.trim()))
{ binder.setEnvironmentValue("REMOTE_USER", username); }
else { throw new IllegalStateException("Username required!"); }
}
//clear idcToken
binder.getLocalData().remove("idcToken");
log.info("About to run '" + serviceName + "' as '" + username +"'");
try
{
log.info("Calling service " + serviceName + ": "
+ binder.getLocalData().toString());
// Execute service
service.createRequestImplementor();
service.getRequestImplementor()
.executeServiceTopLevelSimple(binder, serviceName,
service.getUserData());
log.info("Finished calling service");
return service.getBinder();
}
catch (DataException e)
{
String msg = "Unexpected Failure executing service '"+serviceName+"'";
log.info(msg, e);
throw e; //log and duck
}
}
So why can't WCC find dProcessingState, and how do I fix it?
Your way of calling the service is fairly standard and I don't think that is the reason. My first guess is that your other code is interfering with your call to DOC_INFO_BY_NAME. If you disable all your other custom code (perhaps easier said than done) and call the service in a clean component, then I think it'll work. Second candidate is to reconsider if your custom service really should be a FileService. Not sure if that affects the result, but perhaps worth a try.
It almost seems like a bug to me.
getProcessingState() is called from getDocFormats() which is called from the DOC_INFO* service.
The code for getProcessingState() first retrieves dProcessingState.
String curProcState = this.m_binder.get("dProcessingState");
Then, it checks if this is null:
if (curProcState != null)
However, it never gets to the null check. This is because DataBinder.get() is requiring a value for any key passed in (in this case dProcessingState.)
Trying setting dProcessingState to a value in the DataBinder before calling the service.
binder.setLocal("dProcessingState", "ignore");
The problem was totally my fault. I had tried to be fancy, and extend Databinder to make it easier for me to use. Turns out my extension was faulty.
Rolling back to the default and double checking my data in the binder fixed it.

Android - how to retrieve a PID from an activity ID?

I'm looking into a way to tie a running task id into it's corresponding process ID (pid).
The context is a system service which is deployed as part of the platform that needs to monitor the current activity in the foreground, I can use the entire AOSP platform, but I cannot change it.
I have the following code in my service to get the activity stack:
private List<StackInfo> getStack() throws Exception
{
Log.i(TAG, "getStack");
List<StackInfo> stacks = null;
stacks = mAm.getAllStackInfos();
return stacks;
}
and this is the monitoring thread:
private class MonitorThread extends Thread
{
public void run()
{
while (true)
{
try
{
Log.i(TAG, "In MonitorThread");
Thread.sleep(1000);
StackInfo fore = getStack().get(0);
String name = fore.taskNames[fore.taskIds.length-1];
name = name.substring(0, name.indexOf("/"));
/* Next line is where I have the problem, I'm getting the task id (activity id) instead of the PID for the process */
Log.i(TAG, "Current FG PID: "+fore.taskIds[fore.taskIds.length-1] + " name: "+ name + "\n");
}
catch (Exception e)
{
Log.i(TAG, "---=== EXCEPTION!!! ===---");
Log.i(TAG, e.getMessage());
Log.i(TAG, "---=== END EXCEPTION!!! ===---");
}
}
}
}
These achieve the goal of getting the process name that is currently in the foreground by taking the top most activity name from the active stack and trimming the activity name from the result (e.g activity name com.example/MainActivity belongs to process com.example).
So far I have two methods of achieving this, both are far from ideal:
The bad way:
I can iterate every application in the system and look up the current activity in it's activity stack, once a match is found, I can use the application PID (inefficient, O(M) where M is the total number of tasks)
The even worst way
The task names return as com.package.xxx/activity_name where the part preceding the '/' is the process name (as far as I can tell, anyhow). Once I have that, I can call 'ps | grep com.package.xxx' and parse the output for the PID (I really don't want shell calls)
All I can find online is how to get the opposite - all activities that belong to an application.
Anyone has a better idea?
Thanks!
You can get your activity PID by:
int id= android.os.Process.myPid();
To find the PID of Others activities check this link. With ActivityManager you can find all running processes, find the desired activity and then get it's PID...

Error refreshing inventory. In-app Billing

I am setting up and testing in-app billing. I managed to purchase the android.test.purchased, and it did what it should. But now I need to consume it to continue my testing. The problem is that I can't reach the inventory.
When this is called I get the result.isFaliure() is called and I can't get the inventory.
IabHelper.QueryInventoryFinishedListener _gotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (_iabHelper == null) return;
if (result.isFailure()) {
Log.d(TAG, "Failed to query inventory: " + result);
return;
}
Log.d(TAG, "Query inventory was successful.");
Purchase premiumPurchase = inventory.getPurchase(SKU_PREMIUM);
_isPremium = (premiumPurchase != null && verifyDeveloperPayload(premiumPurchase));
Log.d(TAG, "User is " + (_isPremium ? "PREMIUM" : "NOT PREMIUM"));
update();
}
};
It logs the error message
Failed to query inventory: IabResult: Error refreshing inventory
(querying owned items). (response: -1003:Purchase signature
verification failed)
The android.test.purchased is still owned - it won't let me buy it again. My phone has network connection so it's not that.
I have NOT uploaded a signed APK to Google Play, does that matter even if I test with googles static ID's?
Solved it...
It seems there are problems with the static purchase ID's.
Here's a sollution I found in THIS thread:
If you have used the android.test.purchased then one way to get rid of the error is to do the following:-
1. Edit Security.java and change the "return false" line in the
verifyPurchase to "return true" - this is temporary, we'll be
putting it back in a minute.
2. In your QueryInventoryFinishedListener, after the "if
(result.isFailure()) {...}" lines add the following to consume and
get rid of your never ending android.test.purchased item:-
if (inventory.hasPurchase(SKU_ANDROID_TEST_PURCHASE_GOOD)) {
mHelper.consumeAsync(inventory.getPurchase(SKU_ANDROID_TEST_PURCHASE_GOOD),null);
}
3. Run your app so the consunmeAsync happens, this gets rid of the
"android.test.purchased" item on the server.
4. Remove the consumeAsync code (or comment it out). Back in the
Security.java, change the "return true" back to "return false".
I found the answer here:
"Here's a recommendation: Make sure that your Billing Key (base64EncodedPublicKey) is properly saved. That was my problem, after all that..."
base64EncodedPublicKey was from another aplication...
It was solution for me.

Categories