Android ClassCastException with blank type
Asked Answered
E

2

8

I am getting a strange behavior, and I guess I'm more looking for an explanation than a solution (although solution is welcome as well!).

Here's the code:

PackageManager pm = context.getPackageManager();
List<PackageInfo> pkgList = pm.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES);
if (pkgList == null)
   return null;
for (PackageInfo pkgInfo : pkgList) {
   ApplicationInfo appInfo = pkgInfo.applicationInfo;
   // do some stuff, doesn't modify pkgInfo or appInfo or pkgList
} 

And on some occasions, I am getting error logs with:

java.lang.ClassCastException: cannot be cast to android.content.pm.PackageInfo

reported for line:

for (PackageInfo pkgInfo : pkgList)

The odd part is that, normally, ClassCastException usually look like (AFAIK):

java.lang.ClassCastException: foo.bar.ClassA cannot be cast to foo.bar.ClassB

However, the error I'm seeing is showing blank for the first part.

I decided to research a bit, and read something along the lines of it might happen if the function that's returning the list internally casted the wrong object list and returned it or something. So I looked:

ApplicationPackageManager.getInstalledPackages()

@SuppressWarnings("unchecked")
@Override
public List<PackageInfo> getInstalledPackages(int flags) {
    try {
        final List<PackageInfo> packageInfos = new ArrayList<PackageInfo>();
        PackageInfo lastItem = null;
        ParceledListSlice<PackageInfo> slice;

        do {
            final String lastKey = lastItem != null ? lastItem.packageName : null;
            slice = mPM.getInstalledPackages(flags, lastKey);
            lastItem = slice.populateList(packageInfos, PackageInfo.CREATOR);
        } while (!slice.isLastSlice());

        return packageInfos;
    } catch (RemoteException e) {
        throw new RuntimeException("Package manager has died", e);
    }
}

Ok, so the list that's being returned is populated from ParceledListSlice.populateList()...

ParceledListSlice.populateList()

public T populateList(List<T> list, Creator<T> creator) {
    mParcel.setDataPosition(0);

    T item = null;
    for (int i = 0; i < mNumItems; i++) {
        item = creator.createFromParcel(mParcel);
        list.add(item);
    }

    mParcel.recycle();
    mParcel = null;

    return item;
}

So the item is being created from PackageInfo.CREATOR.createFromParcel()...

And finally the creator.createFromParcel of PackageInfo

public static final Parcelable.Creator<PackageInfo> CREATOR
        = new Parcelable.Creator<PackageInfo>() {
    public PackageInfo createFromParcel(Parcel source) {
        return new PackageInfo(source);
    }

    public PackageInfo[] newArray(int size) {
        return new PackageInfo[size];
    }
};

So everything seems to be ok. It's creating a ParceledListSlice of type PackageInfo, and so in populateList it's creating a PackageInfo item and putting it into a List of PackageInfo, which is the returned list. All the types/classes seem fine to me.

So my questions,

  1. how would the above ClassCastException happen?
  2. Why would it show "blank" type for the error message?
  3. And what would be a possible solution?

I was thinking of just getting the list as a list of Object and checking "instanceof", but I don't think that's gonna work either because it'll probably end up saying

ClassCastException: cannot be cast into java.lang.Object" or something.

Any insights and explanations on how this could happen would be greatly appreciated.

  1. Is Dalvik/JVM simply messing up?
  2. Is the memory being corrupted?

I could only come up wild guesses =)

Essieessinger answered 22/10, 2012 at 21:25 Comment(6)
What if you tried to iterate through the list manually and print out the type of each item?Graeme
@NathanVillaescusa It's a log from customer device, so I can't do that. It's not 100% reproducible either. And also, I don't think I can loop through each item and print out the type, because the moment I try to get the item, it will probably throw a ClassCastException anyway I think.Essieessinger
I was thinking of a traditional for loop where you loop to the length of pkgList. This would tell you if some of the items in the list are of class PackageInfo.Graeme
i'm guessing you mean do a for (int i = 0; i < pkgList.size(); i++) and do a pkgList.getItem(i);. I am not sure if that'll go around the problem because pkgList.getItem(i) will still be returning a PackageInfo item, and I think it'll complain about the same thing, this time about pkgList.getItem(i) line, not at the for-each line. Also, the fact that ClassCastException is not showing the type of the first object leads me to believe that it'll have the same problem (will show blank or just show the same error).Essieessinger
Either way, it's not a issue I can reproduce and the log is from customer devices so I can't really put some debug log and push it and get a log back =( Thanks for the suggestion though =) I would be happy with just understanding how such log is possible from the given code above.Essieessinger
It's a really weird problem. Too bad you can't step through it with a debugger.Graeme
M
0

If you sure you getting ClassCastException in exactly on this line

for (PackageInfo pkgInfo : pkgList)

when a reason is following - somehow pkgList contains a value of not PackageInfo class. I assume this is NULL value. What's why you can't see it in ClassCast error message, because className for NULL object is just empty string.

Now, how to solve this - use manual for loop. for each loop uses Iterator under the hood, so you don't have control over class casting on each iteration. But if you will use code like this:

for (int i = 0; i < pkgList.length; i++) {
    Object pkgInfoObj = pkgList.get(i);
    if (pkgInfoObj instanceof PackageInfo) {
        PackageInfo pkgInfo = (PackageInfo) pkgInfoObj;
        ApplicationInfo appInfo = pkgInfo.applicationInfo;
        // do some stuff, doesn't modify pkgInfo or appInfo or pkgList
    }
}

you will be able to control class casting.

Additionally you can catch ClassCastException and just skip this cycle step to the next one.

Means answered 23/10, 2012 at 11:48 Comment(4)
Thank you for your answer. I considered this at first as well. However, there's a few things that I'd like to go over. You can add null to pkgList by doing pkgList.add(null), and it won't have a problem iterating over it (unless you try to dereference the null item, but in which case it will get a NPE). And also, as you can see from the code I pasted, the populateList() function never adds a null item.Essieessinger
Taking a different approach, if you are saying that the pkgInfo is of some NULL type and has no type associated with it, I think the for loop would still not work. In "Object pkgInfoObj = pkgList.get(i);", it will probably say "ClassCastException : cannot be cast into java.lang.Object" since it has no type. Also, if it was of type object, I think I'd initally get "ClassCastException: java.lang.Object cannot be cast into android.content.pm.PackageInfo" in the original log. Thank you for your input, but I'd like to have a better understanding of what's happening =)Essieessinger
Object is a superclass of all classes in Java, so any object can be casted to type ObjectMeans
So it seems like an object of incorrect type is trying to be casted into a PackageInfo object. How that object got into the List<PackageInfo> seems be a bug on Android side like the bug report shows. And yes, if a null object of a different type was added, the error log would probably show empty string since you won't be able to run getClass() on a null object. In that case, your for-loop would work since there actually was a null object of wrong type added to the list.Essieessinger
E
0

This seems to be an issue in Android itself. I found some bug reports against Android that show similar behaviors:

Issue 26644: ClassCastException and IncompatibleClassChangeError

Issue 36560: Exception in (SensorManager.java:521)

I've also seen the IncompatibleClassChangeError that's mentioned in Issue 26644 as well. My only guess is that these seeming impossible exceptions/errors are due to some weird memory corruption or dalvik/jvm issue. It doesn't seem like there's anything wrong with the code itself. If anyone else has a better explanation, feel free to chip in =)

Essieessinger answered 23/10, 2012 at 22:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.