9

Since several weeks I am trying to find out, why does a "LGE Nexus 5 (hammerhead), 2048MB RAM, Android 5.0" device keep reporting crashes with java.lang.NoClassDefFoundError caused by ClassNotFoundException in a Google Play beta app.

My problem is to find out, which class is missing -

screenshot

The function names reported (the SlovaApplication.onCreate()) do not even have any code (because my app has 2 flavors - one for Google Play with FCM and one for Amazon with ADM).

Below is my app/build.gradle:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 27
    buildToolsVersion '27.0.3'
    defaultConfig {
        versionCode 29
        applicationId "de.slova"
        minSdkVersion 16
        targetSdkVersion 27
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }

    signingConfigs {
        debug {
            keyAlias 'AndroidDebugKey'
            storeFile file('../../../../../.android/debug.keystore')
            keyPassword 'android'
            storePassword 'android'
        }
        release {
            keyAlias 'AndroidReleaseKey'
            storeFile file('../../../conf/release.keystore')
            keyPassword System.getenv('PASSPHRASE1')
            storePassword System.getenv('PASSPHRASE1')
        }
    }

    flavorDimensions "store"
    productFlavors {
        google {
            dimension "store"
            versionName "$defaultConfig.versionCode-google"
            resConfigs "ru"
        }
        amazon {
            dimension "store"
            versionName "$defaultConfig.versionCode-amazon"
        }
    }

    buildTypes {
        release {
            signingConfig signingConfigs.release
            debuggable false
            shrinkResources true
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    splits {
        density {
            enable true
            reset()
            include "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"
        }
    }
}

dependencies {
    implementation 'com.android.support:multidex:1.0.3'
    implementation "com.android.support:appcompat-v7:$supportVersion"
    implementation "com.android.support:cardview-v7:$supportVersion"
    implementation "com.android.support:customtabs:$supportVersion"
    implementation "com.android.support:design:$supportVersion"
    implementation "com.android.support:support-compat:$supportVersion"
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    implementation 'com.squareup.okhttp3:okhttp:3.10.0'
    implementation 'com.squareup.picasso:picasso:2.5.2'
    implementation 'com.neovisionaries:nv-websocket-client:2.3'
    implementation 'com.readystatesoftware.sqliteasset:sqliteassethelper:2.0.1'
    implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3'
    implementation 'ru.ok:odnoklassniki-android-sdk:2.1.2'
    implementation 'com.vk:androidsdk:1.6.9'
    implementation 'com.facebook.android:facebook-login:4.28.0'
    googleImplementation "com.google.android.gms:play-services-auth:$firebaseVersion"
    googleImplementation "com.google.firebase:firebase-messaging:$firebaseVersion"
    googleImplementation 'com.android.billingclient:billing:1.0'
    amazonCompileOnly files('libs/amazon-device-messaging-1.0.1.jar')
    amazonImplementation files('libs/login-with-amazon-sdk.jar')
    implementation 'com.mikepenz:crossfader:1.5.2@aar'
    implementation('com.mikepenz:materialdrawer:6.0.6@aar') {
        transitive = true
    }
    androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    testImplementation 'junit:junit:4.12'
}

if (!getGradle().getStartParameter().getTaskRequests().toString().contains("Amazon")) {
    apply plugin: 'com.google.gms.google-services'
}

And I have also tried changing from Application to MultiDexApplication but that has not helped (below is the google flavor class whose 2 tasks are: update Play Services if needed and sign-in the Google user):

public class SlovaApplication extends MultiDexApplication implements Flavor, Keys {
    private static final int PLAY_SERVICES = 1972; // USED TO UPDATE PLAY SERVICES
    private static final int PLAY_LOGIN = 1979;    // USED FOR GOOGLE SIGN-IN

    @Override
    public void onCreate() {
        super.onCreate();
        MultiDex.install(this);            // HAS NOT HELPED WITH CRASHES
        FirebaseApp.initializeApp(this);
        VKSdk.initialize(this);
        Utils.init(this);
    }

    @Override
    public void onCreate(Activity activity) {
        // DO NOTHING IN google FLAVOR
    }

    @Override
    public void onResume() {
        // DO NOTHING IN google FLAVOR
    }

    @Override
    public boolean onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case PLAY_SERVICES: {
                if (resultCode != Activity.RESULT_OK) {
                    Toast.makeText(activity, getString(R.string.error_play_services), Toast.LENGTH_LONG).show();
                    activity.finish();
                }
                return true;
            }

            case PLAY_LOGIN: {
                User user = null;
                if (resultCode == Activity.RESULT_OK) {
                    try {
                        Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
                        if (task.isSuccessful()) {
                            GoogleSignInAccount account = task.getResult();
                            String photoUrl = (account.getPhotoUrl() != null ? account.getPhotoUrl().toString() : null);
                            user = new User(
                                    account.getId(),
                                    User.GOOGLE,
                                    account.getGivenName(),
                                    account.getFamilyName(),
                                    (URLUtil.isNetworkUrl(photoUrl) ? photoUrl : null)
                            );
                            DatabaseService.updateUser(activity, user);
                        }
                    } catch (Exception ex) {
                        Log.w(TAG, "Adding Google user failed", ex);
                    }
                }

                if (user == null) {
                    Toast.makeText(activity, getString(R.string.error_play_login), Toast.LENGTH_LONG).show();
                    activity.finish();
                }
                return true;
            }

            default: return false;
        }
    }

    @Override
    public boolean doesExist() {
        return Users.getInstance().doesUserExist(GOOGLE);
    }

    @Override
    public void signin(final Activity activity) {
        GoogleApiAvailability api = GoogleApiAvailability.getInstance();
        int code = api.isGooglePlayServicesAvailable(activity);
        if (code == ConnectionResult.SUCCESS) {
            GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN).build();
            GoogleSignInClient client = GoogleSignIn.getClient(activity, gso);
            Intent i = client.getSignInIntent();
            activity.startActivityForResult(i, PLAY_LOGIN);
        } else if (api.isUserResolvableError(code)) {
            api.showErrorDialogFragment(activity, code, PLAY_SERVICES, new DialogInterface.OnCancelListener() {
                @Override
                public void onCancel(DialogInterface dialog) {
                    Toast.makeText(activity, getString(R.string.error_play_services), Toast.LENGTH_LONG).show();
                    activity.finish();
                }
            });
        } else {
            Toast.makeText(activity, getString(R.string.error_play_services) + " " + api.getErrorString(code), Toast.LENGTH_LONG).show();
            activity.finish();
        }
    }

    @Override
    public void putToken(JSONObject obj, String ignore) throws JSONException {
        String token = FirebaseInstanceId.getInstance().getToken();
        obj.put(KEY_FCM, token);
    }
}

