4

I have an app which uses Google Maps (v1) and from the crash reports, I am seeing this exception from time to time:

java.lang.NoClassDefFoundError: android.security.MessageDigest
at com.google.android.maps.KeyHelper.getSignatureFingerprint(KeyHelper.java:60)
at com.google.android.maps.MapActivity.createMap(MapActivity.java:513)
at com.google.android.maps.MapActivity.onCreate(MapActivity.java:409)

I have defined

<uses-library
            android:name="com.google.android.maps"
            android:required="true" />

inside the application tag and I am extending MapActivity as well. The application works fine on most devices but there are some uncommon ones that report this exception, usually on Android 4.0.4 like Woxter Tablet PC 90BL, TAB9008GBBK and other generic names.

From what I read in Stackoverflow, it is a problem in the ROM and it can be solved by the user doing some advanced tricks but what I want is to prevent this crash, as I don't think it can be solved, I just want to inform the user (and thell him to buy a better device :) and disable maps functionality instead of crashing. But I can't find a way to handle this error or test it with the devices I have.

Also my main activity is based on MapActivity so I don't know how can I handle this exception before opening it.

Fernando Gallego
  • 4,064
  • 31
  • 50

3 Answers3

4

Disclaimer: I've not come across this error on any of my apps / devices but I solved a similar problem. May be that same technique can help you.

Given that the class is either unavailable or an exception occurrs while loading the class, why not try to force load it when your application starts ? Class.forName("android.security.MessageDigest") should load the class and you can catch the Error thrown from that call. I know its dirty, but it should work. You can declare a custom Application class on the manifest to make this check.

Class loading test

    try
    {
        Class.forName("android.security.MessageDigest");
    }
    catch (Throwable e1)
    {
        e1.printStackTrace();
        //Bad device
    }

You can also perform a litmus test and check the functionality of the class should the class loading succeed by digesting a simple String.

Functional test

    try
    {
        MessageDigest digester = MessageDigest.getInstance("MD5");
        digester.update("test".getBytes("UTF-8"));
        byte[] digest = digester.digest();
    }
    catch (Throwable e1)
    {
        e1.printStackTrace();
        // Class available but not functional
    }

If the class loading / litmus test fails, update a shared preference flag and let the user know that his device sucks :)

Community
  • 1
  • 1
Deepak Bala
  • 11,095
  • 2
  • 38
  • 49
  • This class is used internally by the Google maps library. In some versions of android the maps library uses this class and in other versions it uses another class. If I do this on the devices that use the other class and this one doesn't exist, it doesn't mean that it will fail to load – Fernando Gallego Apr 13 '13 at 14:29
  • True. According to a thread on the [google product forum](http://productforums.google.com/forum/#!category-topic/maps/google-maps-for-mobile/KinrGn9DcIE) this switch was made on HONEYCOMB. You can still use this test on <= HONEYCOMB, which is `Build.VERSION.SDK_INT <= 12`. I believe that should work. – Deepak Bala Apr 13 '13 at 14:44
  • 2
    Actually, I am seeing it on Android 4.0.4, rare devices but quite often – Fernando Gallego Apr 13 '13 at 17:32
2

Try to change the import android.security.MessageDigest to java.security.MessageDigest

by the look at this link:

What is 'android.security.MessageDigest''?

It looks that the android.security.MessageDigest was remove from Honeycomb so change it to the java one. and check this link as well:

http://productforums.google.com/forum/#!category-topic/maps/google-maps-for-mobile/KinrGn9DcIE

As been suggested there by @XGouchet:

Try downloading the latest version of the Google Maps API and rebuild your application with targetSDK set to the highest available (as of today it should be 17 / Jelly Bean).

Community
  • 1
  • 1
Emil Adz
  • 40,709
  • 36
  • 140
  • 187
  • 1
    I can't change the import as the error is inside Google Maps APIs. Also, I am already compiling against the latest SDK but it seems to be an operating system problem more than a library problem. – Fernando Gallego Apr 07 '13 at 15:31
  • Yes, it's inside the Google Maps API. I see bug reports for these devices: Lenco CoolTab-70, MIDC802 (Polariod?!) and MP1010, and only OS 4.04. So it seems to be very device and/or os version specific. – Bachi Aug 29 '13 at 17:15
1

The class android.security.MessageDigest is an abstract class (see MessageDigest API) what means that it can't be instantiated right away. So what happens is, that any time a device/app can't find an implementation of this class you will get the exception above, namely

java.lang.NoClassDefFoundError: android.security.MessageDigest

It's a good question why this happens. May be some phone vendors didn't ship their phone with the required library that actually implements this abstract class. I faced a similar issue with the TUN.ko module in the past.

Approach 1

What should help is, if you provide your own (empty) implementation of this class that "implements" the abstract classes and methods like this:

public class MessageDigestSpi extends Object {
     byte[] engineDigest() { return new byte[0]; }              

     void engineReset() { }

     void engineUpdate(byte[] input, int offset, int len) { }

}

public class MessageDigest extends MessageDigestSpi {

}

... and put those classes into the folder <src>/java/security/. So this way you provide your own implementation that is always found and might contain some code in order to inform the user or provide an alternative implementation.

So the remaining questions are: what does the app do, if the implementation is provided by the system, too and how to control that the system implementation is the first choice?

The answer: which implementation is chosen depends on the import order. Looking at Eclipse you can define the order in the project properties, Java build path, tab order and export. Be sure that you have any system libraries on top that might include the system implementation (most likely the Android libraries). This way the system searches in those libraries first. If nothing is found your classes get loaded and executed.

Approach 2

As an alternative to the implementation in an own abstract class you could of course simply instantiate the MessageDigest class, catch the NoClassDefFoundError exception and store the result for later evaluation:

import android.security.MessageDigest;

public class MessageDigestTester {

    private static Boolean messageDigestAvailable = null;

    public static Boolean isLibraryAvailable() {
        if (messageDigestAvailable == null) {
            try {
                MessageDigest digest = MessageDigest.getInstance("MD5");

                messageDigestAvailable = true;

            } catch (NoClassDefFoundError e) {
                messageDigestAvailable = false;
            }

        } 

        return messageDigestAvailable;
    }
}

Then use if (MessageDigestTester.isLibraryAvailable()) { } else { } in your code in order to encapsulate the usage of this library and to provide an alternative.

Approach two is easier to implement whereas approach one is the more sophisticated solution.

Hope this was helpful ... Cheers!

Trinimon
  • 13,839
  • 9
  • 44
  • 60
  • I can't provide an alternative as it is used inside google maps library and I don't have access – Fernando Gallego Apr 15 '13 at 12:00
  • Using approach one you should be able to provide your own imlementation. Have you tried that one? Would be easier to demonstrate, if you could provide a complete sample application. – Trinimon Apr 15 '13 at 12:19
  • I can't provide my own implementation as I don't know what the library is supposed to do and it is closed so I can't see the source code. Even if the class exists and it is instantiated, It will fail later when trying to get a value from it. – Fernando Gallego Apr 15 '13 at 12:38