xplodwild / bbqscreen_client

Sources for the client app of BBQScreen
GNU General Public License v2.0
54 stars 23 forks source link

Immediately disconnected with non-rooted Xperia Z3 Compact #7

Open estan opened 8 years ago

estan commented 8 years ago

Hi,

I purchased the app hoping to use it on my non-rooted Sony Ericsson Xperia Z3 Compact. I'm running the client on an up-to-date Arch Linux.

Here's what I did:

  1. Installed the app.
  2. Enabled USB debugging on the phone.
  3. Started the app and pressed "USB" at the first dialog.
  4. Built the client from master Git branch.
  5. (Confirmed that running Linux/prebuilts/adb devices shows my device.)
  6. Started the client with Linux/BBQScreenClient2.
  7. Pressed "Start USB Service".
  8. Pressed "Connect via USB".

The result is that the black screen is shown, but the text message switches between

very very quickly, and "Connecting to "127.0.0.1"" is repeated in the terminal.

While the client is running and the USB service is running, the output from netstat -tulpn is:

(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.1:5037          0.0.0.0:*               LISTEN      13738/adb           
tcp        0      0 127.0.0.1:9876          0.0.0.0:*               LISTEN      13738/adb           
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      -                   
tcp6       0      0 ::1:631                 :::*                    LISTEN      -                   
udp        0      0 0.0.0.0:5353            0.0.0.0:*                           -                   
udp        0      0 0.0.0.0:36013           0.0.0.0:*                           -                   
udp        0      0 0.0.0.0:68              0.0.0.0:*                           -                   
udp        0      0 192.168.0.13:123        0.0.0.0:*                           -                   
udp        0      0 127.0.0.1:123           0.0.0.0:*                           -                   
udp        0      0 0.0.0.0:123             0.0.0.0:*                           -                   
udp        0      0 0.0.0.0:62366           0.0.0.0:*                           -                   
udp6       0      0 :::5353                 :::*                                -                   
udp6       0      0 :::9876                 :::*                                13765/Linux/BBQScre 
udp6       0      0 :::51130                :::*                                -                   
udp6       0      0 :::11880                :::*                                -                   
udp6       0      0 fe80::a11:96ff:fe91:123 :::*                                -                   
udp6       0      0 ::1:123                 :::*                                -                   
udp6       0      0 :::123                  :::*                                -

So it seems the forwarding of TCP port 9876 that the client sets up using adb is working. However, when I tried telnet 127.0.0.1 9876, the connection is established, but telnet is immediately disconnected again:

[estan@pyret ~]$ telnet 127.0.0.1 9876
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Connection closed by foreign host.
[estan@pyret ~]$

I guess this is what's happening for BBQScreenClient2 as well.

Any idea what is going on? I would love to get this working.

The debug log window in the client shows nothing while this is happening. I've also tried the Legacy mode with same results.

Please let me know if you need more info or want me to try something, as I have some experience with Qt programming.

As a side note, I'm getting a warning about missing version information on libcrypto.so.1.0.0 when running the prebuilt adb:

[estan@pyret bbqscreen_client]$ Linux/prebuilts/adb devices
Linux/prebuilts/adb: /usr/lib32/libcrypto.so.1.0.0: no version information available (required by Linux/prebuilts/adb)
List of devices attached 
YT9112CUE1      device

[estan@pyret bbqscreen_client]$

But I don't think that is related to this problem, since the device is found and the forwarding seems to work. Running ldd on the adb executable gives:

[estan@pyret bbqscreen_client]$ ldd Linux/prebuilts/adb 
Linux/prebuilts/adb: /usr/lib32/libcrypto.so.1.0.0: no version information available (required by Linux/prebuilts/adb)
        linux-gate.so.1 (0xf7731000)
        libc.so.6 => /usr/lib32/libc.so.6 (0xf7509000)
        libpthread.so.0 => /usr/lib32/libpthread.so.0 (0xf74eb000)
        libz.so.1 => /usr/lib32/libz.so.1 (0xf74d4000)
        libcrypto.so.1.0.0 => /usr/lib32/libcrypto.so.1.0.0 (0xf72c0000)
        /lib/ld-linux.so.2 (0xf7732000)
        libdl.so.2 => /usr/lib32/libdl.so.2 (0xf72ba000)

To double check, I also hacked the MainWindow::runAdb function to use another adb that I have, which does not give this warning, and got the same problem as described in the beginning (immediate disconnect while trying to load the first frame).

Thanks in advance.

estan commented 8 years ago

Some more info: The phone is running Android 5.1.1 Lollipop, and I'm using client version 2.3.3.

xplodwild commented 8 years ago

Hey, can you do an adb logcat when connecting to see if there's any errors there ?

estan commented 8 years ago

Here is the output from Linux/prebuilt/adb logcat when I

  1. Started the client.
  2. Clicked "Start USB Service".
  3. Clicked "Connect via USB".
  4. Let it run for around 5 seconds.
  5. Closed the black window.
  6. Closed the client window.

During 4, the "Connecting to '127.0.0.1'... (Attempt 1/3)" and "Connected, loading first frame..." messages were looping fast in the black window.

Looking at the log, I can't see anything suspicious. Maybe you can see something? I only see a few messages mentioning "bbq" or "BBQ".

estan commented 8 years ago

The logcat output when not running the client, and doing just:

[estan@pyret android-sdk-linux]$ telnet 127.0.0.1 9876
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Connection closed by foreign host.
[estan@pyret android-sdk-linux]$

doesn't contain anything interesting either I think. Just a couple of

V/WindowManager(  860): Adding window Window{d302bf2 u0 android/com.android.internal.app.ResolverActivity} at 8 of 13 (after Window{c227200 u0 org.bbqdroid.bbqscreen/org.bbqdroid.bbqscreen.BBQScreenPreferencesActivity})

messages.

The output from adb forward --list is:

[estan@pyret bbqscreen_client]$ Linux/prebuilts/adb forward --list
Linux/prebuilts/adb: /usr/lib32/libcrypto.so.1.0.0: no version information available (required by Linux/prebuilts/adb)
YT9112CUE1 tcp:9876 tcp:9876
[estan@pyret bbqscreen_client]$

So the forwarding is correctly set up.

estan commented 8 years ago

I made an interesting find. I tried starting the USB service interactively from an adb shell, and got this:

shell@D5803:/ $ /data/local/tmp/bbqscreen -s 50 -720 -q 4500 -i                
CANNOT LINK EXECUTABLE: cannot locate symbol "_ZN7android11AudioSourceC1E14audio_source_tjj" referenced by "/data/local/tmp/bbqscreen"...

So I'm suspecting the final command that the client tries to run when you press "Start USB Service" actually fails because of this. Any idea why it is missing that symbol?

estan commented 8 years ago

I also realized I have to run BBQScreenClient2 with the Linux directory as the current working directory, otherwise the adb command is not found since the relative path prebuilt/adb is used. So I think the adb executable was not found and errors because of this are strangely supressed.

When running from within the Linux directory, I finally get output in the log window. It's the same error as above:

CANNOT LINK EXECUTABLE: cannot locate symbol "_ZN7android11AudioSourceC1E14audio_source_tjj" referenced by "/data/local/tmp/bbqscreen"...

Repeated 20 times before it fails with an error dialog.

So the problem really seems to be that symbol that the bbqscreen process is missing.

estan commented 8 years ago

The missing symbol seems to be part of libmedia.so, which is not part of the public API of NDK. So perhaps it is wrong of bbqscreen to link against this? I'm guessing that the libmedia.so that my device has is slightly different from the one you linked against when making the app, or maybe that root privileges are required for it to be accessible? (Or maybe the app needs more permissions for this to work on a non-rooted phone?).

estan commented 8 years ago

A last update, which I think should help you fix this: Using nm from the Android NDK on the various libmedia*.so files I have in /system/lib, I found that on my device the missing symbol is actually part of libmediaplayerservice.so, not libmedia.so:

[estan@pyret android-sdk-linux]$ ./platform-tools/adb pull /system/lib/libmediaplayerservice.so
2536 KB/s (582956 bytes in 0.224s)

and then:

[estan@pyret android-ndk-r10e]$ ./toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64/arm-linux-androideabi/bin/nm -D ~/Blandat/android-sdk-linux/libmediaplayerservice.so | grep audio_source_tjji
         U _ZN7android11AudioSourceC1E14audio_source_tjji

So I think you need to add -lmediaplayerservice to the link command line when you link bbqscreen for my device (or perhaps when you link the ffmpeg library).

estan commented 8 years ago

Sorry. I misread the output from nm. The 'U' means it's undefined, so libmediaplayerservice.so is just a user of that symbol not the provider. But I finally found which library has it:

[estan@pyret phone_libs]$ ~/Nedladdat/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/bin/nm -D libstagefright.so | grep tjji
00072590 T _ZN7android11AudioSourceC1E14audio_source_tjji
000722c0 T _ZN7android11AudioSourceC2E14audio_source_tjji
[estan@pyret phone_libs]$

So on my device, it's libstagefright.so which has that symbol ('T' means it's in the text (code) section). So you probably need to add -lstagefright to your link line.

