1

I'm really new to Android NDK, and have the following issue.

I have a file within my JNI folder named 'get-raw-image.cpp' (trying to integrate from here), and within it I've made a function to call natively with the Java android code.

extern "C"{
    void Java_com_example_ndksetup_MainActivity_testTest(JNIEnv * env, jobject ths){
        __android_log_print(ANDROID_LOG_DEBUG, "LOG_TAG", "Testing");

        ScreenshotClient screenshot;
        //screenshot.update();
    }
}

I'm trying to reference the ScreenshotClient class (within this same file) and create a new instance of it, but keep getting this error when I build/compile:

"undefined reference to 'android::ScreenshotClient::ScreenshotClient()' collect2: ld returned 1 exit status"

Here is what the ScreenshotClient class looks like, any help is appreciated, thanks.

class ScreenshotClient {
    /*
    sp<IMemoryHeap> mHeap;
    uint32_t mWidth;
    uint32_t mHeight;
    PixelFormat mFormat;
    */
    char data[1024]; //android 4.2 embed CpuConsumer::LockedBuffer here which cause more space
public:
    ScreenshotClient();

#if defined(TARGET_ICS)
    // frees the previous screenshot and capture a new one
    int32_t update();
#endif
#if defined(TARGET_JB)
    // frees the previous screenshot and capture a new one
    int32_t update(const sp<IBinder>& display);
#endif
    // pixels are valid until this object is freed or
    // release() or update() is called
    void const* getPixels() const;

    uint32_t getWidth() const;
    uint32_t getHeight() const;
    uint32_t getStride() const; //base + getStride()*bytesPerPixel will get start address of next row
    int32_t getFormat() const;
    // size of allocated memory in bytes
    size_t getSize() const;
};

#if defined(TARGET_JB)
class SurfaceComposerClient {
public:
    //! Get the token for the existing default displays.
    //! Possible values for id are eDisplayIdMain and eDisplayIdHdmi.
    static sp<IBinder> getBuiltInDisplay(int32_t id);
};
#endif

class ProcessState {
    char data[1024]; //please adjust this value when you copy this definition to your real source!!!!!!!!!!!!!!!!!!!!!!!
public:
    static sp<ProcessState> self();
    void startThreadPool();
};

} //end of namespace android

using android::ScreenshotClient;
using android::sp;
using android::IBinder;
#if defined(TARGET_JB)
using android::SurfaceComposerClient;
#endif
using android::ProcessState;

#endif //} end of "if defined(TARGET_ICS) || defined(TARGET_JB)"

edit: I made the native function static in the extern "C" section and now have the following errors when I call it, but it is compiling now at least.

06-17 16:47:09.365: E/AndroidRuntime(18566): FATAL EXCEPTION: main 06-17 16:47:09.365: E/AndroidRuntime(18566): java.lang.IllegalStateException: Could not execute method of the activity 06-17 16:47:09.365: E/AndroidRuntime(18566): at android.view.View$1.onClick(View.java:3699) 06-17 16:47:09.365: E/AndroidRuntime(18566): at android.view.View.performClick(View.java:4223) 06-17 16:47:09.365: E/AndroidRuntime(18566): at android.view.View$PerformClick.run(View.java:17281) 06-17 16:47:09.365: E/AndroidRuntime(18566): at android.os.Handler.handleCallback(Handler.java:615) 06-17 16:47:09.365: E/AndroidRuntime(18566): at android.os.Handler.dispatchMessage(Handler.java:92) 06-17 16:47:09.365: E/AndroidRuntime(18566): at android.os.Looper.loop(Looper.java:137) 06-17 16:47:09.365: E/AndroidRuntime(18566): at android.app.ActivityThread.main(ActivityThread.java:4898) 06-17 16:47:09.365: E/AndroidRuntime(18566): at java.lang.reflect.Method.invokeNative(Native Method) 06-17 16:47:09.365: E/AndroidRuntime(18566): at java.lang.reflect.Method.invoke(Method.java:511) 06-17 16:47:09.365: E/AndroidRuntime(18566): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1008) 06-17 16:47:09.365: E/AndroidRuntime(18566): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:775) 06-17 16:47:09.365: E/AndroidRuntime(18566): at dalvik.system.NativeStart.main(Native Method) 06-17 16:47:09.365: E/AndroidRuntime(18566): Caused by: java.lang.reflect.InvocationTargetException 06-17 16:47:09.365: E/AndroidRuntime(18566): at java.lang.reflect.Method.invokeNative(Native Method) 06-17 16:47:09.365: E/AndroidRuntime(18566): at java.lang.reflect.Method.invoke(Method.java:511) 06-17 16:47:09.365: E/AndroidRuntime(18566): at android.view.View$1.onClick(View.java:3694) 06-17 16:47:09.365: E/AndroidRuntime(18566): ... 11 more 06-17 16:47:09.365: E/AndroidRuntime(18566): Caused by: java.lang.UnsatisfiedLinkError: Native method not found: com.example.ndksetup.MainActivity.testTest:()V 06-17 16:47:09.365: E/AndroidRuntime(18566): at com.example.ndksetup.MainActivity.testTest(Native Method) 06-17 16:47:09.365: E/AndroidRuntime(18566): at com.example.ndksetup.MainActivity.t(MainActivity.java:72) 06-17 16:47:09.365: E/AndroidRuntime(18566): ... 14 more

