diff --git a/qrenderdoc/Code/Interface/PersistantConfig.cpp b/qrenderdoc/Code/Interface/PersistantConfig.cpp index f5b380464..26956be2b 100644 --- a/qrenderdoc/Code/Interface/PersistantConfig.cpp +++ b/qrenderdoc/Code/Interface/PersistantConfig.cpp @@ -219,9 +219,6 @@ void PersistantConfig::AddAndroidHosts() SetConfigSetting("MaxConnectTimeout", QString::number(Android_MaxConnectTimeout)); - SetConfigSetting(lit("Android_AutoPushLayerToApp"), - Android_AutoPushLayerToApp ? lit("1") : lit("0")); - rdcstr androidHosts; RENDERDOC_EnumerateAndroidDevices(&androidHosts); for(const QString &hostName : diff --git a/qrenderdoc/Code/Interface/PersistantConfig.h b/qrenderdoc/Code/Interface/PersistantConfig.h index c53767ccc..b62571f85 100644 --- a/qrenderdoc/Code/Interface/PersistantConfig.h +++ b/qrenderdoc/Code/Interface/PersistantConfig.h @@ -219,8 +219,6 @@ DECLARE_REFLECTION_STRUCT(BugReport); \ CONFIG_SETTING_VAL(public, int, int, Android_MaxConnectTimeout, 30) \ \ - CONFIG_SETTING_VAL(public, bool, bool, Android_AutoPushLayerToApp, false) \ - \ CONFIG_SETTING_VAL(public, bool, bool, CheckUpdate_AllowChecks, true) \ \ CONFIG_SETTING_VAL(public, bool, bool, CheckUpdate_UpdateAvailable, false) \ @@ -495,12 +493,6 @@ For more information about some of these settings that are user-facing see Defaults to ``30``. -.. data:: Android_AutoPushLayerToApp - - Whether to automatically push the RenderDoc layer to the application's lib directory when running - on a device with root access. This can enable debugging of Vulkan applications that didn't already - package the layer in the APK. - .. data:: CheckUpdate_AllowChecks ``True`` if when coloring marker regions in the :class:`EventBrowser`, the whole row should be diff --git a/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp b/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp index b00b147ef..03a2a818a 100644 --- a/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp +++ b/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp @@ -428,11 +428,10 @@ void CaptureDialog::CheckAndroidSetup(QString &filename) rdcstr host = m_Ctx.Replay().CurrentRemote()->hostname; RENDERDOC_CheckAndroidPackage(host.c_str(), filename.toUtf8().data(), &m_AndroidFlags); - const bool missingLibrary = bool(m_AndroidFlags & AndroidFlags::MissingLibrary); - const bool missingPermissions = bool(m_AndroidFlags & AndroidFlags::MissingPermissions); - const bool wrongLayerVersion = bool(m_AndroidFlags & AndroidFlags::WrongLayerVersion); + const bool debuggable = bool(m_AndroidFlags & AndroidFlags::Debuggable); + const bool hasroot = bool(m_AndroidFlags & AndroidFlags::RootAccess); - if(missingLibrary || missingPermissions || wrongLayerVersion) + if(!debuggable && !hasroot) { // Check failed - set the warning visible GUIInvoke::call([this]() { @@ -442,7 +441,7 @@ void CaptureDialog::CheckAndroidSetup(QString &filename) } else { - // Check passed - no warnings needed + // Check passed, either app is debuggable or we have root - no warnings needed GUIInvoke::call([this]() { ui->androidScan->setVisible(false); ui->androidWarn->setVisible(false); @@ -458,192 +457,78 @@ void CaptureDialog::androidWarn_mouseClick() { QString exe = ui->exePath->text(); - QString caption = tr("Missing RenderDoc requirements"); + QString caption = tr("Application is not debuggable"); - QString msg = tr("In order to debug on Android, the following problems must be fixed:

"); + QString msg = tr(R"(In order to debug on Android, the package must be debuggable. +

+On UE4 you must disable for distribution, on Unity enable development mode. +

+RenderDoc can try to add the flag for you, which will involve completely reinstalling your package +as well as re-signing it with a debug key. This method is prone to error and is +not recommended. It is instead advised to configure your app to be debuggable at build time. +

+Would you like RenderDoc to try patching your package? +)"); - bool missingPermissions = bool(m_AndroidFlags & AndroidFlags::MissingPermissions); - bool missingLibrary = bool(m_AndroidFlags & AndroidFlags::MissingLibrary); - bool wrongLayerVersion = bool(m_AndroidFlags & AndroidFlags::WrongLayerVersion); - bool rootAccess = bool(m_AndroidFlags & AndroidFlags::RootAccess); + QMessageBox::StandardButton prompt = RDDialog::question(this, caption, msg, RDDialog::YesNoCancel); - if(missingPermissions) + if(prompt == QMessageBox::Yes) { - msg += - tr("Missing permissions
" - "The target APK must have the following permissions:
" - "android.permission.INTERNET

"); - } + float progress = 0.0f; + bool patchSucceeded = false; - if(missingLibrary) - { - msg += - tr("Missing library
" - "The RenderDoc library must be present in the " - "installed application.

"); - } + // call into APK pull, patch, install routine, then continue + LambdaThread *patch = new LambdaThread([this, exe, &patchSucceeded, &progress]() { + rdcstr host = m_Ctx.Replay().CurrentRemote()->hostname; + AndroidFlags result = RENDERDOC_MakeDebuggablePackage(host.c_str(), exe.toUtf8().data(), + [&progress](float p) { progress = p; }); - if(wrongLayerVersion) - { - msg += - tr("Wrong layer version
" - "The RenderDoc library was found, but its version " - "does not match that of the host.

"); - } - - if(missingPermissions) - { - // Don't prompt for patching if permissions are wrong - we can't fix that - RDDialog::critical(this, caption, msg); - return; - } - - // Track whether we tried to push layer directly, to influence text - bool triedPush = false; - - // Track whether to continue with push suggestion dialogue, in case user clicked Cancel - bool suggestPatch = true; - - if(rootAccess) - { - // Check whether user has requested automatic pushing - bool autoPushConfig = m_Ctx.Config().Android_AutoPushLayerToApp; - - // Separately, track whether the persistent checkBox is selected - bool autoPushCheckBox = autoPushConfig; - - QMessageBox::StandardButton prompt = QMessageBox::No; - - // Only display initial prompt if user has not chosen to push automatically - if(!autoPushConfig) - { - QString rootmsg = msg; - rootmsg += - tr("Your device appears to have root access. If you are only targeting Vulkan, " - "RenderDoc can try to push the layer directly to your application.

" - "Would you like RenderDoc to push the layer?
"); - - QString checkMsg(tr("Automatically push the layer on rooted devices")); - QCheckBox *cb = new QCheckBox(checkMsg, this); - cb->setChecked(autoPushCheckBox); - prompt = RDDialog::questionChecked(this, caption, rootmsg, cb, autoPushCheckBox, - RDDialog::YesNoCancel); - } - - if(autoPushConfig || prompt == QMessageBox::Yes) - { - bool pushSucceeded = false; - triedPush = true; - - // Only update the autoPush setting if Yes was clicked - if(autoPushCheckBox != m_Ctx.Config().Android_AutoPushLayerToApp) + if(result & AndroidFlags::Debuggable) { - m_Ctx.Config().Android_AutoPushLayerToApp = autoPushCheckBox; - m_Ctx.Config().Save(); - } + // Sucess! + patchSucceeded = true; - // Call into layer push routine, then continue - LambdaThread *push = new LambdaThread([this, exe, &pushSucceeded]() { - rdcstr host = m_Ctx.Replay().CurrentRemote()->hostname; - if(RENDERDOC_PushLayerToInstalledAndroidApp(host.c_str(), exe.toUtf8().data())) + RDDialog::information(this, tr("Patch succeeded!"), + tr("The patch process succeeded and the package is ready to debug")); + } + else + { + QString failmsg = tr("Something has gone wrong and the patching process failed.

"); + + if(result == AndroidFlags::MissingTools) { - // Sucess! - pushSucceeded = true; - - RDDialog::information( - this, tr("Push succeeded!"), - tr("The push attempt succeeded and
%1 now contains the RenderDoc layer").arg(exe)); + failmsg += + tr("Tools required for the process were not found. Try configuring the path to your " + "android SDK or java JDK in the settings dialog."); } - }); - - push->start(); - if(push->isRunning()) - { - ShowProgressDialog(this, tr("Pushing layer to %1, please wait...").arg(exe), - [push]() { return !push->isRunning(); }); - } - push->deleteLater(); - - if(pushSucceeded) - { - // We should be good from here, no futher prompts - suggestPatch = false; - ui->androidWarn->setVisible(false); - } - } - else if(prompt == QMessageBox::Cancel) - { - // Cancel skips any other fix prompts - suggestPatch = false; - } - } - - if(suggestPatch) - { - if(triedPush) - msg.insert(0, tr("The push attempt failed, so other methods must be used to fix the missing " - "layer.

")); - - msg += - tr("To fix this, you should repackage the APK following guidelines on the " - "" - "RenderDoc Wiki

" - "If you are only targeting Vulkan, RenderDoc can try to add the layer for you, " - "which requires pulling the APK, patching it, uninstalling the original, and " - "installing the modified version with a debug key. " - "This works for many debuggable applications, but not all, especially those that " - "check their integrity before launching.

" - "Your system will need several tools installed and available to RenderDoc. " - "Any missing tools will be noted in the log. Follow the steps " - "here" - " to get them.

" - "Would you like RenderDoc to try patching your APK?"); - - QMessageBox::StandardButton prompt = - RDDialog::question(this, caption, msg, RDDialog::YesNoCancel); - - if(prompt == QMessageBox::Yes) - { - float progress = 0.0f; - bool patchSucceeded = false; - - // call into APK pull, patch, install routine, then continue - LambdaThread *patch = new LambdaThread([this, exe, &patchSucceeded, &progress]() { - rdcstr host = m_Ctx.Replay().CurrentRemote()->hostname; - if(RENDERDOC_AddLayerToAndroidPackage(host.c_str(), exe.toUtf8().data(), - [&progress](float p) { progress = p; })) + else if(result == AndroidFlags::ManifestPatchFailure) { - // Sucess! - patchSucceeded = true; - - RDDialog::information( - this, tr("Patch succeeded!"), - tr("The patch process succeeded and
%1 now contains the RenderDoc layer").arg(exe)); + failmsg += + tr("The package manifest could not be patched. This is not solveable, you will have " + "to rebuild the package with the debuggable flag."); } - else + else if(result == AndroidFlags::RepackagingAPKFailure) { - RDDialog::critical(this, tr("Failed to patch APK"), - tr("Something has gone wrong and APK patching failed " - "for:
%1
Check diagnostic log in Help " - "menu for more details.") - .arg(exe)); + failmsg += tr("The package was patched but could not be repackaged and installed."); } - }); - patch->start(); - // wait a few ms before popping up a progress bar - patch->wait(500); - if(patch->isRunning()) - { - ShowProgressDialog(this, tr("Patching %1, please wait...").arg(exe), - [patch]() { return !patch->isRunning(); }, - [&progress]() { return progress; }); + RDDialog::critical(this, tr("Failed to patch package"), failmsg); } - patch->deleteLater(); + }); - if(patchSucceeded) - ui->androidWarn->setVisible(false); + patch->start(); + // wait a few ms before popping up a progress bar + patch->wait(500); + if(patch->isRunning()) + { + ShowProgressDialog(this, tr("Patching %1, please wait...").arg(exe), + [patch]() { return !patch->isRunning(); }, + [&progress]() { return progress; }); } + patch->deleteLater(); + + if(patchSucceeded) + ui->androidWarn->setVisible(false); } } diff --git a/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp b/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp index 5d5c59cad..83d4888d9 100644 --- a/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp +++ b/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp @@ -158,7 +158,6 @@ SettingsDialog::SettingsDialog(ICaptureContext &ctx, QWidget *parent) ui->Android_SDKPath->setText(m_Ctx.Config().Android_SDKPath); ui->Android_JDKPath->setText(m_Ctx.Config().Android_JDKPath); ui->Android_MaxConnectTimeout->setValue(m_Ctx.Config().Android_MaxConnectTimeout); - ui->Android_AutoPushLayerToApp->setChecked(m_Ctx.Config().Android_AutoPushLayerToApp); ui->TextureViewer_ResetRange->setChecked(m_Ctx.Config().TextureViewer_ResetRange); ui->TextureViewer_PerTexSettings->setChecked(m_Ctx.Config().TextureViewer_PerTexSettings); @@ -641,13 +640,6 @@ void SettingsDialog::on_Android_MaxConnectTimeout_valueChanged(double timeout) m_Ctx.Config().Save(); } -void SettingsDialog::on_Android_AutoPushLayerToApp_toggled(bool checked) -{ - m_Ctx.Config().Android_AutoPushLayerToApp = ui->Android_AutoPushLayerToApp->isChecked(); - - m_Ctx.Config().Save(); -} - void SettingsDialog::on_UIStyle_currentIndexChanged(int index) { if(index < 0 || index >= StyleData::numAvailable) diff --git a/qrenderdoc/Windows/Dialogs/SettingsDialog.h b/qrenderdoc/Windows/Dialogs/SettingsDialog.h index e3a809a8c..e167e7634 100644 --- a/qrenderdoc/Windows/Dialogs/SettingsDialog.h +++ b/qrenderdoc/Windows/Dialogs/SettingsDialog.h @@ -96,7 +96,6 @@ private slots: void on_Android_MaxConnectTimeout_valueChanged(double timeout); void on_Android_SDKPath_textEdited(const QString &path); void on_Android_JDKPath_textEdited(const QString &path); - void on_Android_AutoPushLayerToApp_toggled(bool checked); // manual slots void formatter_valueChanged(int value); diff --git a/qrenderdoc/Windows/Dialogs/SettingsDialog.ui b/qrenderdoc/Windows/Dialogs/SettingsDialog.ui index 8adc15e15..91faba70a 100644 --- a/qrenderdoc/Windows/Dialogs/SettingsDialog.ui +++ b/qrenderdoc/Windows/Dialogs/SettingsDialog.ui @@ -73,7 +73,7 @@ QTabWidget::West - 0 + 6 true @@ -1005,7 +1005,7 @@ Only happens if the capture is not in the recent files list. - + Qt::Vertical @@ -1018,25 +1018,6 @@ Only happens if the capture is not in the recent files list. - - - - Automatically push the RenderDoc layer to applications that need it when running on a device with root access. -This can enable debugging of Vulkan apps that don't already contain the layer. - - - Automatically push RenderDoc layer for Vulkan on devices with root access. - - - - - - - Automatically push the RenderDoc layer to applications that need it when running on a device with root access. -This can enable debugging of Vulkan apps that don't already contain the layer. - - - diff --git a/renderdoc/android/android_patch.cpp b/renderdoc/android/android_patch.cpp index 06ad1281e..badfe542c 100644 --- a/renderdoc/android/android_patch.cpp +++ b/renderdoc/android/android_patch.cpp @@ -86,50 +86,6 @@ bool RemoveAPKSignature(const string &apk) return true; } -bool AddLayerToAPK(const string &apk, const string &layerPath, const string &layerName, - const string &abi, const string &tmpDir) -{ - RDCLOG("Adding RenderDoc layer"); - - std::string aapt = getToolPath(ToolDir::BuildTools, "aapt", false); - - // Run aapt from the directory containing "lib" so the relative paths are good - string relativeLayer("lib/" + abi + "/" + layerName); - string workDir = removeFromEnd(layerPath, relativeLayer); - - // If the layer was already present in the APK, we need to remove it first - Process::ProcessResult contents = execCommand(aapt, "list \"" + apk + "\"", workDir); - if(contents.strStdout.empty()) - { - RDCERR("Failed to list contents of APK. STDERR: %s", contents.strStderror.c_str()); - return false; - } - - if(contents.strStdout.find(relativeLayer) != std::string::npos) - { - RDCLOG("Removing existing layer from APK before trying to add"); - Process::ProcessResult remove = - execCommand(aapt, "remove \"" + apk + "\" " + relativeLayer, workDir); - - if(!remove.strStdout.empty()) - { - RDCERR("Failed to remove existing layer from APK. STDERR: %s", remove.strStderror.c_str()); - return false; - } - } - - // Add the RenderDoc layer - Process::ProcessResult result = execCommand(aapt, "add \"" + apk + "\" " + relativeLayer, workDir); - - if(result.strStdout.empty()) - { - RDCERR("Failed to add layer to APK. STDERR: %s", result.strStderror.c_str()); - return false; - } - - return true; -} - bool RealignAPK(const string &apk, string &alignedAPK, const string &tmpDir) { std::string zipalign = getToolPath(ToolDir::BuildTools, "zipalign", false); @@ -383,265 +339,74 @@ bool PullAPK(const string &deviceID, const string &pkgPath, const string &apk) return false; } -bool CheckLayerVersion(const string &deviceID, const string &layerName, const string &remoteLayer) +bool HasRootAccess(const std::string &deviceID) { - RDCDEBUG("Checking layer version of: %s", layerName.c_str()); + RDCLOG("Checking for root access on %s", deviceID.c_str()); - bool match = false; + Process::ProcessResult result = {}; - // Use 'strings' command on the device to find the layer's build version - // i.e. strings -n | grep - // Subtract 5 to provide a bit of wiggle room on version length - Process::ProcessResult result = adbExecCommand( - deviceID, "shell strings -n " + - StringFormat::Fmt("%u", strlen(RENDERDOC_Version_Tag_String) - 5) + " " + - remoteLayer + " | grep RenderDoc_build_version"); + // Try switching adb to root and check a few indicators for success + // Nothing will fall over if we get a false positive here, it just enables + // additional methods of getting things set up. - string line = trim(result.strStdout); + result = adbExecCommand(deviceID, "root"); - if(line.empty()) + std::string whoami = trim(adbExecCommand(deviceID, "shell whoami").strStdout); + if(whoami == "root") + return true; + + std::string checksu = + trim(adbExecCommand(deviceID, "shell test -e /system/xbin/su && echo found").strStdout); + if(checksu == "found") + return true; + + return false; +} + +bool IsDebuggable(const std::string &deviceID, const std::string &packageName) +{ + RDCLOG("Checking that APK is debuggable"); + + std::string info = adbExecCommand(deviceID, "shell dumpsys package " + packageName).strStdout; + + size_t flagsOffset = info.find("pkgFlags=["); + + if(flagsOffset == std::string::npos) { - RDCLOG("RenderDoc layer is not versioned, so cannot be checked for compatibility."); + RDCERR("Couldn't get pkgFlags from adb"); return false; } - std::vector vec; - split(line, vec, ' '); - string version = vec[1]; - string hash = vec[5]; + size_t nextLine = info.find('\n', flagsOffset + 1); - if(version == FULL_VERSION_STRING && hash == GitVersionHash) - { - RDCLOG("RenderDoc layer version (%s) and git hash (%s) match.", version.c_str(), hash.c_str()); - match = true; - } - else - { - RDCLOG( - "RenderDoc layer version (%s) and git hash (%s) do NOT match the host version (%s) or git " - "hash (%s).", - version.c_str(), hash.c_str(), FULL_VERSION_STRING, GitVersionHash); - } + std::string pkgFlags = + info.substr(flagsOffset, nextLine == std::string::npos ? nextLine : nextLine - flagsOffset); - return match; -} - -bool CheckPermissions(const string &dump) -{ - // TODO: remove this if we are sure that there are no permissions to check. - return true; -} - -bool CheckAPKPermissions(const string &apk) -{ - RDCLOG("Checking that APK can be can write to sdcard"); - - std::string aapt = getToolPath(ToolDir::BuildTools, "aapt", false); - - string badging = execCommand(aapt, "dump badging \"" + apk + "\"").strStdout; - - if(badging.empty()) - { - RDCERR("Unable to aapt dump %s", apk.c_str()); - return false; - } - - return CheckPermissions(badging); -} - -bool CheckDebuggable(const string &apk) -{ - RDCLOG("Checking that APK s debuggable"); - - std::string aapt = getToolPath(ToolDir::BuildTools, "aapt", false); - - string badging = execCommand(aapt, "dump badging \"" + apk + "\"").strStdout; - - if(badging.find("application-debuggable") == string::npos) - { - RDCERR("APK is not debuggable"); - return false; - } - - return true; -} - -bool CheckInstalledPermissions(const string &deviceID, const string &packageName) -{ - RDCLOG("Checking installed permissions for %s", packageName.c_str()); - - string dump = adbExecCommand(deviceID, "shell pm dump " + packageName).strStdout; - if(dump.empty()) - RDCERR("Unable to pm dump %s", packageName.c_str()); - - return CheckPermissions(dump); -} - -string DetermineInstalledABI(const string &deviceID, const string &packageName) -{ - RDCLOG("Checking installed ABI for %s", packageName.c_str()); - string abi; - - string dump = adbExecCommand(deviceID, "shell pm dump " + packageName).strStdout; - if(dump.empty()) - RDCERR("Unable to pm dump %s", packageName.c_str()); - - // Walk through the output and look for primaryCpuAbi - std::istringstream contents(dump); - string line; - string prefix("primaryCpuAbi="); - while(std::getline(contents, line)) - { - line = trim(line); - if(line.compare(0, prefix.size(), prefix) == 0) - { - // Extract the abi - abi = line.substr(line.find_last_of("=") + 1); - RDCLOG("primaryCpuAbi found: %s", abi.c_str()); - break; - } - } - - if(abi.empty()) - RDCERR("Unable to determine installed abi for: %s", packageName.c_str()); - - return abi; -} - -string FindAndroidLayer(const string &abi, const string &layerName) -{ - string layer; - - // Check known paths for RenderDoc layer - string exePath; - FileIO::GetExecutableFilename(exePath); - string exeDir = dirname(FileIO::GetFullPathname(exePath)); - - std::vector paths; - -#if defined(RENDERDOC_LAYER_PATH) - string customPath(RENDERDOC_LAYER_PATH); - RDCLOG("Custom layer path: %s", customPath.c_str()); - - if(FileIO::IsRelativePath(customPath)) - customPath = exeDir + "/" + customPath; - - if(!endswith(customPath, "/")) - customPath += "/"; - - // Custom path must point to directory containing ABI folders - customPath += abi; - if(!FileIO::exists(customPath.c_str())) - { - RDCWARN("Custom layer path does not contain required ABI"); - } - paths.push_back(customPath + "/" + layerName); -#endif - - string windows = "/android/lib/"; - string linux = "/../share/renderdoc/android/lib/"; - string local = "/../../build-android/renderdoccmd/libs/lib/"; - string macOS = "/../../../../../build-android/renderdoccmd/libs/lib/"; - - paths.push_back(exeDir + windows + abi + "/" + layerName); - paths.push_back(exeDir + linux + abi + "/" + layerName); - paths.push_back(exeDir + local + abi + "/" + layerName); - paths.push_back(exeDir + macOS + abi + "/" + layerName); - - for(uint32_t i = 0; i < paths.size(); i++) - { - RDCLOG("Checking for layer in %s", paths[i].c_str()); - if(FileIO::exists(paths[i].c_str())) - { - layer = paths[i]; - RDCLOG("Layer found!: %s", layer.c_str()); - break; - } - } - - if(layer.empty()) - { - RDCERR( - "%s 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.", - layerName.c_str()); - } - - return layer; -} - -std::string GetPathForPackage(const std::string &deviceID, const std::string &packageName) -{ - std::string pkgPath = trim(adbExecCommand(deviceID, "shell pm path " + packageName).strStdout); - - if(pkgPath.empty() || pkgPath.find("package:") != 0 || pkgPath.find("base.apk") == std::string::npos) - return pkgPath; - - pkgPath.erase(pkgPath.begin(), pkgPath.begin() + strlen("package:")); - pkgPath.erase(pkgPath.end() - strlen("base.apk"), pkgPath.end()); - - return pkgPath; + return pkgFlags.find("DEBUGGABLE") != std::string::npos; } }; -using namespace Android; -extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_CheckAndroidPackage(const char *host, - const char *exe, +extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_CheckAndroidPackage(const char *hostname, + const char *packageName, AndroidFlags *flags) { - string packageName(basename(string(exe))); - int index = 0; std::string deviceID; - Android::ExtractDeviceIDAndIndex(host, index, deviceID); - - // Find the path to package - std::string pkgPath = Android::GetPathForPackage(deviceID, packageName) + "lib"; - - string layerName = "libVkLayer_GLES_RenderDoc.so"; + Android::ExtractDeviceIDAndIndex(hostname, index, deviceID); // Reset the flags each time we check *flags = AndroidFlags::NoFlags; - bool found = false; - string layerPath = ""; - - // Check a debug location only usable by rooted devices, overriding app's layer - if(SearchForAndroidLibrary(deviceID, "/data/local/debug/vulkan", layerName, layerPath)) - found = true; - - // See if the application contains the layer - if(!found && SearchForAndroidLibrary(deviceID, pkgPath, layerName, layerPath)) - found = true; - - // TODO: Add any future layer locations - - if(found) + if(Android::IsDebuggable(deviceID, basename(std::string(packageName)))) { -#if ENABLED(RDOC_DEVEL) - // Check the version of the layer found - if(!CheckLayerVersion(deviceID, layerName, layerPath)) - { - RDCWARN("RenderDoc layer found, but version does not match"); - *flags |= AndroidFlags::WrongLayerVersion; - } -#endif + *flags |= AndroidFlags::Debuggable; } else { - RDCWARN("No RenderDoc layer for Vulkan or GLES was found"); - *flags |= AndroidFlags::MissingLibrary; + RDCLOG("%s is not debuggable", packageName); } - // Next check permissions of the installed application (without pulling the APK) - if(!CheckInstalledPermissions(deviceID, packageName)) - { - RDCWARN("Android application does not have required permissions"); - *flags |= AndroidFlags::MissingPermissions; - } - - if(CheckRootAccess(deviceID)) + if(Android::HasRootAccess(deviceID)) { RDCLOG("Root access detected"); *flags |= AndroidFlags::RootAccess; @@ -650,124 +415,9 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_CheckAndroidPackage(const c return; } -extern "C" RENDERDOC_API bool RENDERDOC_CC RENDERDOC_PushLayerToInstalledAndroidApp(const char *host, - const char *exe) +extern "C" RENDERDOC_API AndroidFlags RENDERDOC_CC RENDERDOC_MakeDebuggablePackage( + const char *hostname, const char *packageName, RENDERDOC_ProgressCallback progress) { - Process::ProcessResult result = {}; - string packageName(basename(string(exe))); - - RDCLOG("Attempting to push RenderDoc layer to %s", packageName.c_str()); - - int index = 0; - std::string deviceID; - Android::ExtractDeviceIDAndIndex(host, index, deviceID); - - // Detect which ABI was installed on the device - string abi = DetermineInstalledABI(deviceID, packageName); - - // Find the layer on host - string layerName("libVkLayer_GLES_RenderDoc.so"); - string layerPath = FindAndroidLayer(abi, layerName); - if(layerPath.empty()) - return false; - - // Determine where to push the layer - string pkgPath = trim(adbExecCommand(deviceID, "shell pm path " + packageName).strStdout); - - // Isolate the app's lib dir - pkgPath.erase(pkgPath.begin(), pkgPath.begin() + strlen("package:")); - string libDir = removeFromEnd(pkgPath, "base.apk") + "lib/"; - - // There will only be one ABI in the lib dir - string libsAbi = trim(adbExecCommand(deviceID, "shell ls " + libDir).strStdout); - string layerDst = libDir + libsAbi + "/"; - result = adbExecCommand(deviceID, "push " + layerPath + " " + layerDst); - - // Ensure the push succeeded - string foundLayer; - return SearchForAndroidLibrary(deviceID, layerDst, layerName, foundLayer); -} - -extern "C" RENDERDOC_API bool RENDERDOC_CC RENDERDOC_AddLayerToAndroidPackage( - const char *host, const char *exe, RENDERDOC_ProgressCallback progress) -{ - Process::ProcessResult result = {}; - string packageName(basename(string(exe))); - - int index = 0; - std::string deviceID; - Android::ExtractDeviceIDAndIndex(host, index, deviceID); - - // make sure progress is valid so we don't have to check it everywhere - if(!progress) - progress = [](float) {}; - - progress(0.0f); - - if(!CheckPatchingRequirements()) - return false; - - progress(0.11f); - - // Detect which ABI was installed on the device - string abi = DetermineInstalledABI(deviceID, packageName); - - // Find the layer on host - string layerName("libVkLayer_GLES_RenderDoc.so"); - string layerPath = FindAndroidLayer(abi, layerName); - if(layerPath.empty()) - return false; - - // Find the APK on the device - std::string apkPath = Android::GetPathForPackage(deviceID, packageName) + "base.apk"; - - string tmpDir = FileIO::GetTempFolderFilename(); - string origAPK(tmpDir + packageName + ".orig.apk"); - string alignedAPK(origAPK + ".aligned.apk"); - - progress(0.21f); - - // Try the following steps, bailing if anything fails - if(!PullAPK(deviceID, pkgPath, origAPK)) - return false; - - progress(0.31f); - - if(!CheckAPKPermissions(origAPK)) - return false; - - progress(0.41f); - - if(!RemoveAPKSignature(origAPK)) - return false; - - progress(0.51f); - - if(!AddLayerToAPK(origAPK, layerPath, layerName, abi, tmpDir)) - return false; - - progress(0.61f); - - if(!RealignAPK(origAPK, alignedAPK, tmpDir)) - return false; - - progress(0.71f); - - if(!DebugSignAPK(alignedAPK, tmpDir)) - return false; - - progress(0.81f); - - if(!UninstallOriginalAPK(deviceID, packageName, tmpDir)) - return false; - - progress(0.91f); - - if(!ReinstallPatchedAPK(deviceID, alignedAPK, abi, packageName, tmpDir)) - return false; - - progress(1.0f); - - // All clean! - return true; + // stub for now + return AndroidFlags::ManifestPatchFailure; } diff --git a/renderdoc/android/android_utils.cpp b/renderdoc/android/android_utils.cpp index 8187a8561..982bbdc94 100644 --- a/renderdoc/android/android_utils.cpp +++ b/renderdoc/android/android_utils.cpp @@ -58,42 +58,17 @@ void ExtractDeviceIDAndIndex(const std::string &hostname, int &index, std::strin deviceID = c; } -bool CheckRootAccess(const std::string &deviceID) +std::string GetPathForPackage(const std::string &deviceID, const std::string &packageName) { - RDCLOG("Checking for root access on %s", deviceID.c_str()); + std::string pkgPath = trim(adbExecCommand(deviceID, "shell pm path " + packageName).strStdout); - Process::ProcessResult result = {}; + if(pkgPath.empty() || pkgPath.find("package:") != 0 || pkgPath.find("base.apk") == std::string::npos) + return pkgPath; - // Try switching adb to root and check a few indicators for success - // Nothing will fall over if we get a false positive here, it just enables - // additional methods of getting things set up. + pkgPath.erase(pkgPath.begin(), pkgPath.begin() + strlen("package:")); + pkgPath.erase(pkgPath.end() - strlen("base.apk"), pkgPath.end()); - result = adbExecCommand(deviceID, "root"); - - std::string whoami = trim(adbExecCommand(deviceID, "shell whoami").strStdout); - if(whoami == "root") - return true; - - std::string checksu = - trim(adbExecCommand(deviceID, "shell test -e /system/xbin/su && echo found").strStdout); - if(checksu == "found") - return true; - - return false; -} - -bool SearchForAndroidLibrary(const std::string &deviceID, const std::string &location, - const std::string &layerName, std::string &foundLayer) -{ - RDCLOG("Checking for layers in: %s", location.c_str()); - foundLayer = - trim(adbExecCommand(deviceID, "shell find " + location + " -name " + layerName).strStdout); - if(!foundLayer.empty()) - { - RDCLOG("Found RenderDoc layer in %s", location.c_str()); - return true; - } - return false; + return pkgPath; } std::string GetFriendlyName(std::string deviceID) diff --git a/renderdoc/android/android_utils.h b/renderdoc/android/android_utils.h index bd47116f8..86977b535 100644 --- a/renderdoc/android/android_utils.h +++ b/renderdoc/android/android_utils.h @@ -48,7 +48,4 @@ std::string getToolPath(ToolDir subdir, const std::string &toolname, bool checkE bool toolExists(const std::string &path); std::string GetFriendlyName(std::string deviceID); -bool CheckRootAccess(const std::string &deviceID); -bool SearchForAndroidLibrary(const std::string &deviceID, const std::string &location, - const std::string &layerName, std::string &foundLayer); }; diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index 54cb981e2..e4dd6fb47 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -2074,17 +2074,13 @@ DOCUMENT("Internal function for starting an android remote server."); extern "C" RENDERDOC_API void 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 *host, - const char *exe, +extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_CheckAndroidPackage(const char *hostname, + const char *packageName, AndroidFlags *flags); -DOCUMENT("Internal function that attempts to push Vulkan layer to Android application."); -extern "C" RENDERDOC_API bool RENDERDOC_CC RENDERDOC_PushLayerToInstalledAndroidApp(const char *host, - const char *exe); - -DOCUMENT("Internal function that attempts to modify APK contents, adding Vulkan layer."); -extern "C" RENDERDOC_API bool RENDERDOC_CC RENDERDOC_AddLayerToAndroidPackage( - const char *host, const char *exe, RENDERDOC_ProgressCallback progress); +DOCUMENT("Internal function that attempts to modify APK contents, adding debuggable flag."); +extern "C" RENDERDOC_API AndroidFlags RENDERDOC_CC RENDERDOC_MakeDebuggablePackage( + const char *hostname, const char *packageName, RENDERDOC_ProgressCallback progress); DOCUMENT("Internal function that runs unit tests."); extern "C" RENDERDOC_API int RENDERDOC_CC RENDERDOC_RunUnitTests(const rdcstr &command, diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index 48de2d96a..248365c1f 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -3529,41 +3529,34 @@ DOCUMENT(R"(A set of flags giving details of the current status of Android traca There are no problems with the Android application setup. -.. data:: MissingLibrary +.. data:: Debuggable - The RenderDoc library (whether Vulkan layer or OpenGLES library) could not be found in the - application or system locations. - -.. data:: MissingPermissions - - The application being checked does not have the requesite permission. Currently there - are no required permissions. - -.. data:: NotDebuggable - - The application is not debuggable. - -.. data:: WrongLayerVersion - - The found RenderDoc layer does not match the server's version. + The application is debuggable. .. data:: RootAccess The device being targeted has root access. -.. data:: Unfixable +.. data:: MissingTools - The current situation is not fixable automatically and requires user intervention/disambiguation. + When patching, some necessary tools were not found. + +.. data:: ManifestPatchFailure + + When patching, modifying the manifest file to include the debuggable flag failed. + +.. data:: RepackagingAPKFailure + + When patching, repackaging, signing and installing the new package failed. )"); enum class AndroidFlags : uint32_t { NoFlags = 0x0, - MissingLibrary = 0x1, - MissingPermissions = 0x2, - NotDebuggable = 0x4, - WrongLayerVersion = 0x8, - RootAccess = 0x10, - Unfixable = 0x20, + Debuggable = 0x1, + RootAccess = 0x2, + MissingTools = 0x1000, + ManifestPatchFailure = 0x2000, + RepackagingAPKFailure = 0x4000, }; BITMASK_OPERATORS(AndroidFlags);