estan commented 8 years ago

And I'm wrong again. Running ndk-depends on the bbqscreen executable, I see that it's actually linked against libstagefright.so, so that's not the problem. The problem is simply that in the version of libstagefright.so that you linked to, the function has the signature

    android::AudioSource::AudioSource(audio_source_t, unsigned int, unsigned int)

(corresponding to the mangled name _ZN7android11AudioSourceC1E14audio_source_tjj), while in the version I have on my device, it's

    android::AudioSource::AudioSource(audio_source_t, unsigned int, unsigned int, int)

(corresponding to the mangled name _ZN7android11AudioSourceC1E14audio_source_tjji, note the extra 'i' on the end!). This is the reason the symbol is not found.

estan commented 8 years ago

In the meantime I've come up with a workaround. It's quite ugly, but works.

What I did was to write a shim shared library containing an AudioSource constructor with the signature that bbqscreen expects. The shim constructor then loads the real constructor using dlsym(RTLD_NEXT, ...) and calls it, passing a 0 for the fourth argument (I only hoped this would work, and it does). This shim library is then injected into the bbqscreen process using LD_PRELOAD.

The full shim library is:

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <dlfcn.h>

#ifndef _GNU_SOURCE
#define _GNU_SOURCE // For RTLD_NEXT
#endif

typedef enum {
    AUDIO_SOURCE_DEFAULT             = 0,
    AUDIO_SOURCE_MIC                 = 1,
    AUDIO_SOURCE_VOICE_UPLINK        = 2,
    AUDIO_SOURCE_VOICE_DOWNLINK      = 3,
    AUDIO_SOURCE_VOICE_CALL          = 4,
    AUDIO_SOURCE_CAMCORDER           = 5,
    AUDIO_SOURCE_VOICE_RECOGNITION   = 6,
    AUDIO_SOURCE_VOICE_COMMUNICATION = 7,
    AUDIO_SOURCE_CNT,
    AUDIO_SOURCE_MAX                 = AUDIO_SOURCE_CNT - 1,
} audio_source_t;

namespace android {

struct AudioSource {
    AudioSource(
            audio_source_t inputSource,
            uint32_t sampleRate,
            uint32_t channels = 1);
};

AudioSource::AudioSource(
        audio_source_t inputSource,
        uint32_t sampleRate,
        uint32_t channels) {
    typedef void (AudioSource::*Original)(audio_source_t, uint32_t, uint32_t, int);

    static Original originalMethod = 0;

    if (originalMethod == 0) {
        // Save a pointer to the original constructor in originalMethod.
        void *tmpPtr = dlsym(RTLD_NEXT, "_ZN7android11AudioSourceC1E14audio_source_tjji");
        memcpy(&originalMethod, &tmpPtr, sizeof(void *));
    }

    // Call the original constructor with last argument hardcoded to 0.
    printf("Calling original (originalMethod=%p)\n", originalMethod);
    (this->*originalMethod)(inputSource, sampleRate, channels, 0);
}

} // namespace android

which I compiled with:

[estan@pyret Linux]$ ~/android-toolchain/bin/arm-linux-androideabi-g++ -shared -fPIC -ldl -o shim.so shim.cpp

I then updated mainwindow.cpp in the client sources to first adb push the shim library to the temp dir, and made sure that LD_PRELOAD is set to its path when the client runs bbqscreen using adb shell:

diff --git a/mainwindow.cpp b/mainwindow.cpp
index 3de035c..ca12dd2 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -447,8 +447,17 @@ void MainWindow::startUsbService()
                return;
        }

+       // Push shim library.
+       args.clear();
+       args << "push";
+       args << QDir(QCoreApplication::applicationDirPath()).absolutePath() + "/shim.so";
+       args << "/data/local/tmp/shim.so";
+       QProcess* pushShimProc = runAdb(args);
+       pushShimProc->waitForFinished();
+
        args.clear();
        args << "shell";
+       args << "export LD_PRELOAD=/data/local/tmp/shim.so ; ";
        args << "/data/local/tmp/bbqscreen";
        args << "-s";
        args << "50";

The bbqscreen process can now start and the client connects successfully:

working

While this workaround works, I wouldn't recommend it. As it seems Sony does not provide the sources for their (I think) modified libstagefright, I'm not sure what passing in 0 like that does. At least I haven't been able to find an AudioSource constructor with the signature that my library has anywhere in the public Android sources.

The right fix is to not rely on libstagefright. It's not public API and not guaranteed to be stable across devices.

estan commented 8 years ago

I'm keeping a fork here with my workaround until this is fixed, in case someone else has an Xperia Z3.

xplodwild commented 8 years ago

Hey! Thanks for the investigation and the workaround! I'll look into that and issue an official update soon.

estan commented 8 years ago

:+1: