diff options
Diffstat (limited to 'README')
| -rw-r--r-- | README | 147 |
1 files changed, 147 insertions, 0 deletions
@@ -0,0 +1,147 @@ +scrcpy-android +============== + + +What it does +------------ +Native Android app that mirrors a second Android device over Wireless ADB, +the same way scrcpy(1) does. Source device runs this app; target is any +Android 11+ device with Wireless Debugging enabled. Video, audio, touch, +key, and clipboard are forwarded. No root required on either side. + +Java only, no Kotlin, no NDK. ADB stack vendored from +MuntashirAkon/libadb-android. scrcpy-server.jar is a pinned upstream binary +in `app/src/main/assets/`. + + +How to run it +------------- +Prerequisites: Android Studio (Iguana or newer) or JDK 17 + Android SDK with +platform-tools and platform 35. + + ./scripts/fetch-vendor # pull libadb-android into vendor/ + ./scripts/update-server # download + verify pinned scrcpy-server.jar + ./gradlew :app:assembleDebug # build debug APK + adb install app/build/outputs/apk/debug/app-debug.apk + +For a signed release APK, see "How to build a release APK" below. + +On the target device: Settings -> Developer options -> Wireless debugging -> +Pair device with pairing code. Note the displayed ip:port and 6-digit code. + +In the app on the source device: enter ip:port and the pairing code, tap +Pair. Then tap the saved device row to connect and mirror. + + +How to test it +-------------- +A `./test` entrypoint runs tests inside a project-local Docker image +(JDK 17, Android cmdline-tools, platform 35, build-tools 35, x86_64 +emulator). The image is built on first run and cached. + + ./test # JVM unit tests (Wire, Sync, ControlMessages, + # Devices, plus piped-stream parser tests for + # VideoStream, AudioStream, ControlStream). + # ~30 s on warm cache. No KVM needed. + + ./test e2e # Single-emulator self-mirror end-to-end. + # Boots the API-35 AOSP emulator, roots it, + # installs the debug APK, drops the app's public + # key into /data/misc/adb/adb_keys, launches + # Mirror against 127.0.0.1:5555, then asserts + # 'video frame n=1' lands in logcat and the + # screencap is non-uniform. Then kills the server + # on the target and asserts the session detects the + # dropped link and auto-reconnects (a fresh + # 'video frame n=1'). Needs /dev/kvm readable by the + # invoking uid. + + ./test all # unit tests followed by e2e. Fails fast: if the + # unit tier fails, the e2e tier is skipped. + # Same /dev/kvm requirement as `./test e2e`. + + ./test record # Boots the emulator, runs a Mirror session, injects + # taps/keys, and stitches a screen recording into + # ./output.mp4. Needs /dev/kvm. + + ./test apk # Smoke build: generates a throwaway PKCS12 keystore + # under .tools/, runs scripts/build-apk against it, + # verifies the resulting APK is signed. No KVM. + +If /dev/kvm is not rw for your uid the script prints the one-line fix +and exits - it does not auto-chmod. + + +How to build a release APK +-------------------------- +Set four environment variables and run the release script: + + export KEYSTORE_PATH=/abs/path/to/release.jks + export KEYSTORE_PASS=... + export KEY_ALIAS=... + export KEY_PASS=... + ./scripts/build-apk + +The signed APK lands at +`app/build/outputs/apk/release/app-release.apk` and its sha256 is +printed at the end. If `apksigner` is reachable (either on $PATH or +under $ANDROID_SDK_ROOT/build-tools/) the script also runs a +post-build `apksigner verify`. + +Generate a release keystore once with: + + keytool -genkey -v -keystore release.jks -storetype PKCS12 \ + -alias scrcpy-android -keyalg RSA -keysize 2048 -validity 10000 + +Keep it OUTSIDE the repo. The .gitignore does not protect arbitrary +filenames; the keystore is your secret. + + +How to debug it +--------------- +Everything is logged with tag `scrcpy-android`: + + adb logcat -s scrcpy-android + +Expected log sequence on a successful connect (pair ok appears once, +at pairing time): + + pair ok host=... pair_port=... + adb connect ok + push /data/local/tmp/scrcpy-server.jar bytes=... + spawn server ver=<v> scid=<hex> + openAbstract scrcpy_<scid> ok (attempt <n>) (x3) + video meta codec=h264 + audio meta codec=raw (or opus if selected in Settings) + video session meta <w>x<h> client_resize=false + video frame n=1 + +Server stdout/stderr is pumped into the same log stream prefixed with +`server: ` so target-side errors are visible without an extra adb shell. + + +Layout +------ + app/ Android module (Java) + vendor/libadb-android/ vendored libadb module, pinned tag, plus licenses + scripts/ POSIX sh, set -eu + gradle/, gradlew* Gradle 8.9 wrapper + LICENSE Apache-2.0 + + +Constraints +----------- +- minSdk 31 (source device needs Android 12+); compileSdk = targetSdk = + 35 (built against and targeting Android 15) +- Wireless ADB only; no USB-OTG host mode +- Multi-touch, soft keys, clipboard sync, and hardware Back (long-press + the device Back key while Mirror is foreground) are forwarded. +- Video codec (h264 / h265 / av1) and audio codec (raw / opus) are + selectable from the in-app Settings screen; defaults are h264 + raw. + + +License +------- +Apache-2.0; see LICENSE. Bundles libadb-android (dual GPL-3.0-or-later OR +Apache-2.0, used here under Apache-2.0) and a pinned, checksum-verified +scrcpy-server.jar from Genymobile/scrcpy (Apache-2.0). |