Here is my Android.mk make file:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_LDLIBS := -llog

LOCAL_MODULE    := ndksetup
LOCAL_MODULE := libgui 
LOCAL_SRC_FILES := fake_libgui.cpp
LOCAL_SRC_FILES := get-raw-image.cpp
LOCAL_SHARED_LIBRARIES += libgui

include $(BUILD_SHARED_LIBRARY)
parameter
  • 894
  • 2
  • 18
  • 35
  • Can we see more of the source code? That linking error makes me wonder if you forgot to define the constructor you declared for ScreenShotClient. – Reynard Jun 18 '14 at 03:13
  • @Reynard I am using code from here: https://github.com/sjitech/sji-android-screen-capture specifically this file: https://github.com/sjitech/sji-android-screen-capture/blob/master/src/android/ffmpeg/get-raw-image.cpp – parameter Jun 18 '14 at 03:29

2 Answers2

3

second attempt: You cannot declare a JNI method static, because it must be "visible" to dynamic loader.

first attempt: On ICS or later, class ScreenshotClient is part of libgui.so (earlier versions had this class in the system library called libsurfaceflinger_client.so) You can pull it from your device or emulator, with command

adb pull /system/lib/libgui.so c:\android\libs\libgui.so

Now in your Android.mk, add LOCAL_LDFLAGS += c:/android/libs/libgui.so to your module.

You will see a warning from ndk-build:

Android NDK: WARNING:jni/Android.mk: non-system libraries in linker flags: c:/android/libs/libgui.so

This is the price of using non-public APIs in ndk-build.

expecting the future questions: Make sure that your app has all relevant permissions to access the screen. See How to use ScreenShotClient in my android application for related discussion.

UPDATE

The referenced GitHub uses fake libs instead of pulling them from the device. It is easy to implement this with Android.mk. Get fake_libbinder.cpp to LOCAL_PATH directory, and add the following section into your Android.mk:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE   := libibgui
LOCAL_SRC_FILES := fake_gui.cpp
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_LDLIBS := -llog
LOCAL_MODULE    := ndksetup
LOCAL_SRC_FILES := get-raw-image.cpp
LOCAL_SHARED_LIBRARIES += libgui
include $(BUILD_SHARED_LIBRARY)

Now add LOCAL_SHARED_LIBRARIES += libgui to your main module.

You will probably need same for the fake libbinder. Still, your app will use the real libbinder and libgui libraries from the device /system/lib. Still, it depends on system calls not being changed.

Community
  • 1
  • 1
Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • I'm using the ScreenshotClient class from here, I believe it's different from the one from surfaceflinger? https://github.com/sjitech/sji-android-screen-capture – parameter Jun 18 '14 at 15:58
  • This solution [fakes libgui](https://github.com/sjitech/sji-android-screen-capture/blob/master/src/android/ffmpeg/fake_libgui.cpp) instead of pulling the real one from a device. This involves [custom build](https://github.com/sjitech/sji-android-screen-capture/blob/master/src/android/ffmpeg/1_build_get-raw-image.sh) instead of `ndk-build`. But you can easily add this to an Anroid.mk, if this is your preferred way. – Alex Cohn Jun 18 '14 at 16:20
  • So I would use the get-raw-image.cpp and fake_libgui.cpp in my jni folder, then include the build file in my Android.mk? How would I go about including that build file in the make file? thank you for helping. – parameter Jun 18 '14 at 16:23
  • what do you mean by 'main module'? what file is this? – parameter Jun 18 '14 at 16:39
  • I have updated my question to include my makefile as well, does this look correct? I still get an undefined reference error when I try to create a new ScreenshotClient and compile/build. – parameter Jun 18 '14 at 16:47
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/55927/discussion-between-user2739431-and-alex-cohn). – parameter Jun 19 '14 at 16:48
1

I am the author of 'get-raw-image.cpp'. About your question, 1: You should be aware that your app will failed due to ScreenshotClient::update method require FRAME_BUFFER_ACCESS permission which normal java app does not have this permission. (Adb shell user is OK).

2: These native code need be linked to libgui.so and libbinder.so, but unfortunately, gcc will ask you for a lot of *.so referenced by them, I failed to do that, so i made a fake libgui.so, libbinder.so and link to get-raw-image. You can use my make file https://github.com/sjitech/sji-android-screen-capture/blob/master/src/android/ffmpeg/1_build_get-raw-image.sh to make these fake so files. (You need remove "rm libgui.so libbinder.so" in the script, then make, then extract so files).

Finally, you add libgui.so libbinder.so to your Android.mk for link.

osexp2000
  • 2,910
  • 30
  • 29
  • hi, thanks for replying. I'm trying to build an android application using NDK and use your file to take screen shots from a non rooted device. Can this be done? I'm wondering what to do with the fake libgui.so and libbinder.so files once I've made them. Do they replace the real libgui.so file in the obj/local/armeabi directory in eclipse? – parameter Jun 26 '14 at 20:28
  • For non rooted device, normal apk does not have FRAME_BUFFER_ACCESS nor root permission, so this method can not be used. Besides, this fake so file will not replace /system/lib/*.so because i set an option to let the get-raw-image link to /system/lib/*.so in runtime. – osexp2000 Jul 20 '14 at 11:02
  • 'get-raw-image.cpp' link broken :( . Please provide the correct link. – RanjithKumarRagavan Mar 16 '15 at 10:45