Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keeping java methods called from Android JNI

I'm trying to obfuscate an Android app code via Proguard. After processing with proguard the app is working by itself, however native calls made from c to java are failing with java.lang.NoSuchMethodError.

This code is from the native part, where a call is made to the java class instance, named EngineStarted:

void callEngineStarted( JNIEnv* env, bool isStreamOne )
{
    jclass cls;
    if(isStreamOne == true) {
        cls = ( *env )->GetObjectClass( env, currentObjectOne );
    } else {
        cls = ( *env )->GetObjectClass( env, currentObjectTwo );
    }

    jmethodID midCallBack = ( *env )->GetMethodID( env, cls, "EngineStarted", "(I)V" );
    if (0 == midCallBack) {
        LOGW("Could not find EngineStarted method in class");
        return;
    }

    if(isStreamOne == true) {
        ( *env )->CallVoidMethod( env, currentObjectOne, midCallBack, 1 );
    } else {
        ( *env )->CallVoidMethod( env, currentObjectTwo, midCallBack, 0 );
    }
}

The java has this method. It is only called from the native part, and not anywhere else. Because of that, proguard is removing the method.

  public void EngineStarted ( int isStreamOne )
  {
    Log.v( "decoderService", "PDecoder - Engine started, using stream " + ( isStreamOne == 1 ? "one" : "two" ) );
    this.isStreamOne = isStreamOne == 1;

    // Initialize the player
    InitializePlayer( isStreamOne );
  }

I've tried adding this to proguard-project.txt, but did not solve the problem.

-keep class com.emrahgunduz.AppBase.Services.PlayService.players.pDecoders.PDecoderNative {
    void EngineStarted( int );
    void PositionChanged( int );
    void SetDuration( int );
    void Completed();
    void CompletedWithFade();
    void Spectrum ( *** );
}

After compiling, mapping.txt does not include the methods, I suspect proguard removes them. How can I keep these methods getting removed and/or renamed?

EDIT / SOLUTION:

I was able to solve the problem by changing full location with a wildcard. This saved some of the methods but was not enough. Don't know why but one other method (void InitializePlayer(int)) that was called by the dumped ones were also being dumped which somehow created a chain reaction. Adding this method solved the remaining missing methods. The final solution became

-keepclassmembers class **.PDecoderNative {
    native <methods>;
    void InitializePlayer(int);
    void EngineStarted(int);
    void PositionChanged(int);
    void SetDuration(int);
    void Completed();
    void CompletedWithFade();
    void Spectrum(float[]);
}

EDIT: The problem was not with the proguard, but with proguard not being able to read the project.txt file from time to time. Moved the whole project to a new location on disk and re-created the file. It is working perfectly.

like image 592
emrahgunduz Avatar asked Jul 23 '14 12:07

emrahgunduz


1 Answers

Your analysis is correct and your configuration looks correct too. You should double-check the fully-qualified name of your class (com.emrahgunduz.AppBase.Services.PlayService.players.pDecoders.PDecoderNative). Note that you have to use '$' instead of '.' to separate inner classes, if applicable.

If you have specified the correct names, you'll see them in the file proguard/seeds.txt that ProGuard writes out in the Android build process.

Once this works, you can replace -keep by -keepclassmembers. ProGuard will then still keep the method names but obfuscate the class name, which is fine in this case.

like image 181
Eric Lafortune Avatar answered Nov 04 '22 01:11

Eric Lafortune