diff --git a/qrenderdoc/Code/Interface/RemoteHost.cpp b/qrenderdoc/Code/Interface/RemoteHost.cpp index 9c6eb2993..3d8c52b6a 100644 --- a/qrenderdoc/Code/Interface/RemoteHost.cpp +++ b/qrenderdoc/Code/Interface/RemoteHost.cpp @@ -105,19 +105,23 @@ void RemoteHost::CheckStatus() QThread::msleep(15); } -void RemoteHost::Launch() +ReplayStatus RemoteHost::Launch() { + ReplayStatus status = ReplayStatus::Succeeded; + int WAIT_TIME = 2000; if(IsADB()) { - RENDERDOC_StartAndroidRemoteServer(hostname.c_str()); + status = RENDERDOC_StartAndroidRemoteServer(hostname.c_str()); QThread::msleep(WAIT_TIME); - return; + return status; } RDProcess process; process.start(runCommand); process.waitForFinished(WAIT_TIME); process.detach(); + + return status; } diff --git a/qrenderdoc/Code/Interface/RemoteHost.h b/qrenderdoc/Code/Interface/RemoteHost.h index dd05db504..5233caf04 100644 --- a/qrenderdoc/Code/Interface/RemoteHost.h +++ b/qrenderdoc/Code/Interface/RemoteHost.h @@ -48,8 +48,10 @@ public: DOCUMENT( "Ping the host to check current status - if the server is running, connection status, etc."); void CheckStatus(); - DOCUMENT("Runs the command specified in :data:`runCommand`."); - void Launch(); + DOCUMENT( + "Runs the command specified in :data:`runCommand`. Returns :class:`ReplayStatus` which " + "indicates success or the type of failure."); + ReplayStatus Launch(); DOCUMENT("``True`` if a remote server is currently running on this host."); bool serverRunning : 1; diff --git a/qrenderdoc/Code/pyrenderdoc/qrenderdoc_stub.cpp b/qrenderdoc/Code/pyrenderdoc/qrenderdoc_stub.cpp index 5083329cd..2a6c2ccb3 100644 --- a/qrenderdoc/Code/pyrenderdoc/qrenderdoc_stub.cpp +++ b/qrenderdoc/Code/pyrenderdoc/qrenderdoc_stub.cpp @@ -171,6 +171,7 @@ void RemoteHost::CheckStatus() { } -void RemoteHost::Launch() +ReplayStatus RemoteHost::Launch() { + return ReplayStatus::Succeeded; } diff --git a/qrenderdoc/Windows/MainWindow.cpp b/qrenderdoc/Windows/MainWindow.cpp index 9e1975c93..6c4fa2a9f 100644 --- a/qrenderdoc/Windows/MainWindow.cpp +++ b/qrenderdoc/Windows/MainWindow.cpp @@ -1843,7 +1843,11 @@ void MainWindow::switchContext() statusProgress->setMaximum(0); }); - host->Launch(); + ReplayStatus launchStatus = host->Launch(); + if(launchStatus != ReplayStatus::Succeeded) + { + showLaunchError(launchStatus); + } // check if it's running now host->CheckStatus(); @@ -2800,3 +2804,32 @@ bool MainWindow::LoadLayout(int layout) return false; } + +void MainWindow::showLaunchError(ReplayStatus status) +{ + QString title; + QString message; + switch(status) + { + case ReplayStatus::AndroidGrantPermissionsFailed: + title = tr("Permission is required"); + message = tr("Enable RenderDocCmd to access storage on your device."); + break; + case ReplayStatus::AndroidABINotFound: + title = tr("Failed to install RenderDoc server"); + message = tr("Couldn't determine supported ABIs."); + break; + case ReplayStatus::AndroidAPKFolderNotFound: + title = tr("Failed to install RenderDoc server"); + message = tr("APK folder missing."); + break; + case ReplayStatus::AndroidAPKInstallFailed: + title = tr("Failed to install RenderDoc server"); + message = tr("Couldn't find any installed APKs."); + default: + title = tr("Failed to install RenderDoc server"); + message = tr("Unknown error."); + break; + } + GUIInvoke::call(this, [this, title, message]() { RDDialog::warning(this, title, message); }); +} diff --git a/qrenderdoc/Windows/MainWindow.h b/qrenderdoc/Windows/MainWindow.h index b25212f53..adbfeedb5 100644 --- a/qrenderdoc/Windows/MainWindow.h +++ b/qrenderdoc/Windows/MainWindow.h @@ -248,4 +248,6 @@ private: bool SaveLayout(int layout); void FillRemotesMenu(QMenu *menu, bool includeLocalhost); + + void showLaunchError(ReplayStatus status); }; diff --git a/renderdoc/android/android.cpp b/renderdoc/android/android.cpp index c4ff5d260..1c9d56d09 100644 --- a/renderdoc/android/android.cpp +++ b/renderdoc/android/android.cpp @@ -323,14 +323,16 @@ ExecuteResult StartAndroidPackageForCapture(const char *host, const char *packag return ret; } -bool InstallRenderDocServer(const std::string &deviceID) +ReplayStatus InstallRenderDocServer(const std::string &deviceID) { + ReplayStatus status = ReplayStatus::Succeeded; + std::vector abis = GetSupportedABIs(deviceID); if(abis.empty()) { RDCERR("Couldn't determine supported ABIs for %s", deviceID.c_str()); - return false; + return ReplayStatus::AndroidABINotFound; } // Check known paths for RenderDoc server @@ -387,7 +389,7 @@ bool InstallRenderDocServer(const std::string &deviceID) "APK folder missing! RenderDoc for Android will not work without it. " "Build your Android ABI in build-android in the root to have it " "automatically found and installed."); - return false; + return ReplayStatus::AndroidAPKFolderNotFound; } for(ABI abi : abis) @@ -409,7 +411,15 @@ bool InstallRenderDocServer(const std::string &deviceID) "%s missing - ensure you build all ABIs your device can support for full compatibility", apk.c_str()); - adbExecCommand(deviceID, "install -r -g \"" + apk + "\""); + Process::ProcessResult adbCheck = adbExecCommand(deviceID, "install -r -g \"" + apk + "\""); + + if(!adbCheck.strStderror.empty()) + { + status = ReplayStatus::AndroidGrantPermissionsFailed; + RDCLOG("Failed to install APK. stderr: %s", adbCheck.strStderror.c_str()); + RDCLOG("Retrying..."); + adbExecCommand(deviceID, "install -r \"" + apk + "\""); + } } // Ensure installation succeeded. We should have as many lines as abis we installed @@ -419,7 +429,7 @@ bool InstallRenderDocServer(const std::string &deviceID) if(adbCheck.strStdout.empty()) { RDCERR("Couldn't find any installed APKs. stderr: %s", adbCheck.strStderror.c_str()); - return false; + return ReplayStatus::AndroidAPKInstallFailed; } size_t lines = adbCheck.strStdout.find('\n') == std::string::npos ? 1 : 2; @@ -427,7 +437,7 @@ bool InstallRenderDocServer(const std::string &deviceID) if(lines != abis.size()) RDCWARN("Installation of some apks failed!"); - return true; + return status; } bool RemoveRenderDocAndroidServer(const string &deviceID) @@ -601,15 +611,16 @@ extern "C" RENDERDOC_API bool RENDERDOC_CC RENDERDOC_IsAndroidSupported(const ch return Android::IsSupported(deviceID); } -extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_StartAndroidRemoteServer(const char *device) +extern "C" RENDERDOC_API ReplayStatus RENDERDOC_CC RENDERDOC_StartAndroidRemoteServer(const char *device) { + ReplayStatus status = ReplayStatus::Succeeded; int index = 0; std::string deviceID; Android::ExtractDeviceIDAndIndex(device, index, deviceID); if(!Android::IsSupported(deviceID)) - return; + return ReplayStatus::UnknownError; std::string packagesOutput = trim( Android::adbExecCommand(deviceID, "shell pm list packages " RENDERDOC_ANDROID_PACKAGE_BASE) @@ -623,10 +634,11 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_StartAndroidRemoteServer(co if(packages.size() != abis.size() || !Android::CheckAndroidServerVersion(deviceID)) { // If server is not detected or has been removed due to incompatibility, install it - if(!Android::InstallRenderDocServer(deviceID)) + status = Android::InstallRenderDocServer(deviceID); + if(status != ReplayStatus::Succeeded && status != ReplayStatus::AndroidGrantPermissionsFailed) { RDCERR("Failed to install RenderDoc server app"); - return; + return status; } } @@ -635,7 +647,7 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_StartAndroidRemoteServer(co Android::adbExecCommand(deviceID, "shell am force-stop " + GetRenderDocPackageForABI(abi)); if(abis.empty()) - return; + return ReplayStatus::AndroidABINotFound; Android::adbForwardPorts(index, deviceID, 0, 0, false); Android::ResetCaptureSettings(deviceID); @@ -643,4 +655,5 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_StartAndroidRemoteServer(co // launch the first ABI, as the default 'most compatible' package Android::adbExecCommand(deviceID, "shell am start -n " + GetRenderDocPackageForABI(abis[0]) + "/.Loader -e renderdoccmd remoteserver"); + return status; } diff --git a/renderdoc/android/android_patch.cpp b/renderdoc/android/android_patch.cpp index b14d4699a..34b92cad0 100644 --- a/renderdoc/android/android_patch.cpp +++ b/renderdoc/android/android_patch.cpp @@ -451,10 +451,22 @@ bool PullAPK(const string &deviceID, const string &pkgPath, const string &apk) elapsed += 1000; } - RDCERR("Failed to pull APK"); + RDCLOG("Failed to pull APK"); return false; } +void CopyAPK(const string &deviceID, const string &pkgPath, const string ©Path) +{ + RDCLOG("Copying APK to %s", copyPath.c_str()); + adbExecCommand(deviceID, "shell cp " + pkgPath + " " + copyPath); +} + +void RemoveAPK(const string &deviceID, const string &path) +{ + RDCLOG("Removing APK from %s", path.c_str()); + adbExecCommand(deviceID, "shell rm -f " + path); +} + bool HasRootAccess(const std::string &deviceID) { RDCLOG("Checking for root access on %s", deviceID.c_str()); @@ -572,7 +584,17 @@ extern "C" RENDERDOC_API AndroidFlags RENDERDOC_CC RENDERDOC_MakeDebuggablePacka // Try the following steps, bailing if anything fails if(!Android::PullAPK(deviceID, pkgPath, origAPK)) - return AndroidFlags::ManifestPatchFailure; + { + // Copy the APK to public storage, then try to pull again + std::string copyPath = "/sdcard/" + package + ".copy.apk"; + Android::CopyAPK(deviceID, pkgPath, copyPath); + bool success = Android::PullAPK(deviceID, copyPath, origAPK); + Android::RemoveAPK(deviceID, copyPath); + if(!success) + { + return AndroidFlags::ManifestPatchFailure; + } + } progress(0.4f); diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index d964a7b10..1561ddcec 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -2243,7 +2243,8 @@ DOCUMENT("Internal function for checking android support."); extern "C" RENDERDOC_API bool RENDERDOC_CC RENDERDOC_IsAndroidSupported(const char *device); DOCUMENT("Internal function for starting an android remote server."); -extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_StartAndroidRemoteServer(const char *device); +extern "C" RENDERDOC_API ReplayStatus RENDERDOC_CC +RENDERDOC_StartAndroidRemoteServer(const char *device); DOCUMENT("Internal function for checking remote Android package for requirements"); extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_CheckAndroidPackage(const char *hostname, diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index 08b2e2b17..a65b69202 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -3078,6 +3078,22 @@ a remote server. The API failed to replay the capture, with some runtime error that couldn't be determined until the replay began. + +.. data:: AndroidGrantPermissionsFailed + + Failed to grant runtime permissions when installing Android remote server. + +.. data:: AndroidABINotFound + + Couldn't determine supported ABIs when installing Android remote server. + +.. data:: AndroidAPKFolderNotFound + + Couldn't find the build-android folder which contains the Android remote server APK. + +.. data:: AndroidAPKInstallFailed + + Failed to install Android remote server. )"); enum class ReplayStatus : uint32_t { @@ -3101,6 +3117,10 @@ enum class ReplayStatus : uint32_t APIDataCorrupted, APIReplayFailed, JDWPFailure, + AndroidGrantPermissionsFailed, + AndroidABINotFound, + AndroidAPKFolderNotFound, + AndroidAPKInstallFailed }; DECLARE_REFLECTION_ENUM(ReplayStatus);