Sunday, December 23, 2007

Android RFB / VNC implementation

Looks like the Android team has created a functional VNC server implementation natively in their surfaceflinger library (/system/lib/libsurfaceflinger.so). After some further exploration, it is possible to actually connect to and use this VNC server, although there are very strict requirements of the client.

First off, you will need to proxy the connection as it only permits connections on the local interface. For this, I have simply cross-compiled a simple TCP proxy and invoked it on the emulator:

arm-none-linux-gnueabi-gcc -static -o proxy simple-tcp-proxy.c
adb push proxy /data/proxy
adb shell /data/proxy 0.0.0.0 5901 127.0.0.1 5900


Then you must redirect port 5901 on the local machine using the Android telnet interface:

$ telnet localhost 5554
Trying 127.0.0.1...
Connected to localhost.localdomain.
Escape character is '^]'.
Android Console: type 'help' for a list of commands
OK
redir add tcp:5900:5901
OK


Now you can connect from your local workstation:

xvncviewer -noauto localhost

Note: the VNC connection will fail if the client suggests any unsupported pixel format (bit depth, endianness(?), etc). So make sure you use -noauto to disable the initial 8bpp performance test and also use a 16bpp native display on your workstation. If your client requests 24 or 32bpp, the server will reject and hang indefinitely. See adb logcat to determine if you have triggered this behaviour.

A few comments on this implementation:
  • You may only specify 16bpp pixel format, with the true colour flag on and a 565 bit pattern. This creates problems trying to use it from mobile devices with tools like .NET VNC Viewer which default specifies a 655 bit pattern when you force a 16bpp encoding. I wrote a very simple proxy that understands the RFB protocol and "fixes" some of this brokenness, but the experience is still less than desirable.
  • Only the raw encoding is supported, with no incremental updates. This causes atrocious performance problems on the client, making it impractical to use an existing mobile device to interact with the Android RFB server. I tried, it's bad. I think the only real option to proceed would be to extend my proxy to have a deeper understanding of the full RFB protocol, interpretting and re-encoding the data sent from the native Android RFB server. This seems like excessive work, and ultimately is work that should go into the native server. Perhaps I will still do it, though, just to prove it's possible.
For reference and further reading, see my android-developers post on the subject.

UPDATE: I have created my own VNC server implementation on Android to work around these problems.

No comments: