Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make scrcpy work with adb over tcpip #5

Closed
h43z opened this issue Mar 9, 2018 · 57 comments
Closed

Make scrcpy work with adb over tcpip #5

h43z opened this issue Mar 9, 2018 · 57 comments

Comments

@h43z
Copy link

h43z commented Mar 9, 2018

Great useful tool. Thank you.
It would be really cool if the device did not have to be connected via usb.

@rom1v
Copy link
Collaborator

rom1v commented Mar 9, 2018

Yes, I planned to work on this.

Here are the technical details: https://news.ycombinator.com/item?id=16546276

Actually, it won't work with the current version because of an adb bug ("adb reverse" does not work over "adb connect"): https://issuetracker.google.com/issues/37066218

The solution would be to change the direction of the connection, and use "adb forward", but then scrcpy would try to connect before the server is started, so it would need to retry until connected (and the error would not be noticed on connect, but on first read, due to the tunnel).

@Yeradon
Copy link

Yeradon commented Mar 9, 2018

I just used it with adb connect. It worked fine.

@rom1v
Copy link
Collaborator

rom1v commented Mar 9, 2018

@Yeradon Hmm… It does not work for me:

$ adb devices
List of devices attached
192.168.0.42:5555	device
$ scrcpy
/usr/local/share/scrcpy/scrcpy-server.jar: 1 file pushed. 0.5 MB/s (19062 bytes in 0.037s)
error: more than one device/emulator
ERROR: "adb reverse" returned with value 1

@Yeradon
Copy link

Yeradon commented Mar 9, 2018

I was using wireless hotspot from my phone to setup the network. Maybe that makes a difference?

@h43z
Copy link
Author

h43z commented Mar 9, 2018

@Yeradon Created a hotspot with phone and connected computer to it. Then adb connected and run scrcpy, still same error as above.

@rom1v rom1v changed the title Feature request: Make scrpy work with adb over tcpip Make scrpy work with adb over tcpip Mar 9, 2018
@jmgao
Copy link

jmgao commented Mar 10, 2018

Oops, sorry, that bug languished partly because it was assigned to the wrong person.
Your analysis in comment 6 seems correct, so a fix won't be helpful to your users for a while...

However, you might be able to sidestep that by communicating with your device-side process directly via stdin/stdout, instead of having it connect to a forwarded port. It should have identical throughput and start up faster, too.

@rom1v
Copy link
Collaborator

rom1v commented Mar 10, 2018

Thank you for your investigations ;-)

Your analysis in comment 6 seems correct, so a fix won't be helpful to your users for a while...

😞

you might be able to sidestep that by communicating with your device-side process directly via stdin/stdout, instead of having it connect to a forwarded port.

In my first PoC, I used stdout to stream the video.

However:

  • IIRC, not all devices support adb exec-out,
  • adb shell output is corrupted depending on the platform and the version (it sometimes replaces \n by \r\n or \r\r\n),
  • if the server starts, it prints its output on stdout, breaking the whole stream (we can minimize the probability by calling adb start-server just before)…

This is for the device-to-computer communication.

But in both direction, how do you do that? adb is not pipe-friendly like ssh. Did I miss something?

@h43z h43z changed the title Make scrpy work with adb over tcpip Make scrcpy work with adb over tcpip Mar 10, 2018
@jmgao
Copy link

jmgao commented Mar 10, 2018

IIRC, not all devices support adb exec-out,

Looks like it was added around Lollipop (API level 20), which is earlier than your minimum supporter version: https://android.googlesource.com/platform/system/core/+/lollipop-release/adb/services.c#426

adb shell output is corrupted depending on the platform and the version (it sometimes replaces \n by \r\n or \r\r\n),

Yeah, this is caused by the device-side adb daemon allocating a TTY instead of a pipe for stdout. exec-in/exec-out solve that by creating a pipe, but they're unidirectional [1] because the adb protocol didn't specify a way to half close a socket, so you'd end up hanging forever. There's a new shell protocol that was added in N that fixes that by adding another protocol on top that allows each side to tell the other that their stdin is closed.

If you're willing to limit yourself to N+, adb shell should Just Work. Alternatively, if you're willing to use JNI, or upload a separate binary, you should be able to set your PTY to raw mode (via cfmakeraw), which will keep the kernel from munging your binary output.

