ContentDescriptor is throwing NullPointerException while instantiation - android

A lot of my users are facing a NullPointerException issue while instantiating content descriptor.
Fatal Exception: java.lang.NullPointerException: Attempt to read from field 'java.lang.String android.content.UriMatcher.mText' on a null object reference
at android.content.UriMatcher.addURI(UriMatcher.java:186)
at com.getsuperapp.chat.db.ContentDescriptor.getUriMatcher(SourceFile:25)
at com.getsuperapp.chat.db.DatabaseProvider.query(SourceFile:33)
at android.content.ContentProvider.query(ContentProvider.java:1017)
at android.content.ContentProvider$Transport.query(ContentProvider.java:238)
at android.content.ContentResolver.query(ContentResolver.java:497)
at android.content.ContentResolver.query(ContentResolver.java:439)
The set of devices and environment can be found here.
The ContentDescriptor file is
/**
* A few constants from other classes used in the file
*
* from UserTable.java
* public static final String PATH = "user_table";
* public static final int PATH_TOKEN = 10;
*/
public class ContentDescriptor {
private static UriMatcher URI_MATCHER = null;
private static Uri BASE_URI = null;
public static UriMatcher getUriMatcher(Context appContext) {
String AUTHORITY = appContext.getPackageName() + ".quickblox";
if (URI_MATCHER == null) {
URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
//The line below is throwing the exception.
URI_MATCHER.addURI(AUTHORITY, UserTable.PATH, UserTable.PATH_TOKEN);
URI_MATCHER.addURI(AUTHORITY, DialogTable.PATH, DialogTable.PATH_TOKEN);
URI_MATCHER.addURI(AUTHORITY, MessageTable.PATH, MessageTable.PATH_TOKEN);
URI_MATCHER.addURI(AUTHORITY, ChatInfoTable.PATH, ChatInfoTable.PATH_TOKEN);
}
return URI_MATCHER;
}
public static Uri getBaseUri(Context appContext) {
String AUTHORITY = appContext.getPackageName() + ".quickblox";
if (BASE_URI == null) {
BASE_URI = Uri.parse("content://" + AUTHORITY);
}
return BASE_URI;
}
}
Let me know if you need any further information. Can anyone provide me some visibility into this. I'm unable to reproduce the crash at my end.
EDIT
Guys stop brushing this into any other NPE category without reading. Its running on a lot of devices without trouble. The initialisation parameters are constants. The line on which its crashing is commented in the code too.

What are the values of your AUTHORITY, PATHs and PATH_TOKENs (all of them)? Depending on the values and the Android version the app is running on your app might fall into the bug fixed in UriMatcher. I guess this is the place you want to start digging into. Also, did you receive the bug report from the developer console or some king of bug reporting library? If yes, what Android version have the reports in common? It might help to reproduce the failure. Or get a ton of devices/emulators running on different versions and try to get it manually.

Related

What are some examples of MediaController commands?

Checking out the MediaController documentation, I noticed that there is a function called sendCommand(...), which requires three parameters:
command: String;
args: Bundle;
cb: ResultReceiver.
But examples of how to use that method are nowhere to be found.
What are the available MediaController#sendCommand(...) default commands and acceptable argument keys and values types?
For example, checking PlaybackState documentation, we can find a constant called ACTION_PLAY_FROM_MEDIA_ID which description is as follows:
Indicates this session supports the play from media id command
This leads us to think that MediaController#sendCommand(...) is able to change a MediaSession's current media by sending it the media ID. How can we do it?
It's known that Google Play Music App's MediaController shares its Media Queue through MediaController#getQueue function.
You can find Commands constants in MediaControllerCompat.
They actually are:
public static final String COMMAND_GET_EXTRA_BINDER =
"android.support.v4.media.session.command.GET_EXTRA_BINDER";
public static final String COMMAND_ADD_QUEUE_ITEM =
"android.support.v4.media.session.command.ADD_QUEUE_ITEM";
public static final String COMMAND_ADD_QUEUE_ITEM_AT =
"android.support.v4.media.session.command.ADD_QUEUE_ITEM_AT";
public static final String COMMAND_REMOVE_QUEUE_ITEM =
"android.support.v4.media.session.command.REMOVE_QUEUE_ITEM";
public static final String COMMAND_REMOVE_QUEUE_ITEM_AT =
"android.support.v4.media.session.command.REMOVE_QUEUE_ITEM_AT";
public static final String COMMAND_ARGUMENT_MEDIA_DESCRIPTION =
"android.support.v4.media.session.command.ARGUMENT_MEDIA_DESCRIPTION";
public static final String COMMAND_ARGUMENT_INDEX =
"android.support.v4.media.session.command.ARGUMENT_INDEX";
For some basic usage samples u can check out its methods, like:
#Override
public void removeQueueItem(MediaDescriptionCompat description) {
long flags = getFlags();
if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
throw new UnsupportedOperationException(
"This session doesn't support queue management operations");
}
Bundle params = new Bundle();
params.putParcelable(COMMAND_ARGUMENT_MEDIA_DESCRIPTION, description);
sendCommand(COMMAND_REMOVE_QUEUE_ITEM, params, null);
}

