diff --git a/docs/credits_acknowledgements.rst b/docs/credits_acknowledgements.rst index 264af4941..f3d3d3776 100644 --- a/docs/credits_acknowledgements.rst +++ b/docs/credits_acknowledgements.rst @@ -108,6 +108,10 @@ The following libraries and components are incorporated into RenderDoc, listed h Used for converting capture files to and from xml. +* `AOSP `_ - Copyright (c) 2006-2016, The Android Open Source Project, distributed under the Apache 2.0 License. + + Used to simplify Android workflows by distributing some tools from the android SDK. + Thanks ------ diff --git a/qrenderdoc/Code/Interface/PersistantConfig.cpp b/qrenderdoc/Code/Interface/PersistantConfig.cpp index 85189dbe3..75fe8416f 100644 --- a/qrenderdoc/Code/Interface/PersistantConfig.cpp +++ b/qrenderdoc/Code/Interface/PersistantConfig.cpp @@ -191,11 +191,13 @@ void PersistantConfig::AddAndroidHosts() } } - QString adbExePath = - QFile::exists(Android_AdbExecutablePath) ? QString(Android_AdbExecutablePath) : QString(); + QString androidSDKPath = QFile::exists(Android_SDKPath) ? QString(Android_SDKPath) : QString(); - // Set the config setting as it will be reused when we start the remoteserver etc. - SetConfigSetting("adbExePath", adbExePath); + SetConfigSetting("androidSDKPath", androidSDKPath); + + QString androidJDKPath = QFile::exists(Android_JDKPath) ? QString(Android_JDKPath) : QString(); + + SetConfigSetting("androidJDKPath", androidJDKPath); SetConfigSetting("MaxConnectTimeout", QString::number(Android_MaxConnectTimeout)); diff --git a/qrenderdoc/Code/Interface/PersistantConfig.h b/qrenderdoc/Code/Interface/PersistantConfig.h index e6134301e..3504ab120 100644 --- a/qrenderdoc/Code/Interface/PersistantConfig.h +++ b/qrenderdoc/Code/Interface/PersistantConfig.h @@ -122,7 +122,9 @@ DECLARE_REFLECTION_STRUCT(SPIRVDisassembler); \ CONFIG_SETTING_VAL(public, bool, bool, Font_PreferMonospaced, false) \ \ - CONFIG_SETTING_VAL(public, QString, rdcstr, Android_AdbExecutablePath, "") \ + CONFIG_SETTING_VAL(public, QString, rdcstr, Android_SDKPath, "") \ + \ + CONFIG_SETTING_VAL(public, QString, rdcstr, Android_JDKPath, "") \ \ CONFIG_SETTING_VAL(public, int, int, Android_MaxConnectTimeout, 30) \ \ @@ -374,9 +376,17 @@ For more information about some of these settings that are user-facing see Defaults to ``False``. -.. data:: Android_AdbExecutablePath +.. data:: Android_SDKPath - The path to the ``adb`` executable to use for Android interaction. + The path to the root of the android SDK, to locate android tools to use for android remote hosts. + + Defaults to using the tools distributed with RenderDoc. + +.. data:: Android_JDKPath + + The path to the root of the Java JDK, to locate java for running android java tools. + + Defaults to using the JAVA_HOME environment variable, if set. .. data:: Android_MaxConnectTimeout diff --git a/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp b/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp index 6db7ac587..982089c48 100644 --- a/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp +++ b/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp @@ -84,7 +84,8 @@ SettingsDialog::SettingsDialog(ICaptureContext &ctx, QWidget *parent) ui->externalDisassemblerArgs->setText(m_Ctx.Config().SPIRVDisassemblers[0].args); ui->externalDisassemblePath->setText(m_Ctx.Config().SPIRVDisassemblers[0].executable); } - ui->Android_AdbExecutablePath->setText(m_Ctx.Config().Android_AdbExecutablePath); + 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); @@ -388,21 +389,52 @@ void SettingsDialog::on_browseTempCaptureDirectory_clicked() m_Ctx.Config().Save(); } -void SettingsDialog::on_browseAdbPath_clicked() +void SettingsDialog::on_browseAndroidSDKPath_clicked() { - QString adb = RDDialog::getExecutableFileName( - this, tr("Locate adb executable"), - QFileInfo(m_Ctx.Config().Android_AdbExecutablePath).absoluteDir().path()); + QString adb = RDDialog::getExistingDirectory( + this, tr("Locate SDK root folder (containing build-tools, platform-tools)"), + QFileInfo(m_Ctx.Config().Android_SDKPath).absoluteDir().path()); if(!adb.isEmpty()) { - ui->Android_AdbExecutablePath->setText(adb); - m_Ctx.Config().Android_AdbExecutablePath = adb; + ui->Android_SDKPath->setText(adb); + m_Ctx.Config().Android_SDKPath = adb; } m_Ctx.Config().Save(); } +void SettingsDialog::on_Android_SDKPath_textEdited(const QString &adb) +{ + if(QFileInfo::exists(adb) || adb.isEmpty()) + m_Ctx.Config().Android_SDKPath = adb; + + m_Ctx.Config().Save(); +} + +void SettingsDialog::on_browseAndroidJDKPath_clicked() +{ + QString adb = + RDDialog::getExistingDirectory(this, tr("Locate JDK root folder (containing bin, jre, lib)"), + QFileInfo(m_Ctx.Config().Android_JDKPath).absoluteDir().path()); + + if(!adb.isEmpty()) + { + ui->Android_JDKPath->setText(adb); + m_Ctx.Config().Android_JDKPath = adb; + } + + m_Ctx.Config().Save(); +} + +void SettingsDialog::on_Android_JDKPath_textEdited(const QString &adb) +{ + if(QFileInfo::exists(adb) || adb.isEmpty()) + m_Ctx.Config().Android_JDKPath = adb; + + m_Ctx.Config().Save(); +} + void SettingsDialog::on_Android_MaxConnectTimeout_valueChanged(double timeout) { m_Ctx.Config().Android_MaxConnectTimeout = ui->Android_MaxConnectTimeout->value(); @@ -410,14 +442,6 @@ void SettingsDialog::on_Android_MaxConnectTimeout_valueChanged(double timeout) m_Ctx.Config().Save(); } -void SettingsDialog::on_Android_AdbExecutablePath_textEdited(const QString &adb) -{ - if(QFileInfo::exists(adb) || adb.isEmpty()) - m_Ctx.Config().Android_AdbExecutablePath = adb; - - m_Ctx.Config().Save(); -} - void SettingsDialog::on_Android_AutoPushLayerToApp_toggled(bool checked) { m_Ctx.Config().Android_AutoPushLayerToApp = ui->Android_AutoPushLayerToApp->isChecked(); diff --git a/qrenderdoc/Windows/Dialogs/SettingsDialog.h b/qrenderdoc/Windows/Dialogs/SettingsDialog.h index 220eff2df..9e1591df7 100644 --- a/qrenderdoc/Windows/Dialogs/SettingsDialog.h +++ b/qrenderdoc/Windows/Dialogs/SettingsDialog.h @@ -87,9 +87,11 @@ private slots: // android void on_browseTempCaptureDirectory_clicked(); - void on_browseAdbPath_clicked(); + void on_browseAndroidSDKPath_clicked(); + void on_browseAndroidJDKPath_clicked(); void on_Android_MaxConnectTimeout_valueChanged(double timeout); - void on_Android_AdbExecutablePath_textEdited(const QString &path); + 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 diff --git a/qrenderdoc/Windows/Dialogs/SettingsDialog.ui b/qrenderdoc/Windows/Dialogs/SettingsDialog.ui index 402bd3e93..fb5d14a23 100644 --- a/qrenderdoc/Windows/Dialogs/SettingsDialog.ui +++ b/qrenderdoc/Windows/Dialogs/SettingsDialog.ui @@ -974,17 +974,24 @@ Only happens if the capture is not in the recent files list. Android + + + + Java JDK path + + + The location of adb.exe, used to control Android devices. - Android ADB executable path + Android SDK root path - + Qt::Vertical @@ -997,7 +1004,7 @@ 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. @@ -1008,7 +1015,7 @@ This can enable debugging of Vulkan apps that don't already contain the layer. - + Automatically push the RenderDoc layer to applications that need it when running on a device with root access. @@ -1016,7 +1023,7 @@ This can enable debugging of Vulkan apps that don't already contain the layer. - + Maximum time to try connecting to the target app. @@ -1026,7 +1033,7 @@ This can enable debugging of Vulkan apps that don't already contain the layer. - + Maximum time to try connecting to the target app. @@ -1040,14 +1047,14 @@ This can enable debugging of Vulkan apps that don't already contain the layer. - + The location of adb.exe, used to control Android devices. - + The location of adb.exe, used to control Android devices. @@ -1056,6 +1063,16 @@ This can enable debugging of Vulkan apps that don't already contain the layer. + + + + + + + Browse + + + diff --git a/renderdoc/core/android.cpp b/renderdoc/core/android.cpp index 042cb209c..268aa2725 100644 --- a/renderdoc/core/android.cpp +++ b/renderdoc/core/android.cpp @@ -30,6 +30,245 @@ namespace Android { +enum class ToolDir +{ + None, + Java, + BuildTools, + BuildToolsLib, + PlatformTools, +}; +static const char keystoreName[] = "renderdoc.keystore"; +bool toolExists(const std::string &path) +{ + if(path.empty()) + return false; + return FileIO::exists(path.c_str()) || FileIO::exists((path + ".exe").c_str()); +} +std::string getToolInSDK(ToolDir subdir, const std::string &jdkroot, const std::string &sdkroot, + const std::string &toolname) +{ + std::string toolpath; + + switch(subdir) + { + case ToolDir::None: + { + // This indicates the file is not a standard tool and will not exist anywhere but our + // distributed folder. + break; + } + case ToolDir::Java: + { + // if no path is configured, abort + if(jdkroot.empty()) + break; + + toolpath = jdkroot + "/bin/" + toolname; + + if(toolExists(toolpath)) + return toolpath; + + break; + } + case ToolDir::BuildTools: + case ToolDir::BuildToolsLib: + case ToolDir::PlatformTools: + { + // if no path is configured, abort + if(sdkroot.empty()) + break; + + // if it's in platform tools it's easy, just concatenate the path + if(subdir == ToolDir::PlatformTools) + { + toolpath = sdkroot + "/platform-tools/" + toolname; + } + else + { + // otherwise we need to find the build-tools versioned folder + toolpath = sdkroot + "/build-tools/"; + + std::vector paths = FileIO::GetFilesInDirectory(toolpath.c_str()); + + if(paths.empty()) + break; + + uint32_t bestversion = 0; + std::string bestpath; + + for(const PathEntry &path : paths) + { + // skip non-directories + if(!(path.flags & PathProperty::Directory)) + continue; + + uint32_t version = 0; + bool valid = true; + for(char c : path.filename) + { + // add digits to the version + if(c >= '0' && c <= '9') + { + int digit = int(c) - int('0'); + version *= 10; + version += digit; + continue; + } + + // ignore .s + if(c == '.') + continue; + + // if any char is not in [.0-9] then this filename is invalid + valid = false; + break; + } + + // skip non-valid directories + if(!valid) + continue; + + // if this directory is a higher version, prefer it + if(version > bestversion) + { + bestversion = version; + bestpath = path.filename; + } + } + + // if we didn't find a version at all, abort + if(bestversion == 0) + break; + + toolpath += bestpath + "/"; + + if(subdir == ToolDir::BuildToolsLib) + toolpath += "lib/"; + + toolpath += toolname; + } + + if(toolExists(toolpath)) + return toolpath; + + break; + } + } + + return ""; +} +struct ToolPathCache +{ + std::string sdk, jdk; + std::map paths; +} cache; +std::string getToolPath(ToolDir subdir, const std::string &toolname, bool checkExist) +{ + // search path for tools: + // 1. First look relative to the configured paths, these come from the user manually setting them + // so they always have priority. + // 2. Next if those paths don't exist or the tool isn't found, we search relative to our + // executable looking for an android/ subfolder, and look for the tool in there. + // 3. If we still don't have that (most likely because it's a local build from a git clone and not + // a distributed build with the tools available) then we fall back to trying to auto-locate it. + // - First check if the tool is in the path, assuming the user configured it to their system. + // - Otherwise check environment variables or default locations + + std::string sdk = RenderDoc::Inst().GetConfigSetting("androidSDKPath"); + std::string jdk = RenderDoc::Inst().GetConfigSetting("androidJDKPath"); + + // invalidate the cache when these settings change + if(sdk != cache.sdk || jdk != cache.jdk) + { + cache.paths.clear(); + cache.sdk = sdk; + cache.jdk = jdk; + } + + // if we have the path cached and it's still valid, return it + if(toolExists(cache.paths[toolname])) + return cache.paths[toolname]; + + std::string &toolpath = cache.paths[toolname]; + + // first try according to the configured paths + toolpath = getToolInSDK(subdir, jdk, sdk, toolname); + + if(toolExists(toolpath)) + return toolpath; + + // next try to locate it in our own distributed android subfolder + { + std::string exepath; + FileIO::GetExecutableFilename(exepath); + std::string exedir = dirname(FileIO::GetFullPathname(exepath)); + + toolpath = exedir + "/android/" + toolname; + if(toolExists(toolpath)) + return toolpath; + } + + // need to try to auto-guess the tool's location + + // first try in PATH + if(subdir != ToolDir::None) + { + toolpath = FileIO::FindFileInPath(toolname); + + if(toolExists(toolpath)) + return toolpath; + + // if the tool name contains a .jar then try stripping that and look for the non-.jar version in + // the PATH. + if(toolname.find(".jar") != std::string::npos) + { + toolpath = toolname; + toolpath.erase(toolpath.rfind(".jar"), 4); + toolpath = FileIO::FindFileInPath(toolpath); + + if(toolExists(toolpath)) + return toolpath; + } + } + + // now try to find it based on heuristics/environment variables + const char *env = Process::GetEnvVariable("JAVA_HOME"); + + jdk = env ? env : ""; + + env = Process::GetEnvVariable("ANDROID_HOME"); + sdk = env ? env : ""; + + if(sdk.empty() || !FileIO::exists(sdk.c_str())) + { + env = Process::GetEnvVariable("ANDROID_SDK_ROOT"); + sdk = env ? env : ""; + } + + if(sdk.empty() || !FileIO::exists(sdk.c_str())) + { + env = Process::GetEnvVariable("ANDROID_SDK"); + sdk = env ? env : ""; + } + + // maybe in future we can try to search in common install locations. + + toolpath = getToolInSDK(subdir, jdk, sdk, toolname); + + if(toolExists(toolpath)) + return toolpath; + + toolpath = ""; + + // if we're checking for existence, we have failed so return empty string. + if(checkExist) + return toolpath; + + // otherwise we at least return the tool name so that there's something to try and run + return toolname; +} + bool IsHostADB(const char *hostname) { return !strncmp(hostname, "adb:", 4); @@ -76,35 +315,14 @@ Process::ProcessResult execCommand(const string &exe, const string &args, } Process::ProcessResult adbExecCommand(const string &device, const string &args, const string &workDir) { - string adbExePath = RenderDoc::Inst().GetConfigSetting("adbExePath"); - if(adbExePath.empty()) - { - string exepath; - FileIO::GetExecutableFilename(exepath); - string exedir = dirname(FileIO::GetFullPathname(exepath)); - - string adbpath = exedir + "/android/adb.exe"; - if(FileIO::exists(adbpath.c_str())) - adbExePath = adbpath; - - if(adbExePath.empty()) - { - static bool warnPath = true; - if(warnPath) - { - RDCWARN("adbExePath not set, attempting to call 'adb' in working env"); - warnPath = false; - } - adbExePath = "adb"; - } - } + std::string adb = getToolPath(ToolDir::PlatformTools, "adb", false); Process::ProcessResult result; string deviceArgs; if(device.empty()) deviceArgs = args; else deviceArgs = StringFormat::Fmt("-s %s %s", device.c_str(), args.c_str()); - return execCommand(adbExePath, deviceArgs, workDir); + return execCommand(adb, deviceArgs, workDir); } string adbGetDeviceList() { @@ -175,8 +393,10 @@ bool RemoveAPKSignature(const string &apk) { RDCLOG("Checking for existing signature"); + std::string aapt = getToolPath(ToolDir::BuildTools, "aapt", false); + // Get the list of files in META-INF - string fileList = execCommand("aapt", "list " + apk).strStdout; + string fileList = execCommand(aapt, "list \"" + apk + "\"").strStdout; if(fileList.empty()) return false; @@ -193,7 +413,7 @@ bool RemoveAPKSignature(const string &apk) if(line.compare(0, prefix.size(), prefix) == 0) { RDCDEBUG("Match found, removing %s", line.c_str()); - execCommand("aapt", "remove " + apk + " " + line); + execCommand(aapt, "remove \"" + apk + "\" " + line); matchCount++; } } @@ -201,7 +421,7 @@ bool RemoveAPKSignature(const string &apk) // Ensure no hits on second pass through RDCDEBUG("Walk through file list again, ensure signature removed"); - fileList = execCommand("aapt", "list " + apk).strStdout; + fileList = execCommand(aapt, "list \"" + apk + "\"").strStdout; std::istringstream recheck(fileList); while(std::getline(recheck, line)) { @@ -219,10 +439,12 @@ bool AddLayerToAPK(const string &apk, const string &layerPath, const string &lay { 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); - Process::ProcessResult result = execCommand("aapt", "add " + apk + " " + relativeLayer, workDir); + Process::ProcessResult result = execCommand(aapt, "add \"" + apk + "\" " + relativeLayer, workDir); if(result.strStdout.empty()) { @@ -235,9 +457,12 @@ bool AddLayerToAPK(const string &apk, const string &layerPath, const string &lay bool RealignAPK(const string &apk, string &alignedAPK, const string &tmpDir) { + std::string zipalign = getToolPath(ToolDir::BuildTools, "zipalign", false); + // Re-align the APK for performance RDCLOG("Realigning APK"); - string errOut = execCommand("zipalign", "-f 4 " + apk + " " + alignedAPK, tmpDir).strStderror; + string errOut = + execCommand(zipalign, "-f 4 \"" + apk + "\" \"" + alignedAPK + "\"", tmpDir).strStderror; if(!errOut.empty()) return false; @@ -263,14 +488,22 @@ bool RealignAPK(const string &apk, string &alignedAPK, const string &tmpDir) string GetAndroidDebugKey() { - string key = FileIO::GetTempFolderFilename() + "debug.keystore"; + std::string keystore = getToolPath(ToolDir::None, keystoreName, false); + + // if we found the keystore, use that. + if(FileIO::exists(keystore.c_str())) + return keystore; + + // otherwise, see if we generated a temporary one + string key = FileIO::GetTempFolderFilename() + keystoreName; if(FileIO::exists(key.c_str())) return key; + // locate keytool and use it to generate a keystore string create; create += " -genkey"; - create += " -keystore " + key; + create += " -keystore \"" + key + "\""; create += " -storepass android"; create += " -alias androiddebugkey"; create += " -keypass android"; @@ -279,7 +512,9 @@ string GetAndroidDebugKey() create += " -validity 10000"; create += " -dname \"CN=, OU=, O=, L=, S=, C=\""; - Process::ProcessResult result = execCommand("keytool", create); + std::string keytool = getToolPath(ToolDir::Java, "keytool", false); + + Process::ProcessResult result = execCommand(keytool, create); if(!result.strStderror.empty()) RDCERR("Failed to create debug key"); @@ -290,19 +525,42 @@ bool DebugSignAPK(const string &apk, const string &workDir) { RDCLOG("Signing with debug key"); + std::string aapt = getToolPath(ToolDir::BuildTools, "aapt", false); + std::string apksigner = getToolPath(ToolDir::BuildToolsLib, "apksigner.jar", false); + string debugKey = GetAndroidDebugKey(); string args; args += " sign "; - args += " --ks " + debugKey + " "; + args += " --ks \"" + debugKey + "\" "; args += " --ks-pass pass:android "; args += " --key-pass pass:android "; args += " --ks-key-alias androiddebugkey "; - args += apk; - execScript("apksigner", args.c_str(), workDir.c_str()); + args += "\"" + apk + "\""; + + if(apksigner.find(".jar") == std::string::npos) + { + // if we found the non-jar version, then the jar wasn't located and we found the wrapper script + // in PATH. Execute it as a script + execScript(apksigner, args, workDir); + } + else + { + // otherwise, find and invoke java on the .jar + std::string java = getToolPath(ToolDir::Java, "java", false); + + std::string signerdir = dirname(FileIO::GetFullPathname(apksigner)); + + std::string javaargs; + javaargs += " \"-Djava.ext.dirs=" + signerdir + "\""; + javaargs += " -jar \"" + apksigner + "\""; + javaargs += args; + + execCommand(java, javaargs, workDir); + } // Check for signature - string list = execCommand("aapt", "list " + apk).strStdout; + string list = execCommand(aapt, "list \"" + apk + "\"").strStdout; // Walk through the output. If it starts with META-INF, we're good std::istringstream contents(list); @@ -353,7 +611,7 @@ bool ReinstallPatchedAPK(const string &deviceID, const string &apk, const string { RDCLOG("Reinstalling APK"); - adbExecCommand(deviceID, "install --abi " + abi + " " + apk, workDir); + adbExecCommand(deviceID, "install --abi " + abi + " \"" + apk + "\"", workDir); // Wait until re-install completes string reinstallResult; @@ -378,19 +636,41 @@ bool ReinstallPatchedAPK(const string &deviceID, const string &apk, const string bool CheckPatchingRequirements() { - // check for aapt, zipalign, apksigner, debug key - vector requirements; - vector missingTools; - requirements.push_back("aapt"); - requirements.push_back("zipalign"); - requirements.push_back("keytool"); - requirements.push_back("apksigner"); - requirements.push_back("java"); + // check for required tools for patching + std::vector> requirements; + std::vector missingTools; + requirements.push_back(std::make_pair(ToolDir::BuildTools, "aapt")); + requirements.push_back(std::make_pair(ToolDir::BuildTools, "zipalign")); + requirements.push_back(std::make_pair(ToolDir::BuildToolsLib, "apksigner.jar")); + requirements.push_back(std::make_pair(ToolDir::Java, "java")); for(uint32_t i = 0; i < requirements.size(); i++) { - if(FileIO::FindFileInPath(requirements[i]).empty()) - missingTools.push_back(requirements[i]); + std::string tool = getToolPath(requirements[i].first, requirements[i].second, true); + + // if we located the tool, we're fine. + if(toolExists(tool)) + continue; + + // didn't find it. + missingTools.push_back(requirements[i].second); + } + + // keytool is special - we look for a debug key first + { + std::string key = getToolPath(ToolDir::None, keystoreName, true); + + if(key.empty()) + { + // if we don't have the debug key, check that we can find keytool. First in our normal search + std::string tool = getToolPath(ToolDir::Java, "keytool", true); + + if(tool.empty()) + { + // if not, it's missing too + missingTools.push_back("keytool"); + } + } } if(missingTools.size() > 0) @@ -407,7 +687,7 @@ bool PullAPK(const string &deviceID, const string &pkgPath, const string &apk) { RDCLOG("Pulling APK to patch"); - adbExecCommand(deviceID, "pull " + pkgPath + " " + apk); + adbExecCommand(deviceID, "pull " + pkgPath + " \"" + apk + "\""); // Wait until the apk lands uint32_t elapsed = 0; @@ -438,7 +718,9 @@ bool CheckAPKPermissions(const string &apk) { RDCLOG("Checking that APK can be can write to sdcard"); - string badging = execCommand("aapt", "dump badging " + apk).strStdout; + std::string aapt = getToolPath(ToolDir::BuildTools, "aapt", false); + + string badging = execCommand(aapt, "dump badging \"" + apk + "\"").strStdout; if(badging.empty()) { @@ -452,7 +734,9 @@ bool CheckDebuggable(const string &apk) { RDCLOG("Checking that APK s debuggable"); - string badging = execCommand("aapt", "dump badging " + apk).strStdout; + std::string aapt = getToolPath(ToolDir::BuildTools, "aapt", false); + + string badging = execCommand(aapt, "dump badging \"" + apk + "\"").strStdout; if(badging.find("application-debuggable") == string::npos) { @@ -556,7 +840,7 @@ bool installRenderDocServer(const string &deviceID) { case Android_armeabi_v7a: case Android_arm64_v8a: - adbInstall = adbExecCommand(deviceID, "install -r -g " + serverApk).strStdout; + adbInstall = adbExecCommand(deviceID, "install -r -g \"" + serverApk + "\"").strStdout; break; case Android_armeabi: case Android_x86: @@ -781,7 +1065,7 @@ string FindAndroidLayer(const string &abi, const string &layerName) "%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.", - layer.c_str()); + layerName.c_str()); } return layer; diff --git a/scripts/dist.sh b/scripts/dist.sh index 523bc720f..307372ba7 100644 --- a/scripts/dist.sh +++ b/scripts/dist.sh @@ -51,22 +51,31 @@ cp -R plugins-win32/ dist/Release32/plugins find dist/Release{32,64}/ -iname '*.ipdb' -exec rm '{}' \; find dist/Release{32,64}/ -iname '*.iobj' -exec rm '{}' \; -if [ -f bin-android32/RenderDocCmd.apk ]; then +ANDROID32="bin-android32" +if [ -f build-android32/bin/RenderDocCmd.apk ]; then + ANDROID32="build-android32/bin"; +fi + +ANDROID64="bin-android64" +if [ -f build-android64/bin/RenderDocCmd.apk ]; then + ANDROID64="build-android64/bin"; +fi + +if [ -f $ANDROID32/RenderDocCmd.apk ]; then # Building for android, copy the apk and vulkan layer into folders mkdir -p dist/Release64/android/apk dist/Release64/android/lib/armeabi-v7a - cp bin-android32/RenderDocCmd.apk dist/Release64/android/apk - cp bin-android32/libVkLayer_GLES_RenderDoc.so dist/Release64/android/lib/armeabi-v7a/libVkLayer_GLES_RenderDoc.so + cp $ANDROID32/RenderDocCmd.apk dist/Release64/android/apk + cp $ANDROID32/libVkLayer_GLES_RenderDoc.so dist/Release64/android/lib/armeabi-v7a/libVkLayer_GLES_RenderDoc.so fi -if [ -f bin-android64/RenderDocCmd.apk ]; then - # We don't distribute the 64-bit apk, we use armeabi-v7a for both 32-bit and 64-bit - +if [ -f $ANDROID64/RenderDocCmd.apk ]; then # Building for android, copy the vulkan layer into folder mkdir -p dist/Release64/android/lib/arm64-v8a - #cp bin-android64/RenderDocCmd.apk dist/Release64/android/apk/64 - cp bin-android64/libVkLayer_GLES_RenderDoc.so dist/Release64/android/lib/arm64-v8a/libVkLayer_GLES_RenderDoc.so + # We don't distribute the 64-bit apk, we use armeabi-v7a for both 32-bit and 64-bit + #cp $ANDROID64/RenderDocCmd.apk dist/Release64/android/apk/64 + cp $ANDROID64/libVkLayer_GLES_RenderDoc.so dist/Release64/android/lib/arm64-v8a/libVkLayer_GLES_RenderDoc.so fi # try to copy adb.exe in as well, with its dll dependencies @@ -74,6 +83,28 @@ if [ -f $ANDROID_SDK/platform-tools/adb.exe ] && [ -d dist/Release64/android ]; cp $ANDROID_SDK/platform-tools/{adb.exe,AdbWinApi.dll,AdbWinUsbApi.dll} dist/Release64/android/ fi +# Copy tools needed for patching apks +if [ -d "$ANDROID_SDK/build-tools" ] && [ -d dist/Release64/android ]; then + BUILD_DIR=$(ls "$ANDROID_SDK/build-tools" | grep -E '^[0-9.]*$' | sort -n | tail -n 1) + + if [ -f "$ANDROID_SDK/build-tools/$BUILD_DIR/aapt.exe" ]; then + cp "$ANDROID_SDK/build-tools/$BUILD_DIR/aapt.exe" dist/Release64/android/ + fi + + if [ -f "$ANDROID_SDK/build-tools/$BUILD_DIR/zipalign.exe" ]; then + cp "$ANDROID_SDK/build-tools/$BUILD_DIR/zipalign.exe" dist/Release64/android/ + fi + + if [ -f "$ANDROID_SDK/build-tools/$BUILD_DIR/lib/apksigner.jar" ]; then + cp "$ANDROID_SDK/build-tools/$BUILD_DIR/lib/apksigner.jar" dist/Release64/android/ + fi +fi + +# Generate a debug key for signing purposes +if [ -f "$JAVA_HOME/bin/keytool.exe" ] && [ -d dist/Release64/android ]; then + keytool -genkey -keystore dist/Release64/android/renderdoc.keystore -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000 -dname "CN=, OU=, O=, L=, S=, C=" +fi + if [ -d dist/Release64/android ]; then cp -R dist/Release64/android dist/Release32/ fi diff --git a/scripts/installer/LICENSE.rtf b/scripts/installer/LICENSE.rtf index 2ae2e1826..27b690bfe 100644 --- a/scripts/installer/LICENSE.rtf +++ b/scripts/installer/LICENSE.rtf @@ -36,5 +36,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI {\field{\*\fldinst{HYPERLINK "{\pntext\f1\'B7\tab}https://github.com/facebook/zstd/"}}{\fldrslt{\ul\cf1 https://github.com/facebook/zstd/}}}\f0\fs22\line zstd distributed under the BSD License. Copyright 2016-present, Facebook, Inc.\par {\field{\*\fldinst{HYPERLINK "{\pntext\f1\'B7\tab}https://github.com/philsquared/Catch"}}{\fldrslt{\ul\cf1 https://github.com/philsquared/Catch}}}\f0\fs22\line Catch distributed under the Boost Software License. Copyright (c) 2012 Two Blue Cubes Ltd.\par {\field{\*\fldinst{HYPERLINK "{\pntext\f1\'B7\tab}https://pugixml.org"}}{\fldrslt{\ul\cf1 https://pugixml.org}}}\f0\fs22\line pugixml distributed under the MIT License. Copyright (c) 2006-2017 Arseny Kapoulkine.\par +{\field{\*\fldinst{HYPERLINK "{\pntext\f1\'B7\tab}https://source.android.com"}}{\fldrslt{\ul\cf1 https://source.android.com}}}\f0\fs22\line components of AOSP distributed under the Apache 2.0 License. Copyright (c) 2006-2016 The Android Open Source Project.\par }