(Or if you're willing to do dirty hacks, you could do something terrible like base64 encoding your output)

if the server starts, it prints its output on stdout, breaking the whole stream (we can minimize the probability by calling adb start-server just before)…

A sufficiently new version of the adb client shouldn't be subject to this (patch that fixed this is https://android.googlesource.com/platform/system/core/+/1fc8f6e0cfdfd5c9dbbd82116fba2ebec82b4659)

But in both direction, how do you do that? adb is not pipe-friendly like ssh. Did I miss something?

Isn't it? There's problems with detecting when your local stdin closes, but AFAIK, this should work even on ancient devices. I dug up the oldest device I could find roughly available, and it seems to be working as expected (i.e. broken, because the terminal isn't raw mode, so it's mirroring the input and then repeating it via cat).

[1] There's no inherent limitation to keep you from using exec bidirectionally, the command line client just does it to keep people from doing things that will just hang. I'm not suggesting that you do it, but you could connect to the adb server yourself and exec: yourself, and clean up after yourself by doing something like printing your pid at the start, and then doing a separate shell invocation to kill that pid at the end.

@rom1v
Copy link
Collaborator

rom1v commented Mar 10, 2018

Wow, thank you for all this relevant information 👍

I'm not suggesting that you do it, but you could connect to the adb server yourself and exec: yourself

IIUC, in that case, if the device is unplugged while running, the server keeps running?

@rom1v rom1v self-assigned this Mar 12, 2018
rom1v added a commit that referenced this issue Mar 12, 2018
"adb reverse" currently does not work over tcpip (i.e. on a device
connected by "adb connect"):
<https://issuetracker.google.com/issues/37066218>

To work around the problem, if the call to "adb reverse" fails, then
fallback to "adb forward", and reverse the client/server roles.

Keep the "adb reverse" mode as the default because it does not involve
connection retries: when using "adb forward", the client must try to
connect successively until the server listens.

Due to the tunnel, every connect() will succeed, so the client must
attempt to read() to detect a connection failure. For this purpose, when
using the "adb forward" mode, the server initially writes a dummy byte,
read by the client.

Fixes <#5>.
@rom1v
Copy link
Collaborator

rom1v commented Mar 12, 2018

I implemented what I explained in my comment, please checkout the forward branch and tell me whether it works for you.

  1. connect your device to the same wifi as your computer
  2. get the IP of your device (available in Settings → About phone → Status, or adb shell netcfg, or adb shell ip a)
  3. adb tcpip 5555
  4. adb connect DEVICE_IP:5555 (replace DEVICE_IP by your device address)
  5. Unplug your device
  6. scrcpy -b 4M (default is 8Mbps, it may be too high for your wifi connection)

To switch back your device to USB mode: adb usb.

Note that while it now works over tcpip, this is not an optimal solution for streaming a video wirelessly, since the raw stream is still sent over TCP, where a packet loss is very bad for latency, due to head-of-line blocking.

As expected, it is better when the device is plugged. 🙂

If everything is ok, I'll probably publish a new release soon 😉

rom1v added a commit that referenced this issue Mar 12, 2018
"adb reverse" currently does not work over tcpip (i.e. on a device
connected by "adb connect"):
<https://issuetracker.google.com/issues/37066218>

To work around the problem, if the call to "adb reverse" fails, then
fallback to "adb forward", and reverse the client/server roles.

Keep the "adb reverse" mode as the default because it does not involve
connection retries: when using "adb forward", the client must try to
connect successively until the server listens.

Due to the tunnel, every connect() will succeed, so the client must
attempt to read() to detect a connection failure. For this purpose, when
using the "adb forward" mode, the server initially writes a dummy byte,
read by the client.

Fixes <#5>.
@h43z
Copy link
Author

h43z commented Mar 12, 2018

@rom1v awesome! it works for me. It's quite laggy but I guess that was expected.

@rom1v
Copy link
Collaborator

rom1v commented Mar 12, 2018

It's quite laggy but I guess that was expected.

Depending on the use case, I think that reducing the bitrate and resolution may be a good compromise:

scrcpy --bit-rate 2M --max-size 800

@jmgao
Copy link

jmgao commented Mar 12, 2018

IIUC, in that case, if the device is unplugged while running, the server keeps running?

I believe this should work (but haven't verified it on an old device). Unplugging the device/ctrl-c should close everything; the problem stems from not being able to report EOF of your write end, and continue waiting for output from the other side. (e.g. the exec moral equivalent of echo foo | adb shell "cat; sleep 1; echo foo" will have cat hang forever, because its stdin won't get EOF).

@fei-ke
Copy link

fei-ke commented Mar 13, 2018

forward working perfectly here: MI 5s with Lineage 15 and Samsung Note8 with stock rom.

@unixfox
Copy link

unixfox commented Mar 14, 2018

@rom1v I get the same error as you:

/usr/share/scrcpy/scrcpy-server.jar: 1 file pushed. 0.7 MB/s (19038 bytes in 0.025s)
error: more than one device/emulator
ERROR: "adb reverse" returned with value 1
WARN: 'adb reverse' failed, fallback to 'adb forward'
ERROR: Exception on thread Thread[main,5,main]
java.io.IOException: Connection refused
	at android.net.LocalSocketImpl.connectLocal(Native Method)
	at android.net.LocalSocketImpl.connect(LocalSocketImpl.java:292)
	at android.net.LocalSocket.connect(LocalSocket.java:131)
	at com.genymobile.scrcpy.DesktopConnection.connect(DesktopConnection.java:32)
	at com.genymobile.scrcpy.DesktopConnection.open(DesktopConnection.java:37)
	at com.genymobile.scrcpy.Server.scrcpy(Server.java:13)
	at com.genymobile.scrcpy.Server.main(Server.java:70)
	at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
	at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:316)