How to replace FileObserver in Android 10?

android.os.FileObserver requires a java.io.File to function.
But with Android 10 Google restricted access to everything but your app's private directory due to the famous "Storage Access Framework". Thus, accessing anything via java.io.File breaks and renders FileObserver useless unless you intend to use it in your app's private directory. However, I want to be notified when something is changed in a certain directory on external storage. I would also like to avoid periodically checking for changes.
I tried using ContentResolver.registerContentObserver(uri,notifyForDescendants,observer) and ran into some problems with that method:
Every Uri I have plugged in so far was accepted
It neither fails nor notifies if the Uri doesn't work
I cannot find any documentation telling me which Uris actually work
The only thing I got working to some extent is the following approach:
// works, but returns all changes to the external storage
contentResolver.registerContentObserver(MediaStore.Files.getContentUri("external"), true, contentObserver)
Unfortunately this includes all of the external storage and only returns Media Uris when changes happen - for example content://media/external/file/67226.
Is there a way to find out whether or not that Uri points to my directory?
Or is there a way to make registerContentObserver() work with a Uri in such a way that I get a notification whenever something in the folder has changed?
I also had no success trying various Uris related to DocumentsFile and external storage Uris.
I kept getting errors when even trying to use the base constructor such as the following -
No direct method <init>(Ljava/util/List;I)V in class Landroid/os/FileObserver; or its super classes (declaration of 'android.os.FileObserver' appears in /system/framework/framework.jar!classes2.dex)
From a comment on Detect file change using FileObserver on Android:
I saw that message (or something like that) when i was trying to use constructor FileObserver(File). Use of deprecated FileObserver(String) solved my problem.... Original FileObserver has bugs.
Full disclosure, I was using the Xamarin.Android API; however, the gist and the commenter I quoted were both working with Java. At any rate, indeed - tried again using the counterpart String constructor and I was finally able to make and use the observer. Grinds my gears to use a deprecated API, but apparently they're hanging onto it at least up to and including Android 12.0.0_r3... still, would much prefer the supported constructors actually work. Maybe there's some warrant here for filing an issue.
I found a way to implement FileObserver on Android 10 with ContentObserver, but it might only work with media files since it works with media content uris.
The uri for ContentResolver.registerContentObserver() should be the file's corresponding media uri (e.g. content://media/external/file/49) which is queried by file path.
fun getMediaUri(context: Context, file: File): Uri? {
val externalUri = MediaStore.Files.getContentUri("external")
context.contentResolver.query(
externalUri,
null,
"${MediaStore.Files.FileColumns.DATA} = ?",
arrayOf(file.path),
null
)?.use { cursor ->
if (cursor.moveToFirst()) {
val idIndex = cursor.getColumnIndex("_id")
val id = cursor.getLong(idIndex)
return Uri.withAppendedPath(externalUri, "$id")
}
}
return null
}
Then ContentObserver.onChange() will be triggered for every file change with uri: content://media/external/file/{id}; uri in ContentObserver.onChange(boolean selfChange, Uri uri) will always be content://media/external/file; only registered file will be with id (e.g. content://media/external/file/49?deletedata=false).
Does what FileObserver used to do when input uri's path matches registered uri.
I have a temporary solution for this issue, so let's see if this can help.
I start an infinite while loop watching for file created and file deleted (if you want file modified or file renamed you have to implement more) using DocumentFile. Below is my sample:
private static int currentFileIndirectory = 0;
private static final int FILE_CREATED = 0;
private static final int FILE_DELETED = 1;
private static DocumentFile[] onDirectoryChanged(DocumentFile[] documentFiles, int event) {
Log.d("FileUtil", "onDirectoryChanged: " + event);
if (event == FILE_CREATED) {
} else {
}
return documentFiles;
}
private static boolean didStartWatching = false;
private static void startWatchingDirectory(final DocumentFile directory) {
if (!didStartWatching) {
didStartWatching = true;
DocumentFile[] documentFiles = directory.listFiles();
if (null != documentFiles && documentFiles.length > 0) {
currentFileIndirectory = documentFiles.length;
}
new Thread(new Runnable() {
public void run() {
while (true) {
DocumentFile[] documentFiles = directory.listFiles();
if (null != documentFiles && documentFiles.length > 0) {
if (documentFiles.length != currentFileIndirectory) {
if (documentFiles.length > currentFileIndirectory) {//file created
DocumentFile[] newFiles = new DocumentFile[documentFiles.length - currentFileIndirectory];
onDirectoryChanged(newFiles, FILE_CREATED);
} else {//file Deleted
onDirectoryChanged(null, FILE_DELETED);
}
currentFileIndirectory = documentFiles.length;
}
}
}
}
}).start();
}
}

