8

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.

emrahgunduz
  • 1,404
  • 1
  • 13
  • 26
  • It is probably due to proguard changing the method names. There is an option to omit that optimization for 1 or more methods, see the [Keep](http://proguard.sourceforge.net/index.html#manual/usage.html) options in the manual under usage > keep. – Alex Barker Jul 23 '14 at 15:38
  • You can log the changed names to mapping.txt, which is a default behaviour. There are similar items like `PDecoderNative getPrevious() -> e`, yet the methods I mentioned do not appear in the log. – emrahgunduz Jul 23 '14 at 16:13

1 Answers1

6

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.

Eric Lafortune
  • 45,150
  • 8
  • 114
  • 106
  • Thanks for the reply. I checked the name of the class, it was ok. The same name also appears in the usage.txt with dumped callback methods. I don't know why but the full usage of the class name results in the dump of the keep methods. However changing the name to **.PDecoderNative solved the problem. I'll accept your answer as the correct one as i managed to solve the problem via your name/package suggestion. – emrahgunduz Jul 24 '14 at 16:44
  • I can't imagine a reason why ProGuard would parse the class name without wild-cards incorrectly. To double-check, you could try copy/pasting it from proguard/seeds.txt into dexguard-project.txt. You should also see the kept method names in proguard/seeds.txt. – Eric Lafortune Jul 26 '14 at 00:22
  • Guess what, I started having the same problem after I changed using wildcards. Moved the project folder and re-created project.txt file from zero. Now it works without the wildcards. Possibly the file got locked or corrupted, or some security issue is not letting proguard read the file from time to time. I think the last update on sdk or macosx was messing up with me. – emrahgunduz Jul 29 '14 at 15:44