Velocity is Like OpenTable on Steroids

We all enjoy a night out with friends or our significant other from time to time. However, there is … more

Android Lollipop Lands for the Sony Xperia Z Ultra

The undisputed king of the beasts–at least in Sony’s current stable,is the … more

Android 5.0 Lollipop in 3D–EVO 3D, That Is!

It is that time of the year once again. Flowers bloom (or snow falls, depending on which … more

Gaming Console with Lollipop? Ouya Gets an Android TV Port

Android is a very flexible platform, and it can be used on a large variety of … more

Welcome to XDA

Search to go directly to your device's forum

Register an account

Unlock full posting privileges

Ask a question

No registration required
Post Reply

findClass() returning java.lang.Class

OP dillonr

20th August 2014, 07:44 AM   |  #1  
OP Senior Member
Flag Oshawa
Thanks Meter: 10
 
100 posts
Join Date:Joined: Apr 2012
More
When i am trying to call a method (to start the music in grooveshark) i come across an error that i believe is because XposedHelpers.findClass() is returning an unwanted class.

Here is the code that is giving the issue
Code:
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
    XposedHelpers.callMethod(XposedHelpers.findClass("com.grooveshark.android.lib.player.AsyncPlayer", lpparam.classLoader),"play", true);
}
And here is the error that it is returning
Code:
08-20 01:30:31.038    2390-2390/? I/Xposed﹕ java.lang.NoSuchMethodError: java.lang.Class#play(java.lang.Boolean)#bestmatch
            at de.robv.android.xposed.XposedHelpers.findMethodBestMatch(XposedHelpers.java:271)
            at de.robv.android.xposed.XposedHelpers.findMethodBestMatch(XposedHelpers.java:284)
            at de.robv.android.xposed.XposedHelpers.callMethod(XposedHelpers.java:947)
            ...

This error is here because callMethod should be calling
Code:
com.grooveshark.android.lib.player.AsyncPlayer#play
but instead it calls
Code:
java.lang.Class#play
because "Class" is being passed to it from findClass, not the correct class
What needs to be done to get the right class/call the right method?

Edit:

i have tried using param.thisObject, but the method i want (play) is in a different package than the onStart method (that i am hooking). The method i am hooking (onStart) is in
Code:
com.grooveshark.android.v1.activity.Home
and using param.thisObject just gives me
Code:
java.lang.NoSuchMethodError: com.grooveshark.android.v1.activity.Home#play(java.lang.Boolean)#bestmatch
as it is in the wrong package
Last edited by dillonr; 20th August 2014 at 08:57 AM.
20th August 2014, 12:15 PM   |  #2  
GermainZ's Avatar
Forum Moderator / Recognized Developer / XDA Portal Team
Thanks Meter: 7,228
 
5,874 posts
Join Date:Joined: Aug 2012
Donate to Me
More
I'm assuming play is a static method, since you wouldn't be able to call it without an instance otherwise.
If that's the case, you should use XposedHelpers.callStaticMethod.
20th August 2014, 03:59 PM   |  #3  
OP Senior Member
Flag Oshawa
Thanks Meter: 10
 
100 posts
Join Date:Joined: Apr 2012
More
Quote:
Originally Posted by GermainZ

I'm assuming play is a static method, since you wouldn't be able to call it without an instance otherwise.
If that's the case, you should use XposedHelpers.callStaticMethod.

When i changed it to
Code:
XposedHelpers.callStaticMethod(XposedHelpers.findClass("com.grooveshark.android.lib.player.AsyncPlayer", lpparam.classLoader),"play", true);
I get the error
Code:
I/Xposed﹕ java.lang.NullPointerException: expected receiver of type com.grooveshark.android.lib.player.AsyncPlayer, but got null
That being said...if i hook into a method in the same class as play, i will use the 'pause' method as an example, and call
Code:
XposedHelpers.callMethod(param.thisObject,"play", true);
then play is correctly called (which has the unfortunate effect of preventing me from stopping the music)
So i still think the issue is that i do not know how to correctly use findClass/callMethod