Retrofit and Slash characters in PATH

I am facing an issue with Retrofit and would like to find a suitable answer as the only way I can think of it is pretty ugly and not practical.
Retrofit PATH annotation requires a "/" in the beginning (as you can read in this code extracted from the library source:
/** Loads {#link #requestUrl}, {#link #requestUrlParamNames}, and {#link #requestQuery}. */
private void parsePath(String path) {
if (path == null || path.length() == 0 || path.charAt(0) != '/') {
throw methodError("URL path \"%s\" must start with '/'.", path);
}
The problem that I am facing is that the PATH part comes from the backend in a response object, meaning that all PATH's strings already come formatted from the backend previously in other response as follows:
Object : {
href: "/resources/login..."
}
As you can see, when including something like this, the URL gets malformed:
#GET("{/loginHref}")
void login(#EncodedPath("loginHref") String loginHref,
Callback<User> callback);
to something like "http://mybaseurl.com//resources/login" *double // in front of resources
This can definitely cause issues in some endpoints and I cannot think a really simple way to solve this issue apart from doing something like:
a) Modify my own version of retrofit to remove that / character check (this is a last resort)
b) Truncate the href before using the method from the interface (which I would like to avoid at all cost as well as would add unnecessary transformation all over the place.
c) Intercept the request and correctly form the URL in case this scenario happens (really ugly solution as well).
Any idea, suggestions?
Thanks!
I think this link will help you Path Replacement
Your new implementation will look like this.
#GET("/")
void login(Callback<User> callback);
You can supply a custom Endpoint implementation on which you can change the relative path.
public final class CustomEndpoint implements Endpoint {
private static final String BASE = "http://192.168.1.64:5050/api/";
private String url;
private String href;
public CustomEndpoint(String href){
this.href = href;
url = BASE + this.href;
}
#Override public String getName() {
return "default";
}
#Override public String getUrl() {
if (url == null) throw new IllegalStateException("relative path not set.");
return url;
}
}
Usage is as follows
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(new CustomEndPoint(object.href));
then restadapter.create........
Hope this will help you.

What are the response codes (values) returned by the Google Play server in a license response?

