Definable split-screen for Android "AI boxes" that project into a car's CarPlay window — built for, and tested on, a Carlinkit Ultra AI Box behind an XPeng G6.
The stock split feature on these boxes is a stub: it can only pin a shortcut, offers five fixed ratios, and silently does nothing unless the vendor's own launcher is the home screen. Split Deck replaces it with named splits, any pair of apps, a free ratio slider, and a pinned shortcut per split.
Grab split-deck.apk from Releases, then:
adb install -r split-deck.apk
adb shell pm grant com.stevelea.splitdeck android.permission.WRITE_SECURE_SETTINGS # lets it toggle freeform supportOpen Split Deck, press + New split, pick the left and right app, drag the ratio, save. Launch runs it, Shortcut pins it to the home screen.
| Stock split (default) | Freeform | |
|---|---|---|
| what happens | writes persist.splitscreen.percent, then broadcasts com.android.intent.ext.main.split.two.screen |
launches both apps as freeform windows at exact pixel bounds |
| needs | stock launcher as HOME, SELinux permissive | enable_freeform_support=1 (app toggles it) |
| looks | real system split, no title bars | freeform windows; title bars can appear if SystemUI starts with freeform enabled |
| survives app close | yes | yes (windows persist) |
Uncheck "Use the box's stock split" per split to use the freeform path.
persist.splitscreen.percent is a ratio of the whole display panel, while the app can
only measure its own window. On the tested box the panel is 2374 px and the app area
2270 px, so the same visible edge is:
| base | value | |
|---|---|---|
| Stock split | 2374 px panel | 63:37 → divider at 1495 px |
| Freeform | 2270 px app area | 66% → 1498 px |
Split Deck does that conversion for you: give it a percentage and it writes
targetPx:(panelW − targetPx) to the property.
This is the part that makes the stock split look "broken" on these boxes.
- The stock split only works when
com.android.launcher3is the default home. The broadcastcom.android.intent.ext.main.split.two.screenhas exactly one receiver —com.android.launcher3/.uioverrides.QuickstepLauncher, viaSplitScreenManager.launchExistingSplitPair. If another launcher (Smart Launcher, Nova, …) is HOME, that activity never runs, the receiver is never registered, and the broadcast is dropped without an error.adb shell cmd package set-home-activity com.android.launcher3/.uioverrides.QuickstepLauncher adb shell setprop persist.vendor.launcher.select com.android.launcher3/.uioverrides.QuickstepLauncher
- The launcher must be foreground when the broadcast lands. Split Deck therefore sends itself to the home screen and broadcasts ~1.9 s later.
- Both apps need running tasks. The vendor manager resolves each package among running tasks; a force-stopped app makes the split silently not form. Split Deck launches both apps first, then goes home, then broadcasts.
- The freeform engine has no launcher requirement — it positions windows itself — but
needs
enable_freeform_support=1. If SystemUI starts with that flag on it draws window title bars, so the app enables it for a launch and restores0afterwards.
| Device | Carlinkit Ultra AI Box ("lito for arm64", product sc162) |
| SoC | Qualcomm lito / SM6350 |
| Android | 15, build SC162-V06.000.002, vendor stack suding / speedplay |
| Display | 2374×1016 @30 Hz, 238 dpi — app area 2270×1016 (vendor status strip ~104 px) |
| Car | XPeng G6 (model F30), XOS CarPlay card, projection area 2400×1016 |
| SELinux | Permissive — required for the property write from an app |
| Root | adb shell is uid 0; no su for apps, and none needed |
Verified by observation on the device (2026-11-02):
- stock split launched from Split Deck — both apps in one split root task,
mode=multi, divider at exactly 1498 px; - freeform split at 1498×981 with 10 px overscan, no clipping;
- pinned shortcut (
dumpsys shortcut→Pinned: split-<uuid>) launches the split cold, after both apps were force-stopped; - name editing saves on the keyboard's Enter/Done.
Not verified: any other box, firmware or car. Treat those rows as "designed for", not "tested on".
- Android 8.0+ (
minSdk 26),targetSdk 33; no root. WRITE_SECURE_SETTINGSviaadb shell pm grant— only used to flipenable_freeform_support.- For the stock engine: the vendor launcher as HOME and SELinux permissive (a system-signed build would not need that).
- A firmware update that sets SELinux to enforcing stops the app writing the property → stock engine breaks; switch splits to freeform.
- No boot-time auto-relaunch: splits are restored by tapping a shortcut.
- The stock engine depends on vendor internals (private broadcast, private property); they can change silently.
- Only one device is tested (see above).
- The editor is a dialog; on very short screens the keyboard can crowd it (
Save & pinis left ofSave).
No Gradle. The app is four Java files and a manifest, built with the SDK command-line tools:
./build.sh # needs ANDROID_SDK_ROOT (default ~/Library/Android/sdk) and a JDK 17+Output: build/split-deck.apk. Signing uses a debug keystore generated on first build.
AndroidManifest.xml
src/com/stevelea/splitdeck/
MainActivity.java UI: list of splits, editor dialog, shortcuts
Launcher.java both engines: stock (property + broadcast) and freeform (bundle keys)
Split.java one saved split
Store.java JSON in SharedPreferences
CalView.java full-screen ruler used to measure the car's window
build.sh
docs/technical-notes.md
MIT — see LICENSE. Not affiliated with Carlinkit or XPeng. Use at your own risk: it writes a vendor system property and drives vendor internals.