I am lost at how to fix these crashes and how to identify the supposedly missing class...

The other devices in my beta program do not crash:

other devices

UPDATE: Adding multiDexEnabled true has unfortunately not helped and here is my proguard-rules.pro:

-libraryjars libs
-dontwarn com.squareup.okhttp.**
-dontwarn com.amazon.device.messaging.**
-keep class com.amazon.device.messaging.** {*;}
-keep public class * extends com.amazon.device.messaging.ADMMessageReceiver
-keep public class * extends com.amazon.device.messaging.ADMMessageHandlerBase

-dontwarn okhttp3.**
-dontwarn okio.**
-dontwarn javax.annotation.**
-dontwarn org.conscrypt.**
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
Alexander Farber
  • 21,519
  • 75
  • 241
  • 416
  • 1
    If you have such problems why do you remove the line number info from your app? With that info you would know which line and potentially also the problematic class name. – Robert Apr 14 '18 at 12:59
  • I am not sure, what do you mean, I did not remove anything and upload mapping.txt with each my beta release – Alexander Farber Apr 14 '18 at 13:08
  • 1
    Proguard removes the debugging info which includes the source code lines. In a stack trace usually for each method the line number is denoted at the end. In your stack trace (the screenshot at the beginning) all classes that belong to your app does not have line numbers. All the other methods (from the Android runtime) do have line numbers. – Robert Apr 14 '18 at 19:00
  • Is there a way to keep the line numbers, but still to obfuscate the most of my source code? – Alexander Farber Apr 15 '18 at 08:50
  • 1
    The debug code has entries of different types, one are the line number hints. Those hints only contain the info which command was located in which line number (nothing an attacker could make use of). If you only leave this type (AFAIR the was a proguard setting to specify which debug info to strip or keep) you will see the line numbers in the stack trace. – Robert Apr 15 '18 at 10:08

2 Answers2

2

Per the documentation, you are missing one line in app/build.gradle:

defaultConfig {
    ...
    multiDexEnabled true
    ...
}
1

you are missing the line

multiDexEnabled true in App gradle file

defaultConfig{
...
multiDexEnabled true
}

Follow this link from android Docs to enable multidex in your application

https://developer.android.com/studio/build/multidex.html

Kousei
  • 1,291
  • 1
  • 9
  • 16