I want to cater for LICENSE_OLD_KEY in my android license policy.
I was going to modify the ServerManagedPolicy as it doesn't cater for this, as far as I can tell, it just seems to look for Policy.LICENSED or Policy.NOT_LICENSED in processServerResponse method:
public void processServerResponse(int response, ResponseData rawData) {
// Update retry counter
if (response != Policy.RETRY) {
setRetryCount(0);
} else {
setRetryCount(mRetryCount + 1);
}
if (response == Policy.LICENSED) {
// Update server policy data
Map<String, String> extras = decodeExtras(rawData.extra);
mLastResponse = response;
setValidityTimestamp(extras.get("VT"));
setRetryUntil(extras.get("GT"));
setMaxRetries(extras.get("GR"));
} else if (response == Policy.NOT_LICENSED) {
// Clear out stale policy data
setValidityTimestamp(DEFAULT_VALIDITY_TIMESTAMP);
setRetryUntil(DEFAULT_RETRY_UNTIL);
setMaxRetries(DEFAULT_MAX_RETRIES);
}
setLastResponse(response);
mPreferences.commit();
}
I'd like to know what the response code is for LICENSE_OLD_KEY because that doesn't exist in Policy:
public static final int LICENSED = 0x0100;
public static final int NOT_LICENSED = 0x0231;
public static final int RETRY = 0x0123;
I had a look here, but I can't find anywhere that lists the name and values.
I can see that there are a list of server response codes in LicenseValidator but they don't match up to those in Policy:
// Server response codes.
private static final int LICENSED = 0x0;
private static final int NOT_LICENSED = 0x1;
private static final int LICENSED_OLD_KEY = 0x2;
private static final int ERROR_NOT_MARKET_MANAGED = 0x3;
private static final int ERROR_SERVER_FAILURE = 0x4;
private static final int ERROR_OVER_QUOTA = 0x5;
private static final int ERROR_CONTACTING_SERVER = 0x101;
private static final int ERROR_INVALID_PACKAGE_NAME = 0x102;
private static final int ERROR_NON_MATCHING_UID = 0x103;
Giving it some thought I decided to try displaying the reason codes returned by the Google Play server on my phone, using AlertDialog's. Here is what I found:
Selecting LICENSED, in the Developer console profile, returned the number 256, as per Policy.LICENSED.
Selecting NOT_LICENSED returned the number 561, again as per Policy.NOT_LICENSED.
Finally selecting LICENSED_OLD_KEY returned the number 256, which is the same as Policy.LICENSED.
So it would seem that LICENSED_OLD_KEY is no longer used, or rather there is no distinction between LICENSED and LICENSED_OLD_KEY. Which is a bit confusing given the information that google provide in their documentation here.
Just to note, I did try uninstalling my app and selecting the different options in the developer console a few times, but it always resulted in the same answer!
The code you're looking at is only a reference implementation. It can't know how you would want to deal with a LICENSED_OLD_KEY situation in detail. The documentation suggests you might want to limit access to the current app, or to your server data from the current app, and ask the user to update and use the latest version. There's nothing much a reference implementation can provide to enable you to deal with all these situations. You can and should modify the code to treat LICENSED_OLD_KEY separately.
There's no indication for LICENSED_OLD_KEY "not being used anymore" because it's still handled as a server response in LicenseValidator.java and "OLD_KEY" refers to an older version of your app, not an older version of Google Play server handling.

Using java.util.regex in Android apps - are there issues with this?

In an Android app I have a utility class that I use to parse strings for 2 regEx's. I compile the 2 patterns in a static initializer so they only get compiled once, then activities can use the parsing methods statically.
This works fine except that the first time the class is accessed and loaded, and the static initializer compiles the pattern, the UI hangs for close to a MINUTE while it compiles the pattern! After the first time, it flies on all subsequent calls to parseString().
My regEx that I am using is rather large - 847 characters, but in a normal java webapp this is lightning fast. I am testing this so far only in the emulator with a 1.5 AVD.
Could this just be an emulator issue or is there some other reason that this pattern is taking so long to compile?
private static final String exp1 = "(insertratherlong---847character--regexhere)";
private static Pattern regex1 = null;
private static final String newLineAndTagsExp = "[<>\\s]";
private static Pattern regexNewLineAndTags = null;
static {
regex1 = Pattern.compile(exp1, Pattern.CASE_INSENSITIVE);
regexNewLineAndTags = Pattern.compile(newLineAndTagsExp);
}
public static String parseString(CharSequence inputStr) {
String replacementStr = "replaceMentText";
String resultString = "none";
try {
Matcher regexMatcher = regex1.matcher(inputStr);
try {
resultString = regexMatcher.replaceAll(replacementStr);
} catch (IllegalArgumentException ex) {
} catch (IndexOutOfBoundsException ex) {
}
} catch (PatternSyntaxException ex) {
}
return resultString;
}
please file a reproduceable test case at http://code.google.com/p/android/issues/entry and i'll have a look. note that i will need a regular expression that reproduces the problem. (our regular expressions are implemented by ICU4C, so the compilation actually happens in native code and this may end up being an ICU bug, but if you file an Android bug i'll worry about upstream.)
If you launched with debugging you can expect it to be about twice as slow as a regular launch. However a minute does seem extraordinary. Some things to suggest, i. look at the console output to see if warnings are being spat out, ii. when it is doing the compile, in the debugger press 'pause' and just see what it is doing. There are ways to get the source, but even so just looking at the call stack may reveal something.

Categories

Resources