I don't know what's causing this issue because I followed your instructions: #5 (comment)

@rom1v
Copy link
Collaborator

rom1v commented Mar 14, 2018

@unixfox, Did you compile the forward branch using the prebuilt server from v1.0?

@unixfox
Copy link

unixfox commented Mar 14, 2018

@rom1v Yes I did.
I made some research about this issue and I found that adb reverse requires to set the IP and the port in the Developer menu: microsoft/vscode-react-native#324 (comment). But unfortunately I don't have this option on my Samsung galaxy S7.

@rom1v
Copy link
Collaborator

rom1v commented Mar 14, 2018

@rom1v Yes I did.

That's the problem: you need the changes for the server too.

I found that adb reverse requires to set the IP and the port in the Developer menu

No, it seems that's in their own app.

@rom1v
Copy link
Collaborator

rom1v commented Mar 14, 2018

We just released v1.1 including this fix: Scrcpy now works wirelessly.

@alex-Symbroson
Copy link

Will the issue be fixes in the near future?
I'm currently having a similar problem while trying to use scrcpy via adb usb on a tolino tablet running android 4.4.4
scrcpy aborts after

error: closed
ERROR: "adb reverse" returned with value 1
WARNING: 'adb reverse' failed, fallback to 'adb forward'

@Raju
Copy link

Raju commented Apr 11, 2020

  • connect the device through usb
  • adb kill-server
  • adb tcpip portNumber example: adb tcpip 5555
  • adb connect ipOfDevice example adb connect 192.168.0.5 or adb connect 192.168.0.5:5555
  • unplug the device from usb
  • adb devices
  • scrcpy -s deviceID or scrcpy -s deviceIP

this will allow scrcpy to work wirelessly

@pranavburnwal
Copy link

Question: Do we have to every time first connect via usb and then switch wireless or its just first time?

rom1v added a commit that referenced this issue Oct 27, 2021
On Linux, socket functions are unblocked by shutdown(), but on Windows
they are unblocked by closesocket().

Expose net_interrupt() and net_close() to abstract these differences:
 - net_interrupt() calls shutdown() on Linux and closesocket() on
   Windows (if not already called);
 - net_close() calls close() on Linux and closesocket() on Windows (if
   not already called).