Here is "play"
Code:
public void play(boolean paramBoolean) {
    while (true) {
        try {
            if (this.context == null) {
                this.logger.severe("No context set! Has the player been released?");
                return;
            }
            if (this.playableSong == null) {
                this.logger.severe("playableSong not set!");
                continue;
            }
        } finally {}
        this.logger.info("playing " + getPlayableSong() + " current state: " + this.state.name() + ", successive? " + paramBoolean);
        this.isSuccessive = paramBoolean;
        this.isPaused = false;
        if (this.state != State.PAUSED) {
            State localState1 = this.state;
            State localState2 = State.PREPARED;
            if (localState1 != localState2);
        } else {
            try {
                this.mp.start();
                setState(State.PLAYING);
                startPolling();
            } catch (IllegalStateException localIllegalStateException) {
                this.mp.reset();
                setState(State.IDLE);
            }
            continue;
        }
        if (this.state == State.PREPARING) {
            this.handler.sendEmptyMessage(2);
        } else {
            setState(State.PREPARING);
            Message localMessage = this.handler.obtainMessage(0, this.context);
            this.handler.sendMessage(localMessage);
        }
    }
}
20th August 2014, 04:04 PM   |  #4  
GermainZ's Avatar
Forum Moderator / Recognized Developer / XDA Portal Team
Thanks Meter: 7,228
 
5,874 posts
Join Date:Joined: Aug 2012
Donate to Me
More
Quote:
Originally Posted by dillonr

When i changed it to

Code:
XposedHelpers.callStaticMethod(XposedHelpers.findClass("com.grooveshark.android.lib.player.AsyncPlayer", lpparam.classLoader),"play", true);
I get the error
Code:
I/Xposed﹕ java.lang.NullPointerException: expected receiver of type com.grooveshark.android.lib.player.AsyncPlayer, but got null
That being said...if i hook into a method in the same class as play, i will use the 'pause' method as an example, and call
Code:
XposedHelpers.callMethod(param.thisObject,"play", true);
then play is correctly called (which has the unfortunate effect of preventing me from stopping the music)
So i still think the issue is that i do not know how to correctly use findClass/callMethod

Here is "play"
Code:
public void play(boolean paramBoolean) {
    while (true) {
        try {
            if (this.context == null) {
                this.logger.severe("No context set! Has the player been released?");
                return;
            }
            if (this.playableSong == null) {
                this.logger.severe("playableSong not set!");
                continue;
            }
        } finally {}
        this.logger.info("playing " + getPlayableSong() + " current state: " + this.state.name() + ", successive? " + paramBoolean);
        this.isSuccessive = paramBoolean;
        this.isPaused = false;
        if (this.state != State.PAUSED) {
            State localState1 = this.state;
            State localState2 = State.PREPARED;
            if (localState1 != localState2);
        } else {
            try {
                this.mp.start();
                setState(State.PLAYING);
                startPolling();
            } catch (IllegalStateException localIllegalStateException) {
                this.mp.reset();
                setState(State.IDLE);
            }
            continue;
        }
        if (this.state == State.PREPARING) {
            this.handler.sendEmptyMessage(2);
        } else {
            setState(State.PREPARING);
            Message localMessage = this.handler.obtainMessage(0, this.context);
            this.handler.sendMessage(localMessage);
        }
    }
}

If play is a static method: use callStaticMethod.
If play is not a static method: you can't use findClass to call it, you'll need the class' instance.
20th August 2014, 04:09 PM   |  #5  
OP Senior Member
Flag Oshawa
Thanks Meter: 10
 
100 posts
Join Date:Joined: Apr 2012
More
Quote:
Originally Posted by GermainZ

If play is a static method: use callStaticMethod.
If play is not a static method: you can't use findClass to call it, you'll need the class' instance.

play isn't static...How would i get the class' instance?
20th August 2014, 04:16 PM   |  #6  
GermainZ's Avatar
Forum Moderator / Recognized Developer / XDA Portal Team
Thanks Meter: 7,228
 
5,874 posts
Join Date:Joined: Aug 2012
Donate to Me
More
Quote:
Originally Posted by dillonr

play isn't static...How would i get the class' instance?

Using getObjectField, assuming there is one. If not, you can try creating an instance (using the newInstance method, IIRC).

I'd think if this is the right way to do it, though. Although I don't know what you're trying to do, it sounds like you should be hooking a different method.
20th August 2014, 09:33 PM   |  #7  
OP Senior Member
Flag Oshawa
Thanks Meter: 10
 
100 posts
Join Date:Joined: Apr 2012
More
Quote:
Originally Posted by GermainZ

Using getObjectField, assuming there is one. If not, you can try creating an instance (using the newInstance method, IIRC).

I'd think if this is the right way to do it, though. Although I don't know what you're trying to do, it sounds like you should be hooking a different method.

Hopefully this clears it up...What i am trying to do is have grooveshark automatically play music when my phone connects to the bluetooth speakers in my car
Currently i am working out how to make the music play, but i already have the code there to check that bluetooth is connected (although untested, as i wrote it last night and haven't been out to the car to check it).
When i am finished, tasker will open grooveshark when my phone connects to the bluetooth speakers and my xposed module will check that bluetooth is connected and then start playing the music. Hooking onto the onStart method seemed to be the easiest way to get everything going when grooveshark opens

Would it be possible to see an example of getObjectField/newInstance? I promise i have tried to figure it out myself, i'm just not sure what to do, and looking at the source of XposedHelpers.java gives me an idea of what is available, but not of how to use what is available
21st August 2014, 12:38 AM   |  #8  
GermainZ's Avatar
Forum Moderator / Recognized Developer / XDA Portal Team
Thanks Meter: 7,228
 
5,874 posts
Join Date:Joined: Aug 2012
Donate to Me
More
Quote:
Originally Posted by dillonr

Hopefully this clears it up...What i am trying to do is have grooveshark automatically play music when my phone connects to the bluetooth speakers in my car
Currently i am working out how to make the music play, but i already have the code there to check that bluetooth is connected (although untested, as i wrote it last night and haven't been out to the car to check it).
When i am finished, tasker will open grooveshark when my phone connects to the bluetooth speakers and my xposed module will check that bluetooth is connected and then start playing the music. Hooking onto the onStart method seemed to be the easiest way to get everything going when grooveshark opens

Would it be possible to see an example of getObjectField/newInstance? I promise i have tried to figure it out myself, i'm just not sure what to do, and looking at the source of XposedHelpers.java gives me an idea of what is available, but not of how to use what is available

OK, here's a general post that should show you how to use various methods, along with some common pitfalls.
I'm not going to write complete code, so use this as an example but don't copy/paste.

If you're using an app to view this post, I'd recommend you open it in a web browser so you get pretty syntax highlighting and formatting.


Let's assume we have two classes, Class1 and Class2. Class1:
Code:
Select Code
class Class1 {
    Object someVariable;
    final static int SOME_FINAL_STATIC_INT = 30;
    Context context;
    Class2 class2Instance;

    method void Class1(Context context) {
        this.context = context;
        class2Instance = Class2.getInstance(context);
    }

    method void someMethod(String stringArg, int intArg) {
        for (int i = intArg; i < SOME_FINAL_STATIC_INT ; i++) {
            class2Instance.doSomething(stringArg);
        }
    }
}
Class2:
Code:
Select Code
class Class2 {
    Context context;
    Class2 instance;

    method void Class2(Context context) {
        this.context = context;
        instance = this;
    }

    static method Class2 getInstance(Context context) {
        if (instance == null)
            instance = new Class2(context);
        return instance;
    }

    method void doSomething(String stringArg) {
        // do something
    }
}

We want to change someMethod's behavior so it always goes from 0 to 10 instead of intArg to SOME_FINAL_STATIC_INT .

We know that we can modify the arguments of a method directly (using param.args[index]) and a static int field using setStaticInt, so it should be easy to do… right? Nope.
Why not? Because SOME_FINAL_STATIC_INT , being static and final, will be replaced by the compiler. In other words, this:
Code:
Select Code
        for (int i = intArg; i < SOME_FINAL_STATIC_INT ; i++) {
            class2Instance.doSomething(stringArg);
        }
Will actually become this when compiled:
Code:
Select Code
        for (int i = intArg; i < 30; i++) {
            class2Instance.doSomething(stringArg);
        }
So, how do you replace the 30 now? You don't. Depending on the situation, you'll have to use alternative ways to accomplish what you want. The most general one is to replace the method:
Code:
Select Code
findAndHookMethod("my.package.Class1", lpparam.classLoader, "someMethod", String.class, int.class, new XC_MethodReplacement() {
    @Override
    Object replaceHookedMethod(param) {
        // we want to go from 0 to 10 only
        for (int i = 0; i < 10; i++) {
            // get class2Instance
            Object class2Instance = getObjectField(param.thisObject, "class2Instance");
            // call doSomething
            callMethod(class2Instance, doSomething, param.args[0]);
        }
    }
});
And we're done. We wanted to modify the for loop, and we did. We then wanted to call a method, but since that method belongs to another class, we needed that class' instance first.

Assume we don't have an instance of the class, what do we do then? Well, you could create an instance yourself… but keep in mind this may not lead to the result you want if the application expects (and uses) only one instance.
For example, let's assume there was no class2Instance in Class1. We could create one like this:
Code:
Select Code
findAndHookMethod("my.package.Class1", lpparam.classLoader, "someMethod", String.class, int.class, new XC_MethodReplacement() {
    @Override
    Object replaceHookedMethod(param) {
        // we want to go from 0 to 10 only
        for (int i = 0; i < 10; i++) {
            // find Class2
            Class Class2 = findClass("my.package.Class2", lpparam.classLoader);
            // we need to get a context to create an instance of Class2, since its constructor needs a context
            Context context = (Context) getObjectField(param.thisObject, "context");
            // create class2Instance
            Object class2Instance = newInstance(Class2, context);
            // we can also call Class2.getInstance, which would be a better choice, although such a method is not always available.
            // Object class2Instance = callStaticMethod(Class2, "getInstance", context);
            // call doSomething
            callMethod(class2Instance, doSomething, param.args[0]);
        }
    }
});
To recap: we got the context using getObjectField, just as before.
We then got the Class for Class2 and used it to create a new instance. (We also could've called the static method, which would have given us the existing instance, if any; such a method isn't always available, though).
The rest was similar to the previous example.


I hope this explains how to use some of the methods Xposed offers clearly. Good luck.
21st August 2014, 05:33 AM   |  #9  
OP Senior Member
Flag Oshawa
Thanks Meter: 10
 
100 posts
Join Date:Joined: Apr 2012
More
I am still unable to call a method (i think that it is because the method i am calling is in a different package (.lib.player.AsyncPlayer) than the one i am hooking (.v1.activity.Home), so i can't get a context/instance.)

I think it is time that i drop this module, and spend my time elsewhere, maybe coming back to it later when i learn more
22nd August 2014, 05:38 AM   |  #10  
Junior Member
Thanks Meter: 6
 
22 posts
Join Date:Joined: Dec 2012
Quote:
Originally Posted by dillonr

I am still unable to call a method (i think that it is because the method i am calling is in a different package (.lib.player.AsyncPlayer) than the one i am hooking (.v1.activity.Home), so i can't get a context/instance.)

I think it is time that i drop this module, and spend my time elsewhere, maybe coming back to it later when i learn more

Your issue has been addressed in the legacy thread. See this and this

Post Reply Subscribe to Thread
Previous Thread Next Thread
Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes