karagoz install
Installs an app from one APK file, or on an iOS simulator or iPhone from a .app or .ipa, or replaces the installed version of the same app.
- Android emulator Done
- Android device Done
- iOS simulator Done
- iOS device Done
karagoz install <apk> [--device <id>]Before you start
Section titled “Before you start”- On Android: platform-tools 30.0.0 or newer.
- On an iOS simulator: Xcode, opened once.
- On an iPhone: Xcode 15 or newer, paired with the Mac, Developer Mode on (Settings > Privacy & Security > Developer Mode).
Options
Section titled “Options”| Option | Default | Meaning |
|---|---|---|
<apk> |
Path to an .apk file, or for a simulator or an iPhone a .app directory or an .ipa file. A relative path is resolved against the current directory. One .apk only: split APKs, .apks and .aab are not supported. |
|
--device <id> |
see Device selection |
Examples
Section titled “Examples”karagoz install /Users/me/app/build/outputs/apk/debug/app-debug.apk{"device":"emulator-5554","path":"/Users/me/app/build/outputs/apk/debug/app-debug.apk"}exit 0Output
Section titled “Output”{ "device": "emulator-5554", "path": "/Users/me/app/build/outputs/apk/debug/app-debug.apk"}The package name is not reported.
devicestring- Serial, or the simulator's or iPhone's UDID.
pathstring- The absolute path of the app, as given (symlinks are not followed).
Errors
Section titled “Errors”| Code | When |
|---|---|
INVALID_ARGS | '<path>' is not an .apk, .app or .ipa file, no file at '<path>', '<path>' is not a file, or for a .app '<path>' is not a directory. Checked before any device call. Once the device is picked, and before anything is sent to it: '<path>' is an Android app; <udid> is an iOS simulator (is an iPhone on one) or '<path>' is an iOS app; <serial> is an Android device. |
INSTALL_FAILED | Android refused the APK. The message is adb's, and reason is Android's code, as in the example. |
ADB_FAILED | A failure without an Android code, such as Error: device is still booting. |
ADB_TIMEOUT | After 30 s plus 1 s for every started MB of the APK. On a phone, the install may be held on a prompt (Play Protect, or the maker's check for USB installs) that waits for a tap; if it is accepted later, the app still installs. |
INSTALL_FAILED codes seen on the emulator:
reason |
Cause |
|---|---|
INSTALL_FAILED_VERSION_DOWNGRADE |
A lower versionCode than the installed app. |
INSTALL_FAILED_UPDATE_INCOMPATIBLE |
Signed with another key. |
INSTALL_PARSE_FAILED_NOT_APK |
|
INSTALL_FAILED_DEPRECATED_SDK_VERSION |
targetSdkVersion below 24 on Android 16. |
On a phone, Android’s code can come from the phone’s own install check, with text that says someone cancelled when nobody did:
reason |
Message | Cause |
|---|---|---|
INSTALL_FAILED_VERIFICATION_FAILURE |
Play Protect refused the app. | |
INSTALL_FAILED_USER_RESTRICTED |
Install canceled by user |
Xiaomi, Redmi, POCO: “Install via USB” is off, or its prompt ran out. |
INSTALL_FAILED_ABORTED |
User rejected permissions |
vivo, iQOO: the same. |
Every install can also get the device selection errors and the adb errors.
- Runs
adb install -r --no-incremental. Platform-tools older than 30.0.0 forward--no-incrementalto the device, and the device rejects it. - There is no way to pass
-g(grant runtime permissions),-d(allow a downgrade) or-t. An APK markedtestOnly, which Android Studio’s Run button can produce, fails withINSTALL_FAILED_TEST_ONLY. - Android installs the app for every user on the device that allows adb installs; Secure Folder, Dual Messenger and work profiles get it too.
- On Android 17, installing over the app in front puts a system placeholder in its task. The app restarts 2 to 20 s after
installreturns. Alaunchin that window exits 0, reportscom.android.systemui/com.android.wm.shell.packageupdate.PackageUpdateActivityasactivityand starts nothing.
On an iOS simulator
Section titled “On an iOS simulator”Takes a .app bundle directory or an .ipa, built for the simulator, and runs simctl install.
{ "device": "2EC769C1-2EF2-47EB-A441-E6843968F039", "path": "/Users/me/Build/Products/Debug-iphonesimulator/Probe.app"}- A build for a physical iPhone is refused:
INSTALL_FAILEDwithreasonIXUserPresentableErrorDomain:4and simctl’s message, which says the executable has no code for the simulator. Other refusals carry simctl’s own<domain>:<code>, such asIXErrorDomain:13for a bundle withoutInfo.plist. A failure without one staysSIMCTL_FAILED. - A lower
CFBundleVersionthan the installed app is not refused. The app’s data is kept across reinstalls. SIMCTL_TIMEOUTafter 30 s plus 1 s for every started MB of the file; a.appgets 31 s.- Over an app stopped in a debugger (an Xcode breakpoint or pause), the install waits for the debugger. The call ends in
SIMCTL_TIMEOUT, and the app is installed once it continues or the debug session ends. Meanwhile other calls to that simulator can time out too.
On an iPhone
Section titled “On an iPhone”Takes a .app bundle directory or an .ipa built and signed for this iPhone, and runs devicectl device install app. Karagöz does not re-sign the app: build it for the phone in Xcode or Flutter, which sign it with a profile that includes this iPhone.
- The phone checks the signature first. A refusal is
INSTALL_FAILED, whosereasonis the phone’s own<domain>:<code>, the deepest error devicectl reports. The message names the cause, then says that Karagöz does not re-sign:- a simulator build:
MIInstallerErrorDomain:13with0xe8008014 (The executable contains an invalid signature.) - an unsigned app:
0xe800801c (No code signature found.) - a profile that does not include this iPhone:
0xe8008015or0xe8008012, from public reports (not measured here)
- a simulator build:
- A bundle without
Info.plistis refused on the Mac:com.apple.dt.CoreDeviceError:3000. - A reinstall replaces the version, a lower one too, and keeps the app’s data. Installing over the running app stops it. A locked iPhone installs too.
DEVICECTL_TIMEOUTafter 30 s plus 1 s for every started MB of the file; a.appgets 31 s.
MCP equivalent
Section titled “MCP equivalent”Arguments: apk (required), device. The names are the CLI's option and positional names.