This simplifies the server code, and prevents a data race on close
(reported by TSAN) on Linux (but does not fix it on Windows):

    WARNING: ThreadSanitizer: data race (pid=836124)
      Write of size 8 at 0x7ba0000000d0 by main thread:
        #0 close ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1690 (libtsan.so.0+0x359d8)
        #1 net_close ../app/src/util/net.c:211 (scrcpy+0x1c76b)
        #2 close_socket ../app/src/server.c:330 (scrcpy+0x19442)
        #3 server_stop ../app/src/server.c:522 (scrcpy+0x19e33)
        #4 scrcpy ../app/src/scrcpy.c:532 (scrcpy+0x156fc)
        #5 main ../app/src/main.c:92 (scrcpy+0x622a)

      Previous read of size 8 at 0x7ba0000000d0 by thread T6:
        #0 recv ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:6603 (libtsan.so.0+0x4f4a6)
        #1 net_recv ../app/src/util/net.c:167 (scrcpy+0x1c5a7)
        #2 run_receiver ../app/src/receiver.c:76 (scrcpy+0x12819)
        #3 <null> <null> (libSDL2-2.0.so.0+0x84f40)
rom1v added a commit that referenced this issue Oct 27, 2021
On Linux, socket functions are unblocked by shutdown(), but on Windows
they are unblocked by closesocket().

Expose net_interrupt() and net_close() to abstract these differences:
 - net_interrupt() calls shutdown() on Linux and closesocket() on
   Windows (if not already called);
 - net_close() calls close() on Linux and closesocket() on Windows (if
   not already called).

This simplifies the server code, and prevents a data race on close
(reported by TSAN) on Linux (but does not fix it on Windows):

    WARNING: ThreadSanitizer: data race (pid=836124)
      Write of size 8 at 0x7ba0000000d0 by main thread:
        #0 close ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1690 (libtsan.so.0+0x359d8)
        #1 net_close ../app/src/util/net.c:211 (scrcpy+0x1c76b)
        #2 close_socket ../app/src/server.c:330 (scrcpy+0x19442)
        #3 server_stop ../app/src/server.c:522 (scrcpy+0x19e33)
        #4 scrcpy ../app/src/scrcpy.c:532 (scrcpy+0x156fc)
        #5 main ../app/src/main.c:92 (scrcpy+0x622a)

      Previous read of size 8 at 0x7ba0000000d0 by thread T6:
        #0 recv ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:6603 (libtsan.so.0+0x4f4a6)
        #1 net_recv ../app/src/util/net.c:167 (scrcpy+0x1c5a7)
        #2 run_receiver ../app/src/receiver.c:76 (scrcpy+0x12819)
        #3 <null> <null> (libSDL2-2.0.so.0+0x84f40)
@meroz92 meroz92 mentioned this issue Jun 25, 2022
2 tasks
@AdmoMontes
Copy link

Help me... i have error:
Phone: Xiaomi Redmi K40 / Poco F3

[server] ERROR: Could not invoke method
java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.genymobile.scrcpy.wrappers.InputManager.injectInputEvent(InputManager.java:37)
at com.genymobile.scrcpy.Device.injectEvent(Device.java:192)
at com.genymobile.scrcpy.Device.injectEvent(Device.java:196)
at com.genymobile.scrcpy.Controller.injectTouch(Controller.java:320)
at com.genymobile.scrcpy.Controller.handleEvent(Controller.java:134)
at com.genymobile.scrcpy.Controller.control(Controller.java:83)
at com.genymobile.scrcpy.Controller.lambda$start$0$com-genymobile-scrcpy-Controller(Controller.java:90)
at com.genymobile.scrcpy.Controller$$ExternalSyntheticLambda0.run(Unknown Source:2)
at java.lang.Thread.run(Thread.java:1012)
Caused by: java.lang.SecurityException: Injecting input events requires the caller (or the source of the instrumentation, if any) to have the INJECT_EVENTS permission.
at android.os.Parcel.createExceptionOrNull(Parcel.java:3011)
at android.os.Parcel.createException(Parcel.java:2995)
at android.os.Parcel.readException(Parcel.java:2978)
at android.os.Parcel.readException(Parcel.java:2920)
at android.hardware.input.IInputManager$Stub$Proxy.injectInputEventToTarget(IInputManager.java:1294)
at android.hardware.input.InputManager.injectInputEvent(InputManager.java:1153)
at android.hardware.input.InputManager.injectInputEvent(InputManager.java:1182)
... 10 more

@rom1v
Copy link
Collaborator

rom1v commented Jul 31, 2023

@AdmoMontes https://github.com/Genymobile/scrcpy/blob/master/FAQ.md#mouse-and-keyboard-do-not-work (but this is not the right place to ask, this issue is about adb over tcpip)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests