diff --git a/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp b/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp
index 4d9af8594..4423ce911 100644
--- a/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp
+++ b/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp
@@ -619,74 +619,9 @@ void CaptureDialog::androidWarn_mouseClick()
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?
)");
- 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, host, exe, &patchSucceeded, &progress]() {
- AndroidFlags result =
- RENDERDOC_MakeDebuggablePackage(host, exe, [&progress](float p) { progress = p; });
-
- if(result & AndroidFlags::Debuggable)
- {
- // Sucess!
- patchSucceeded = true;
-
- 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)
- {
- 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.");
- }
- else if(result == AndroidFlags::ManifestPatchFailure)
- {
- 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 if(result == AndroidFlags::RepackagingAPKFailure)
- {
- failmsg += tr("The package was patched but could not be repackaged and installed.");
- }
-
- RDDialog::critical(this, tr("Failed to patch package"), failmsg);
- }
- });
-
- patch->setName(lit("Android patch"));
- 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);
- }
+ RDDialog::information(this, caption, msg);
}
void CaptureDialog::lineEdit_keyPress(QKeyEvent *ev)
diff --git a/renderdoc/3rdparty/aosp/android_manifest.h b/renderdoc/3rdparty/aosp/android_manifest.h
deleted file mode 100644
index 8677752c9..000000000
--- a/renderdoc/3rdparty/aosp/android_manifest.h
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include
-
-//////////////////////////////////////////////////////////////////////////////////
-//
-// These constants are taken from ResourceTypes.h:
-// https://android.googlesource.com/platform/frameworks/base/+/42ebcb80b50834a1ce4755cd4ca86918c96ca3c6/libs/androidfw/include/androidfw/ResourceTypes.h
-//
-// The ones we need are extracted here to not be dependent on the rest of the android framework.
-// There are some slight modifications for ease of integration, but this is still all under the
-// android license.
-
-enum class ResType : uint16_t
-{
- Null = 0x0000,
- StringPool = 0x0001,
- XML = 0x0003,
- NamespaceStart = 0x0100,
- NamespaceEnd = 0x0101,
- StartElement = 0x0102,
- EndElement = 0x0103,
- CDATA = 0x0104,
- ResourceMap = 0x0180,
-};
-
-/**
- * Header that appears at the front of every data chunk in a resource.
- */
-struct ResChunk_header
-{
- // Type identifier for this chunk. The meaning of this value depends
- // on the containing chunk.
- ResType type;
-
- // Size of the chunk header (in bytes). Adding this value to
- // the address of the chunk allows you to find its associated data
- // (if any).
- uint16_t headerSize;
-
- // Total size of this chunk (in bytes). This is the chunkSize plus
- // the size of any data associated with the chunk. Adding this value
- // to the chunk allows you to completely skip its contents (including
- // any child chunks). If this value is the same as chunkSize, there is
- // no data associated with the chunk.
- uint32_t size;
-};
-
-/**
- * Reference to a string in a string pool.
- */
-struct ResStringPool_ref
-{
- // Index into the string pool table (uint32_t-offset from the indices
- // immediately after ResStringPool_header) at which to find the location
- // of the string data in the pool.
- uint32_t index;
-};
-
-/**
- * Representation of a value in a resource, supplying type
- * information.
- */
-struct Res_value
-{
- // Number of bytes in this structure.
- uint16_t size;
-
- // Always set to 0.
- uint8_t res0;
-
- // Type of the data value.
- enum class DataType : uint8_t
- {
- // The 'data' holds an index into the containing resource table's
- // global value string pool.
- String = 0x03,
- // The 'data' is either 0 or 1, for input "false" or "true" respectively.
- Boolean = 0x12,
- } dataType;
-
- // The data for this item, as interpreted according to dataType.
- uint32_t data;
-};
-
-/**
- * Definition for a pool of strings. The data of this chunk is an
- * array of uint32_t providing indices into the pool, relative to
- * stringsStart. At stringsStart are all of the UTF-16 strings
- * concatenated together; each starts with a uint16_t of the string's
- * length and each ends with a 0x0000 terminator. If a string is >
- * 32767 characters, the high bit of the length is set meaning to take
- * those 15 bits as a high word and it will be followed by another
- * uint16_t containing the low word.
- *
- * If styleCount is not zero, then immediately following the array of
- * uint32_t indices into the string table is another array of indices
- * into a style table starting at stylesStart. Each entry in the
- * style table is an array of ResStringPool_span structures.
- */
-struct ResStringPool_header
-{
- struct ResChunk_header header;
-
- // Number of strings in this pool (number of uint32_t indices that follow
- // in the data).
- uint32_t stringCount;
-
- // Number of style span arrays in the pool (number of uint32_t indices
- // follow the string indices).
- uint32_t styleCount;
-
- // Flags.
- enum StringFlags : uint32_t
- {
- // If set, the string index is sorted by the string values (based
- // on strcmp16()).
- SORTED_FLAG = 1 << 0,
-
- // String pool is encoded in UTF-8
- UTF8_FLAG = 1 << 8
- };
- StringFlags flags;
-
- // Index from header of the string data.
- uint32_t stringsStart;
-
- // Index from header of the style data.
- uint32_t stylesStart;
-};
-
-/**
- * Basic XML tree node. A single item in the XML document. Extended info
- * about the node can be found after header.headerSize.
- */
-struct ResXMLTree_node
-{
- struct ResChunk_header header;
-
- // Line number in original source file at which this element appeared.
- uint32_t lineNumber;
-
- // Optional XML comment that was associated with this element; -1 if none.
- ResStringPool_ref comment;
-};
-
-/**
- * Extended XML tree node for namespace start/end nodes.
- * Appears header.headerSize bytes after a ResXMLTree_node.
- */
-struct ResXMLTree_namespaceExt
-{
- // The prefix of the namespace.
- struct ResStringPool_ref prefix;
-
- // The URI of the namespace.
- struct ResStringPool_ref uri;
-};
-
-/**
- * Extended XML tree node for element start/end nodes.
- * Appears header.headerSize bytes after a ResXMLTree_node.
- */
-struct ResXMLTree_endElementExt
-{
- // String of the full namespace of this element.
- struct ResStringPool_ref ns;
-
- // String name of this node if it is an ELEMENT; the raw
- // character data if this is a CDATA node.
- struct ResStringPool_ref name;
-};
-
-/**
- * Extended XML tree node for start tags -- includes attribute
- * information.
- * Appears header.headerSize bytes after a ResXMLTree_node.
- */
-struct ResXMLTree_attrExt
-{
- // String of the full namespace of this element.
- struct ResStringPool_ref ns;
-
- // String name of this node if it is an ELEMENT; the raw
- // character data if this is a CDATA node.
- struct ResStringPool_ref name;
-
- // Byte offset from the start of this structure where the attributes start.
- uint16_t attributeStart;
-
- // Size of the ResXMLTree_attribute structures that follow.
- uint16_t attributeSize;
-
- // Number of attributes associated with an ELEMENT. These are
- // available as an array of ResXMLTree_attribute structures
- // immediately following this node.
- uint16_t attributeCount;
-
- // Index (1-based) of the "id" attribute. 0 if none.
- uint16_t idIndex;
-
- // Index (1-based) of the "class" attribute. 0 if none.
- uint16_t classIndex;
-
- // Index (1-based) of the "style" attribute. 0 if none.
- uint16_t styleIndex;
-};
-
-struct ResXMLTree_attribute
-{
- // Namespace of this attribute.
- struct ResStringPool_ref ns;
-
- // Name of this attribute.
- struct ResStringPool_ref name;
-
- // The original raw string value of this attribute.
- struct ResStringPool_ref rawValue;
-
- // Processesd typed value of this attribute.
- struct Res_value typedValue;
-};
-
-/**
- * Extended XML tree node for CDATA tags -- includes the CDATA string.
- * Appears header.headerSize bytes after a ResXMLTree_node.
- */
-struct ResXMLTree_cdataExt
-{
- // The raw CDATA character data.
- struct ResStringPool_ref data;
-
- // The typed value of the character data if this is a CDATA node.
- struct Res_value typedData;
-};
diff --git a/renderdoc/CMakeLists.txt b/renderdoc/CMakeLists.txt
index a26438d76..887895e56 100644
--- a/renderdoc/CMakeLists.txt
+++ b/renderdoc/CMakeLists.txt
@@ -150,10 +150,8 @@ set(sources
core/bit_flag_iterator.h
core/bit_flag_iterator_tests.cpp
android/android.cpp
- android/android_patch.cpp
android/android_tools.cpp
android/android_utils.cpp
- android/android_manifest.cpp
android/android.h
android/android_utils.h
android/jdwp.h
@@ -218,7 +216,6 @@ set(sources
3rdparty/jpeg-compressor/jpgd.h
3rdparty/jpeg-compressor/jpge.cpp
3rdparty/jpeg-compressor/jpge.h
- 3rdparty/aosp/android_manifest.h
3rdparty/catch/catch.cpp
3rdparty/catch/catch.hpp
3rdparty/pugixml/pugixml.cpp
diff --git a/renderdoc/android/android.cpp b/renderdoc/android/android.cpp
index 7276cbc24..43cd2dcc6 100644
--- a/renderdoc/android/android.cpp
+++ b/renderdoc/android/android.cpp
@@ -1478,3 +1478,31 @@ ExecuteResult AndroidRemoteServer::ExecuteAndInject(const rdcstr &packageAndActi
AndroidController AndroidController::m_Inst;
DeviceProtocolRegistration androidProtocol("adb", &AndroidController::Get);
+
+extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_CheckAndroidPackage(
+ const rdcstr &URL, const rdcstr &packageAndActivity, AndroidFlags *flags)
+{
+ IDeviceProtocolHandler *adb = RenderDoc::Inst().GetDeviceProtocol("adb");
+
+ rdcstr deviceID = adb->GetDeviceID(URL);
+
+ // Reset the flags each time we check
+ *flags = AndroidFlags::NoFlags;
+
+ if(Android::IsDebuggable(deviceID, Android::GetPackageName(packageAndActivity)))
+ {
+ *flags |= AndroidFlags::Debuggable;
+ }
+ else
+ {
+ RDCLOG("%s is not debuggable", packageAndActivity.c_str());
+ }
+
+ if(Android::HasRootAccess(deviceID))
+ {
+ RDCLOG("Root access detected");
+ *flags |= AndroidFlags::RootAccess;
+ }
+
+ return;
+}
diff --git a/renderdoc/android/android_manifest.cpp b/renderdoc/android/android_manifest.cpp
deleted file mode 100644
index 5a0dfedf7..000000000
--- a/renderdoc/android/android_manifest.cpp
+++ /dev/null
@@ -1,630 +0,0 @@
-/******************************************************************************
- * The MIT License (MIT)
- *
- * Copyright (c) 2019-2022 Baldur Karlsson
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- ******************************************************************************/
-
-#include "aosp/android_manifest.h"
-#include "core/core.h"
-#include "strings/string_utils.h"
-#include "android_utils.h"
-
-const uint32_t debuggableResourceId = 0x0101000f;
-const uint32_t addingStringIndex = 0x8b8b8b8b;
-
-// the string pool always immediately follows the XML header, which is just an empty header.
-const size_t stringpoolOffset = sizeof(ResChunk_header);
-
-namespace Android
-{
-template
-void SetFromBytes(bytebuf &bytes, size_t offs, const T &t)
-{
- T *ptr = (T *)(bytes.data() + offs);
- memcpy(ptr, &t, sizeof(T));
-}
-
-template
-T GetFromBytes(bytebuf &bytes, size_t offs)
-{
- T ret;
- T *ptr = (T *)(bytes.data() + offs);
- memcpy(&ret, ptr, sizeof(T));
- return ret;
-}
-
-template
-void InsertBytes(bytebuf &bytes, size_t offs, const T &data)
-{
- byte *byteData = (byte *)&data;
-
- bytes.insert(offs, byteData, sizeof(T));
-}
-
-template <>
-void InsertBytes(bytebuf &bytes, size_t offs, const bytebuf &data)
-{
- bytes.insert(offs, data);
-}
-
-rdcstr GetStringPoolValue(bytebuf &bytes, ResStringPool_ref ref)
-{
- ResStringPool_header stringpool = GetFromBytes(bytes, stringpoolOffset);
-
- byte *base = bytes.data() + stringpoolOffset;
-
- uint32_t stringCount = stringpool.stringCount;
- uint32_t *stringOffsets = (uint32_t *)(base + stringpool.header.headerSize);
- byte *stringData = base + stringpool.stringsStart;
-
- if(ref.index == ~0U)
- return "";
-
- if(ref.index >= stringCount)
- return "__invalid_string__";
-
- byte *strdata = stringData + stringOffsets[ref.index];
-
- // strdata now points at len characters of string. Check if it's UTF-8 or UTF-16
- if((stringpool.flags & ResStringPool_header::UTF8_FLAG) == 0)
- {
- uint16_t *str = (uint16_t *)strdata;
-
- uint32_t len = *(str++);
-
- // see comment above ResStringPool_header - if high bit is set, then this string is >32767
- // characters, so it's followed by another uint16_t with the low word
- if(len & 0x8000)
- {
- len &= 0x7fff;
- len <<= 16;
- len |= *(str++);
- }
-
- rdcwstr wstr(len);
-
- // wchar_t isn't always 2 bytes, so we iterate over the uint16_t and cast.
- for(uint32_t i = 0; i < len; i++)
- wstr[i] = wchar_t(str[i]);
-
- return StringFormat::Wide2UTF8(wstr);
- }
- else
- {
- byte *str = (byte *)strdata;
-
- uint32_t len = *(str++);
-
- // the length works similarly for UTF-8 data but with single bytes instead of uint16s.
- if(len & 0x80)
- {
- len &= 0x7f;
- len <<= 8;
- len |= *(str++);
- }
-
- // the length is encoded twice. I can only assume to preserve uint16 size although I don't see
- // why that would be necessary - it can't be fully backwards compatible even with the alignment
- // except with readers that ignore the length entirely and look for trailing NULLs.
- if(len < 0x80)
- str++;
- else
- str += 2;
-
- return rdcstr((char *)str, len);
- }
-}
-
-void ShiftStringPoolValue(ResStringPool_ref &ref, uint32_t insertedLocation)
-{
- // if we found our added attribute, then set the index here (otherwise we'd remap it with the
- // others!)
- if(ref.index == addingStringIndex)
- ref.index = insertedLocation;
- else if(ref.index != ~0U && ref.index >= insertedLocation)
- ref.index++;
-}
-
-void ShiftStringPoolValue(Res_value &val, uint32_t insertedLocation)
-{
- if(val.dataType == Res_value::DataType::String && val.data >= insertedLocation)
- val.data++;
-}
-
-bool PatchManifest(bytebuf &manifestBytes)
-{
- if(manifestBytes.size() < sizeof(ResChunk_header))
- {
- RDCERR("Manifest is truncated, %zu bytes doesn't contain full XML header", manifestBytes.size());
- return false;
- }
-
- size_t cur = 0;
-
- ResChunk_header xmlroot = GetFromBytes(manifestBytes, cur);
-
- if(xmlroot.type != ResType::XML)
- {
- RDCERR("XML Header is malformed, type is %u expected %u", xmlroot.type, ResType::XML);
- return false;
- }
-
- if(xmlroot.headerSize != sizeof(xmlroot))
- {
- RDCERR("XML Header is malformed, header size is reported as %u but expected %u",
- xmlroot.headerSize, sizeof(xmlroot));
- return false;
- }
-
- // this isn't necessarily fatal, but it is unexpected.
- if(xmlroot.size != manifestBytes.size())
- RDCWARN("XML header is malformed, size is reported as %u but %zu bytes found", xmlroot.size,
- manifestBytes.size());
-
- cur += xmlroot.headerSize;
-
- ResStringPool_header stringpool = GetFromBytes(manifestBytes, cur);
-
- if(stringpool.header.type != ResType::StringPool)
- {
- RDCERR("Manifest format is unsupported, expected string pool but got %u", stringpool.header.type);
- return false;
- }
-
- if(stringpool.header.headerSize != sizeof(stringpool))
- {
- RDCERR("String pool is malformed, header size is reported as %u but expected %u",
- stringpool.header.headerSize, sizeof(stringpool));
- return false;
- }
-
- if(cur + stringpool.header.size > manifestBytes.size())
- {
- RDCERR("String pool is truncated, expected %u more bytes but only have %u",
- stringpool.header.size, manifestBytes.size() - cur);
- return false;
- }
-
- cur += stringpool.header.size;
-
- ResChunk_header resMap = GetFromBytes(manifestBytes, cur);
- const size_t resMapOffset = cur;
-
- if(resMap.type != ResType::ResourceMap)
- {
- RDCERR("Manifest format is unsupported, expected resource table but got %u", resMap.type);
- return false;
- }
-
- if(resMap.headerSize != sizeof(resMap))
- {
- RDCERR("Resource map is malformed, header size is reported as %u but expected %u",
- resMap.headerSize, sizeof(resMap));
- return false;
- }
-
- if(cur + resMap.size > manifestBytes.size())
- {
- RDCERR("Resource map is truncated, expected %u more bytes but only have %u", resMap.size,
- manifestBytes.size() - cur);
- return false;
- }
-
- const uint32_t resourceMappingCount = (resMap.size - resMap.headerSize) / sizeof(uint32_t);
- const rdcarray resourceMapping(
- (const uint32_t *)(manifestBytes.data() + cur + resMap.headerSize), resourceMappingCount);
-
- cur += resMap.size;
-
- bool stringAdded = false;
-
- // now chunks will come along. There will likely first be a namespace begin, then XML tag open and
- // close. Since the tag is only valid in one place in the XML we can just continue
- // iterating until we find it - we don't actually need to care about the structure of the XML
- // since we are identifying a unique tag and adding one attribute.
- while(cur < manifestBytes.size())
- {
- ResChunk_header node = GetFromBytes(manifestBytes, cur);
-
- if(node.type != ResType::StartElement)
- {
- cur += node.size;
- continue;
- }
-
- ResXMLTree_attrExt startElement =
- GetFromBytes(manifestBytes, cur + node.headerSize);
-
- rdcstr name = GetStringPoolValue(manifestBytes, startElement.name);
-
- if(name != "application")
- {
- cur += node.size;
- continue;
- }
-
- // found the application tag! Now search its attribtues to see if it already has a debuggable
- // attribute (that might be set explicitly to false instead of defaulting)
- if(startElement.attributeSize != sizeof(ResXMLTree_attribute))
- {
- RDCWARN("Declared attribute size %u doesn't match what we expect %zu",
- startElement.attributeSize, sizeof(ResXMLTree_attribute));
- }
-
- if(startElement.attributeStart != sizeof(startElement))
- {
- RDCWARN("Declared attribute start offset %u doesn't match what we expect %zu",
- startElement.attributeStart, sizeof(startElement));
- }
-
- const size_t attributeStartOffset = cur + node.headerSize + startElement.attributeStart;
- byte *attributesStart = manifestBytes.data() + attributeStartOffset;
-
- bool found = false;
-
- for(uint32_t i = 0; i < startElement.attributeCount; i++)
- {
- ResXMLTree_attribute *attribute =
- (ResXMLTree_attribute *)(attributesStart + startElement.attributeSize * i);
-
- rdcstr attr = GetStringPoolValue(manifestBytes, attribute->name);
-
- if(attr != "debuggable")
- continue;
-
- uint32_t resourceId = 0;
-
- if(attribute->name.index < resourceMappingCount)
- {
- resourceId = resourceMapping[attribute->name.index];
- }
- else
- {
- RDCWARN("Found debuggable attribute, but it's not linked to any resource ID");
-
- if(attribute->typedValue.dataType != Res_value::DataType::Boolean)
- {
- RDCERR("Found debuggable attribute that isn't boolean typed! Not modifying");
- return false;
- }
- else
- {
- RDCDEBUG("Setting non-resource ID debuggable attribute to true");
- attribute->typedValue.data = ~0U;
-
- if(attribute->rawValue.index != ~0U)
- {
- RDCWARN("attribute has raw value '%s' which we aren't patching",
- GetStringPoolValue(manifestBytes, attribute->rawValue).c_str());
- }
-
- // we'll still add a debuggable attribute that is resource ID linked, so we don't mark the
- // attribute as found and break out of the loop yet
- continue;
- }
- }
-
- if(resourceId != debuggableResourceId)
- {
- RDCERR(
- "Found debuggable attribute mapped to resource %x, not %x as we expect! Not modifying",
- resourceId, debuggableResourceId);
- return false;
- }
-
- RDCDEBUG("Found debuggable attribute.");
-
- if(attribute->typedValue.dataType != Res_value::DataType::Boolean)
- {
- RDCERR("Found debuggable attribute that isn't boolean typed! Not modifying");
- return false;
- }
- else
- {
- RDCDEBUG("Setting resource ID debuggable attribute to true");
- attribute->typedValue.data = ~0U;
-
- if(attribute->rawValue.index != ~0U)
- {
- RDCWARN("attribute has raw value '%s' which we aren't patching",
- GetStringPoolValue(manifestBytes, attribute->rawValue).c_str());
- }
- }
-
- found = true;
- break;
- }
-
- if(found)
- break;
-
- if(startElement.attributeSize != sizeof(ResXMLTree_attribute))
- {
- RDCERR("Unexpected attribute size %u, can't add missing attribute", startElement.attributeSize);
- return false;
- }
-
- // default to an invalid value (the manifest would have to be GBs to have this as a valid string
- // index.
- // If we don't find the existing string to use, then this will be remapped below when we're
- // remapping all the other indices.
- ResStringPool_ref stringIndex = {addingStringIndex};
-
- // we didn't find the attribute, so we need to search for the appropriate string, add it if not
- // there, and add the attribute.
- for(uint32_t i = 0; i < resourceMappingCount; i++)
- {
- if(resourceMapping[i] == debuggableResourceId)
- {
- rdcstr str = GetStringPoolValue(manifestBytes, {i});
-
- if(str != "debuggable")
- {
- RDCWARN("Found debuggable resource ID, but it was linked to string '%s' not 'debuggable'",
- str.c_str());
- continue;
- }
-
- stringIndex = {i};
- }
- }
-
- // declare the debuggable attribute
- ResXMLTree_attribute debuggable;
- debuggable.ns.index = ~0U;
- debuggable.name = stringIndex;
- debuggable.rawValue.index = ~0U;
- debuggable.typedValue.size = sizeof(Res_value);
- debuggable.typedValue.res0 = 0;
- debuggable.typedValue.dataType = Res_value::DataType::Boolean;
- debuggable.typedValue.data = ~0U;
-
- // search the stringpool for the schema, it should be there already.
- for(uint32_t i = 0; i < stringpool.stringCount; i++)
- {
- rdcstr val = GetStringPoolValue(manifestBytes, {i});
- if(val == "http://schemas.android.com/apk/res/android")
- {
- debuggable.ns.index = i;
- break;
- }
- }
-
- if(debuggable.ns.index == ~0U)
- RDCWARN("Couldn't find android schema, declaring attribute without schema");
-
- // it seems the attribute must be added so that the attributes are sorted in resource ID order.
- // We assume the attributes are already sorted according to this order, so we insert at the
- // index of the first attribute we encounter with either no resource ID (i.e. if we only
- // encountered lower resource IDs then we hit a non-resource ID attribute), or a higher resource
- // ID than ours (in which case we're inserting it in the right place).
- uint32_t attributeInsertIndex = 0;
-
- for(uint32_t i = 0; i < startElement.attributeCount; i++)
- {
- ResXMLTree_attribute *attr =
- (ResXMLTree_attribute *)(attributesStart + startElement.attributeSize * i);
-
- if(attr->name.index >= resourceMappingCount)
- {
- attributeInsertIndex = i;
- RDCDEBUG("Inserting attribute before %s, with no resource ID",
- GetStringPoolValue(manifestBytes, attr->name).c_str());
- break;
- }
-
- uint32_t resourceId = resourceMapping[attr->name.index];
-
- if(resourceId >= debuggableResourceId)
- {
- attributeInsertIndex = i;
- RDCDEBUG("Inserting attribute before %s, with resource ID %x",
- GetStringPoolValue(manifestBytes, attr->name).c_str(), resourceId);
- break;
- }
-
- RDCDEBUG("Skipping past attribute %s, with resource ID %x",
- GetStringPoolValue(manifestBytes, attr->name).c_str(), resourceId);
- }
-
- InsertBytes(manifestBytes,
- attributeStartOffset + startElement.attributeSize * attributeInsertIndex, debuggable);
-
- // update header
- node.size += sizeof(ResXMLTree_attribute);
- SetFromBytes(manifestBytes, cur, node);
-
- startElement.attributeCount++;
- SetFromBytes(manifestBytes, cur + node.headerSize, startElement);
-
- stringAdded = (stringIndex.index == addingStringIndex);
-
- break;
- }
-
- // if we added the string, we need to update the string pool and resource map, then finally update
- // all stringrefs in the nodes. We do this in reverse order so that we don't invalidate pointers
- // with insertions
- if(stringAdded)
- {
- uint32_t insertIdx = resourceMappingCount;
-
- // add to the resource map first because it's after the string pool, that way we don't have to
- // account for string pool modifications in resMapOffset
- {
- InsertBytes(manifestBytes, resMapOffset + resMap.size, debuggableResourceId);
- resMap.size += sizeof(uint32_t);
- SetFromBytes(manifestBytes, resMapOffset, resMap);
- }
-
- // add to the string pool next
- {
- // add the offset
- stringpool.header.size += sizeof(uint32_t);
- stringpool.stringCount++;
- stringpool.stringsStart += sizeof(uint32_t);
- // if we're adding a string we don't bother to do it sorted, so remove the sorted flag
- stringpool.flags =
- ResStringPool_header::StringFlags(stringpool.flags & ~ResStringPool_header::SORTED_FLAG);
-
- size_t stringpoolStringOffsetsOffset = stringpoolOffset + stringpool.header.headerSize;
-
- // we insert a zero offset at the position we're inserting. Then when we fix up that and all
- // subsequent offsets
- InsertBytes(manifestBytes, stringpoolStringOffsetsOffset + sizeof(uint32_t) * insertIdx,
- uint32_t(0));
-
- bytebuf stringbytes;
-
- // construct the string, with length prefix and trailing NULL
- if(stringpool.flags & ResStringPool_header::UTF8_FLAG)
- {
- stringbytes = {0xA, 0xA, 'd', 'e', 'b', 'u', 'g', 'g', 'a', 'b', 'l', 'e', 0};
- }
- else
- {
- stringbytes = {0xA, 0x0, 'd', 0, 'e', 0, 'b', 0, 'u', 0, 'g', 0,
- 'g', 0, 'a', 0, 'b', 0, 'l', 0, 'e', 0, 0, 0};
- }
-
- // account for added string
- stringpool.header.size += stringbytes.count();
-
- // shift all the offsets *after* the string we inserted (we inserted precisely at that
- // offset).
- uint32_t *stringOffsets = (uint32_t *)(manifestBytes.data() + stringpoolStringOffsetsOffset);
-
- // the one we inserted will be inserted at the offset of whichever was previously at that
- // index (which is now one further on)
- stringOffsets[insertIdx] = stringOffsets[insertIdx + 1];
-
- for(uint32_t i = insertIdx + 1; i < stringpool.stringCount; i++)
- stringOffsets[i] += stringbytes.count();
-
- // now insert the string bytes
- InsertBytes(manifestBytes,
- stringpoolOffset + stringpool.stringsStart + stringOffsets[insertIdx], stringbytes);
-
- // if the stringpool isn't integer aligned, add padding bytes
- uint32_t alignedSize = AlignUp4(stringpool.header.size);
-
- if(alignedSize > stringpool.header.size)
- {
- uint32_t paddingLen = alignedSize - stringpool.header.size;
-
- RDCDEBUG("Inserting %u padding bytes to align %u up to %u", paddingLen,
- stringpool.header.size, alignedSize);
-
- bytebuf padding;
- padding.resize(paddingLen);
-
- InsertBytes(manifestBytes, stringpoolOffset + stringpool.header.size, padding);
-
- stringpool.header.size += paddingLen;
- }
-
- // write the updated stringpool
- SetFromBytes(manifestBytes, stringpoolOffset, stringpool);
- }
-
- // now iterate over all nodes and fixup any stringrefs pointing after our insert point
- byte *ptr = manifestBytes.data() + xmlroot.headerSize;
- // skip string pool, whatever size it is now
- ptr += ((ResChunk_header *)ptr)->size;
- // skip resource map, whatever size it is now
- ptr += ((ResChunk_header *)ptr)->size;
-
- while(ptr < manifestBytes.end())
- {
- ResXMLTree_node *node = (ResXMLTree_node *)ptr;
-
- if(node->header.headerSize != sizeof(*node))
- RDCWARN("Headersize was reported as %u, but we expected ResXMLTree_node size %zu",
- node->header.headerSize, sizeof(*node));
-
- ShiftStringPoolValue(node->comment, insertIdx);
-
- switch(node->header.type)
- {
- // namespace start and end are identical
- case ResType::NamespaceStart:
- case ResType::NamespaceEnd:
- {
- ResXMLTree_namespaceExt *ns = (ResXMLTree_namespaceExt *)(ptr + node->header.headerSize);
-
- ShiftStringPoolValue(ns->prefix, insertIdx);
- ShiftStringPoolValue(ns->uri, insertIdx);
- break;
- }
- case ResType::EndElement:
- {
- ResXMLTree_endElementExt *endElement =
- (ResXMLTree_endElementExt *)(ptr + node->header.headerSize);
-
- ShiftStringPoolValue(endElement->ns, insertIdx);
- ShiftStringPoolValue(endElement->name, insertIdx);
- break;
- }
- case ResType::CDATA:
- {
- ResXMLTree_cdataExt *cdata = (ResXMLTree_cdataExt *)(ptr + node->header.headerSize);
-
- ShiftStringPoolValue(cdata->data, insertIdx);
- ShiftStringPoolValue(cdata->typedData, insertIdx);
- break;
- }
- case ResType::StartElement:
- {
- ResXMLTree_attrExt *startElement = (ResXMLTree_attrExt *)(ptr + node->header.headerSize);
-
- ShiftStringPoolValue(startElement->ns, insertIdx);
- ShiftStringPoolValue(startElement->name, insertIdx);
-
- // update attributes
- byte *attributesStart = ptr + node->header.headerSize + startElement->attributeStart;
-
- for(uint32_t i = 0; i < startElement->attributeCount; i++)
- {
- ResXMLTree_attribute *attr =
- (ResXMLTree_attribute *)(attributesStart + startElement->attributeSize * i);
-
- ShiftStringPoolValue(attr->ns, insertIdx);
- ShiftStringPoolValue(attr->name, insertIdx);
- ShiftStringPoolValue(attr->rawValue, insertIdx);
- ShiftStringPoolValue(attr->typedValue, insertIdx);
- }
- break;
- }
- default:
- RDCERR("Unhandled chunk %x, can't patch stringpool references", node->header.type);
- return false;
- }
-
- ptr += node->header.size;
- }
- }
-
- xmlroot.size = (uint32_t)manifestBytes.size();
- SetFromBytes(manifestBytes, 0, xmlroot);
-
- return true;
-}
-};
diff --git a/renderdoc/android/android_patch.cpp b/renderdoc/android/android_patch.cpp
deleted file mode 100644
index 1802fc9b9..000000000
--- a/renderdoc/android/android_patch.cpp
+++ /dev/null
@@ -1,622 +0,0 @@
-/******************************************************************************
- * The MIT License (MIT)
- *
- * Copyright (c) 2019-2022 Baldur Karlsson
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- ******************************************************************************/
-
-#include "api/replay/version.h"
-#include "core/core.h"
-#include "miniz/miniz.h"
-#include "replay/replay_driver.h"
-#include "strings/string_utils.h"
-#include "android_utils.h"
-
-static const char keystoreName[] = "renderdoc.keystore";
-
-namespace Android
-{
-bool RemoveAPKSignature(const rdcstr &apk)
-{
- RDCLOG("Checking for existing signature");
-
- rdcstr aapt = getToolPath(ToolDir::BuildTools, "aapt", false);
-
- // Get the list of files in META-INF
- rdcstr fileList = execCommand(aapt, "list \"" + apk + "\"").strStdout;
- if(fileList.empty())
- return false;
-
- // Walk through the output. If it starts with META-INF, remove it.
- uint32_t fileCount = 0;
- uint32_t matchCount = 0;
-
- rdcarray lines;
- split(fileList, lines, '\n');
-
- for(rdcstr &line : lines)
- {
- line.trim();
- fileCount++;
- if(line.beginsWith("META-INF"))
- {
- RDCDEBUG("Match found, removing %s", line.c_str());
- execCommand(aapt, "remove \"" + apk + "\" " + line);
- matchCount++;
- }
- }
- RDCLOG("%d files searched, %d removed", fileCount, matchCount);
-
- // Ensure no hits on second pass through
- RDCDEBUG("Walk through file list again, ensure signature removed");
- fileList = execCommand(aapt, "list \"" + apk + "\"").strStdout;
- split(fileList, lines, '\n');
- for(rdcstr &line : lines)
- {
- line.trim();
- if(line.beginsWith("META-INF"))
- {
- RDCERR("Match found, that means removal failed! %s", line.c_str());
- return false;
- }
- }
- return true;
-}
-
-bool ExtractAndRemoveManifest(const rdcstr &apk, bytebuf &manifest)
-{
- // pull out the manifest with miniz
- mz_zip_archive zip;
- RDCEraseEl(zip);
-
- mz_bool b = mz_zip_reader_init_file(&zip, apk.c_str(), 0);
-
- if(b)
- {
- mz_uint numfiles = mz_zip_reader_get_num_files(&zip);
-
- for(mz_uint i = 0; i < numfiles; i++)
- {
- mz_zip_archive_file_stat zstat;
- mz_zip_reader_file_stat(&zip, i, &zstat);
-
- if(!strcmp(zstat.m_filename, "AndroidManifest.xml"))
- {
- size_t sz = 0;
- byte *buf = (byte *)mz_zip_reader_extract_to_heap(&zip, i, &sz, 0);
-
- RDCLOG("Got manifest of %zu bytes", sz);
-
- manifest = bytebuf(buf, sz);
- free(buf);
- break;
- }
- }
- }
- else
- {
- RDCERR("Couldn't open %s", apk.c_str());
- }
-
- mz_zip_reader_end(&zip);
-
- if(manifest.empty())
- return false;
-
- rdcstr aapt = getToolPath(ToolDir::BuildTools, "aapt", false);
-
- RDCDEBUG("Removing AndroidManifest.xml");
- execCommand(aapt, "remove \"" + apk + "\" AndroidManifest.xml");
-
- rdcstr fileList = execCommand(aapt, "list \"" + apk + "\"").strStdout;
- rdcarray files;
- split(fileList, files, ' ');
-
- for(rdcstr &f : files)
- {
- f.trim();
- if(f == "AndroidManifest.xml")
- {
- RDCERR("AndroidManifest.xml found, that means removal failed!");
- return false;
- }
- }
-
- return true;
-}
-
-bool AddManifestToAPK(const rdcstr &apk, const rdcstr &tmpDir, const bytebuf &manifest)
-{
- rdcstr aapt = getToolPath(ToolDir::BuildTools, "aapt", false);
-
- // write the manifest to disk
- FileIO::WriteAll(tmpDir + "AndroidManifest.xml", manifest);
-
- // run aapt to add the manifest
- Process::ProcessResult result =
- execCommand(aapt, "add \"" + apk + "\" AndroidManifest.xml", tmpDir);
-
- if(result.strStdout.empty())
- {
- RDCERR("Failed to add manifest to APK. STDERR: %s", result.strStderror.c_str());
- return false;
- }
-
- return true;
-}
-
-bool RealignAPK(const rdcstr &apk, rdcstr &alignedAPK, const rdcstr &tmpDir)
-{
- rdcstr zipalign = getToolPath(ToolDir::BuildTools, "zipalign", false);
-
- // Re-align the APK for performance
- RDCLOG("Realigning APK");
- rdcstr errOut =
- execCommand(zipalign, "-f 4 \"" + apk + "\" \"" + alignedAPK + "\"", tmpDir).strStderror;
-
- if(!errOut.empty())
- return false;
-
- // Wait until the aligned version exists to proceed
- uint32_t elapsed = 0;
- uint32_t timeout = 10000; // 10 seconds
- while(elapsed < timeout)
- {
- if(FileIO::exists(alignedAPK))
- {
- RDCLOG("Aligned APK ready to go, continuing...");
- return true;
- }
-
- Threading::Sleep(1000);
- elapsed += 1000;
- }
-
- RDCERR("Timeout reached aligning APK");
- return false;
-}
-
-rdcstr GetAndroidDebugKey()
-{
- rdcstr keystore = getToolPath(ToolDir::None, keystoreName, false);
-
- // if we found the keystore, use that.
- if(FileIO::exists(keystore))
- return keystore;
-
- // otherwise, generate a temporary one
- rdcstr key = FileIO::GetTempFolderFilename() + keystoreName;
-
- FileIO::Delete(key);
-
- // locate keytool and use it to generate a keystore
- rdcstr create;
- create += " -genkey";
- create += " -keystore \"" + key + "\"";
- create += " -storepass android";
- create += " -alias rdocandroidkey";
- create += " -keypass android";
- create += " -keyalg RSA";
- create += " -keysize 2048";
- create += " -validity 10000";
- create += " -dname \"CN=, OU=, O=, L=, S=, C=\"";
-
- rdcstr keytool = getToolPath(ToolDir::Java, "keytool", false);
-
- Process::ProcessResult createResult = execCommand(keytool, create);
-
- Process::ProcessResult verifyResult;
-
- // if the keystore was created, check that the key we expect to be in it is there
- if(FileIO::exists(key))
- {
- rdcstr verify;
- verify += " -list";
- verify += " -keystore \"" + key + "\"";
- verify += " -storepass android";
-
- verifyResult = execCommand(keytool, verify);
-
- if(verifyResult.strStdout.contains("rdocandroidkey"))
- return key;
- }
-
- RDCERR("Failed to create debug key: %s\n%s\n%s\n%s", createResult.strStdout.c_str(),
- createResult.strStderror.c_str(), verifyResult.strStdout.c_str(),
- verifyResult.strStderror.c_str());
- return "";
-}
-
-bool DebugSignAPK(const rdcstr &apk, const rdcstr &workDir)
-{
- RDCLOG("Signing with debug key");
-
- rdcstr aapt = getToolPath(ToolDir::BuildTools, "aapt", false);
- rdcstr apksigner = getToolPath(ToolDir::BuildToolsLib, "apksigner.jar", false);
-
- rdcstr debugKey = GetAndroidDebugKey();
-
- if(debugKey.empty())
- return false;
-
- rdcstr args;
- args += " sign ";
- args += " --ks \"" + debugKey + "\" ";
- args += " --ks-pass pass:android ";
- args += " --key-pass pass:android ";
- args += " --ks-key-alias rdocandroidkey ";
- args += "\"" + apk + "\"";
-
- if(!apksigner.contains(".jar"))
- {
- // 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
- rdcstr java = getToolPath(ToolDir::Java, "java", false);
-
- rdcstr signerdir = get_dirname(FileIO::GetFullPathname(apksigner));
-
- rdcstr javaargs;
- javaargs += " \"-Djava.ext.dirs=" + signerdir + "\"";
- javaargs += " -jar \"" + apksigner + "\"";
- javaargs += args;
-
- execCommand(java, javaargs, workDir);
- }
-
- // Check for signature
- rdcstr list = execCommand(aapt, "list \"" + apk + "\"").strStdout;
-
- list.insert(0, '\n');
-
- if(list.find("\nMETA-INF") >= 0)
- {
- RDCLOG("Signature found, continuing...");
- return true;
- }
-
- RDCERR("re-sign of APK failed!");
- return false;
-}
-
-bool UninstallOriginalAPK(const rdcstr &deviceID, const rdcstr &packageName, const rdcstr &workDir)
-{
- RDCLOG("Uninstalling previous version of application");
-
- adbExecCommand(deviceID, "uninstall " + packageName, workDir);
-
- // Wait until uninstall completes
- rdcstr uninstallResult;
- uint32_t elapsed = 0;
- uint32_t timeout = 10000; // 10 seconds
- while(elapsed < timeout)
- {
- uninstallResult = adbExecCommand(deviceID, "shell pm path " + packageName).strStdout;
- if(uninstallResult.empty())
- {
- RDCLOG("Package removed");
- return true;
- }
-
- Threading::Sleep(1000);
- elapsed += 1000;
- }
-
- RDCERR("Uninstallation of APK failed!");
- return false;
-}
-
-bool ReinstallPatchedAPK(const rdcstr &deviceID, const rdcstr &apk, const rdcstr &abi,
- const rdcstr &packageName, const rdcstr &workDir)
-{
- RDCLOG("Reinstalling APK");
-
- if(abi == "null" || abi.empty())
- adbExecCommand(deviceID, "install \"" + apk + "\"", workDir);
- else
- adbExecCommand(deviceID, "install --abi " + abi + " \"" + apk + "\"", workDir);
-
- // Wait until re-install completes
- rdcstr reinstallResult;
- uint32_t elapsed = 0;
- uint32_t timeout = 10000; // 10 seconds
- while(elapsed < timeout)
- {
- reinstallResult = adbExecCommand(deviceID, "shell pm path " + packageName).strStdout;
- if(!reinstallResult.empty())
- {
- RDCLOG("Patched APK reinstalled, continuing...");
- return true;
- }
-
- Threading::Sleep(1000);
- elapsed += 1000;
- }
-
- RDCERR("Reinstallation of APK failed!");
- return false;
-}
-
-bool CheckPatchingRequirements()
-{
- // check for required tools for patching
- rdcarray> requirements;
- rdcarray missingTools;
- requirements.push_back({ToolDir::BuildTools, "aapt"});
- requirements.push_back({ToolDir::BuildTools, "zipalign"});
- requirements.push_back({ToolDir::BuildToolsLib, "apksigner.jar"});
- requirements.push_back({ToolDir::Java, "java"});
-
- for(uint32_t i = 0; i < requirements.size(); i++)
- {
- rdcstr 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
- {
- rdcstr 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
- rdcstr tool = getToolPath(ToolDir::Java, "keytool", true);
-
- if(tool.empty())
- {
- // if not, it's missing too
- missingTools.push_back("keytool");
- }
- }
- }
-
- if(missingTools.size() > 0)
- {
- for(uint32_t i = 0; i < missingTools.size(); i++)
- RDCERR("Missing %s", missingTools[i].c_str());
- return false;
- }
-
- return true;
-}
-
-bool PullAPK(const rdcstr &deviceID, const rdcstr &pkgPath, const rdcstr &apk)
-{
- RDCLOG("Pulling APK to patch");
-
- adbExecCommand(deviceID, "pull " + pkgPath + " \"" + apk + "\"");
-
- // Wait until the apk lands
- uint32_t elapsed = 0;
- uint32_t timeout = 10000; // 10 seconds
- while(elapsed < timeout)
- {
- if(FileIO::exists(apk))
- {
- RDCLOG("Original APK ready to go, continuing...");
- return true;
- }
-
- Threading::Sleep(1000);
- elapsed += 1000;
- }
-
- RDCLOG("Failed to pull APK");
- return false;
-}
-
-void CopyAPK(const rdcstr &deviceID, const rdcstr &pkgPath, const rdcstr ©Path)
-{
- RDCLOG("Copying APK to %s", copyPath.c_str());
- adbExecCommand(deviceID, "shell cp " + pkgPath + " " + copyPath);
-}
-
-void RemoveAPK(const rdcstr &deviceID, const rdcstr &path)
-{
- RDCLOG("Removing APK from %s", path.c_str());
- adbExecCommand(deviceID, "shell rm -f " + path);
-}
-
-bool HasRootAccess(const rdcstr &deviceID)
-{
- RDCLOG("Checking for root access on %s", deviceID.c_str());
-
- // 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.
-
- Process::ProcessResult result = adbExecCommand(deviceID, "root");
-
- rdcstr whoami = adbExecCommand(deviceID, "shell whoami").strStdout.trimmed();
- if(whoami == "root")
- return true;
-
- rdcstr checksu =
- adbExecCommand(deviceID, "shell test -e /system/xbin/su && echo found").strStdout.trimmed();
- if(checksu == "found")
- return true;
-
- return false;
-}
-
-rdcstr GetFirstMatchingLine(const rdcstr &haystack, const rdcstr &needle)
-{
- int needleOffset = haystack.find(needle);
-
- if(needleOffset == -1)
- return rdcstr();
-
- int nextLine = haystack.find('\n', needleOffset + 1);
-
- return haystack.substr(needleOffset, nextLine == -1 ? ~0U : size_t(nextLine - needleOffset));
-}
-
-bool IsDebuggable(const rdcstr &deviceID, const rdcstr &packageName)
-{
- RDCLOG("Checking that APK is debuggable");
-
- rdcstr info = adbExecCommand(deviceID, "shell dumpsys package " + packageName).strStdout;
-
- rdcstr pkgFlags = GetFirstMatchingLine(info, "pkgFlags=[");
-
- if(pkgFlags == "")
- {
- RDCERR("Couldn't get pkgFlags from adb");
- return false;
- }
-
- return pkgFlags.contains("DEBUGGABLE");
-}
-};
-
-extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_CheckAndroidPackage(
- const rdcstr &URL, const rdcstr &packageAndActivity, AndroidFlags *flags)
-{
- IDeviceProtocolHandler *adb = RenderDoc::Inst().GetDeviceProtocol("adb");
-
- rdcstr deviceID = adb->GetDeviceID(URL);
-
- // Reset the flags each time we check
- *flags = AndroidFlags::NoFlags;
-
- if(Android::IsDebuggable(deviceID, Android::GetPackageName(packageAndActivity)))
- {
- *flags |= AndroidFlags::Debuggable;
- }
- else
- {
- RDCLOG("%s is not debuggable", packageAndActivity.c_str());
- }
-
- if(Android::HasRootAccess(deviceID))
- {
- RDCLOG("Root access detected");
- *flags |= AndroidFlags::RootAccess;
- }
-
- return;
-}
-
-extern "C" RENDERDOC_API AndroidFlags RENDERDOC_CC RENDERDOC_MakeDebuggablePackage(
- const rdcstr &URL, const rdcstr &packageAndActivity, RENDERDOC_ProgressCallback progress)
-{
- rdcstr package = Android::GetPackageName(packageAndActivity);
-
- Process::ProcessResult result = {};
-
- IDeviceProtocolHandler *adb = RenderDoc::Inst().GetDeviceProtocol("adb");
-
- rdcstr deviceID = adb->GetDeviceID(URL);
-
- // make sure progress is valid so we don't have to check it everywhere
- if(!progress)
- progress = [](float) {};
-
- progress(0.0f);
-
- if(!Android::CheckPatchingRequirements())
- return AndroidFlags::MissingTools;
-
- progress(0.02f);
-
- rdcstr abi = Android::DetermineInstalledABI(deviceID, package);
-
- // Find the APK on the device
- rdcstr pkgPath = Android::GetPathForPackage(deviceID, package) + "base.apk";
-
- rdcstr tmpDir = FileIO::GetTempFolderFilename();
- rdcstr origAPK(tmpDir + package + ".orig.apk");
- rdcstr alignedAPK(origAPK + ".aligned.apk");
- bytebuf manifest;
-
- // Try the following steps, bailing if anything fails
- if(!Android::PullAPK(deviceID, pkgPath, origAPK))
- {
- // Copy the APK to public storage, then try to pull again
- rdcstr 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);
-
- if(!Android::RemoveAPKSignature(origAPK))
- return AndroidFlags::ManifestPatchFailure;
-
- progress(0.425f);
-
- if(!Android::ExtractAndRemoveManifest(origAPK, manifest))
- return AndroidFlags::ManifestPatchFailure;
-
- progress(0.45f);
-
- if(!Android::PatchManifest(manifest))
- return AndroidFlags::ManifestPatchFailure;
-
- progress(0.46f);
-
- if(!Android::AddManifestToAPK(origAPK, tmpDir, manifest))
- return AndroidFlags::ManifestPatchFailure;
-
- progress(0.475f);
-
- if(!Android::RealignAPK(origAPK, alignedAPK, tmpDir))
- return AndroidFlags::RepackagingAPKFailure;
-
- progress(0.5f);
-
- if(!Android::DebugSignAPK(alignedAPK, tmpDir))
- return AndroidFlags::RepackagingAPKFailure;
-
- progress(0.525f);
-
- if(!Android::UninstallOriginalAPK(deviceID, package, tmpDir))
- return AndroidFlags::RepackagingAPKFailure;
-
- progress(0.6f);
-
- if(!Android::ReinstallPatchedAPK(deviceID, alignedAPK, abi, package, tmpDir))
- return AndroidFlags::RepackagingAPKFailure;
-
- progress(0.95f);
-
- if(!Android::IsDebuggable(deviceID, package))
- return AndroidFlags::ManifestPatchFailure;
-
- progress(1.0f);
-
- // All clean!
- return AndroidFlags::Debuggable;
-}
diff --git a/renderdoc/android/android_utils.cpp b/renderdoc/android/android_utils.cpp
index 2d5048859..6c56dbe3e 100644
--- a/renderdoc/android/android_utils.cpp
+++ b/renderdoc/android/android_utils.cpp
@@ -274,6 +274,57 @@ rdcstr GetFriendlyName(const rdcstr &deviceID)
return combined;
}
+bool HasRootAccess(const rdcstr &deviceID)
+{
+ RDCLOG("Checking for root access on %s", deviceID.c_str());
+
+ // 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.
+
+ Process::ProcessResult result = adbExecCommand(deviceID, "root");
+
+ rdcstr whoami = adbExecCommand(deviceID, "shell whoami").strStdout.trimmed();
+ if(whoami == "root")
+ return true;
+
+ rdcstr checksu =
+ adbExecCommand(deviceID, "shell test -e /system/xbin/su && echo found").strStdout.trimmed();
+ if(checksu == "found")
+ return true;
+
+ return false;
+}
+
+rdcstr GetFirstMatchingLine(const rdcstr &haystack, const rdcstr &needle)
+{
+ int needleOffset = haystack.find(needle);
+
+ if(needleOffset == -1)
+ return rdcstr();
+
+ int nextLine = haystack.find('\n', needleOffset + 1);
+
+ return haystack.substr(needleOffset, nextLine == -1 ? ~0U : size_t(nextLine - needleOffset));
+}
+
+bool IsDebuggable(const rdcstr &deviceID, const rdcstr &packageName)
+{
+ RDCLOG("Checking that APK is debuggable");
+
+ rdcstr info = adbExecCommand(deviceID, "shell dumpsys package " + packageName).strStdout;
+
+ rdcstr pkgFlags = GetFirstMatchingLine(info, "pkgFlags=[");
+
+ if(pkgFlags == "")
+ {
+ RDCERR("Couldn't get pkgFlags from adb");
+ return false;
+ }
+
+ return pkgFlags.contains("DEBUGGABLE");
+}
+
// on android only when we hit this function we write a marker that isn't a standard log. The
// purpose is to always try and have a unique message in the last N lines so that we can detect if
// we ever lose messages.
diff --git a/renderdoc/android/android_utils.h b/renderdoc/android/android_utils.h
index 437722b40..6f6cc7f4e 100644
--- a/renderdoc/android/android_utils.h
+++ b/renderdoc/android/android_utils.h
@@ -48,6 +48,8 @@ enum class ToolDir
rdcstr getToolPath(ToolDir subdir, const rdcstr &toolname, bool checkExist);
bool toolExists(const rdcstr &path);
+bool IsDebuggable(const rdcstr &deviceID, const rdcstr &packageName);
+bool HasRootAccess(const rdcstr &deviceID);
rdcstr GetFirstMatchingLine(const rdcstr &haystack, const rdcstr &needle);
bool IsSupported(rdcstr deviceID);
diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h
index 912038b13..53b5949e3 100644
--- a/renderdoc/api/replay/renderdoc_replay.h
+++ b/renderdoc/api/replay/renderdoc_replay.h
@@ -2166,10 +2166,6 @@ DOCUMENT("INTERNAL: Check remote Android package for requirements");
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_CheckAndroidPackage(
const rdcstr &URL, const rdcstr &packageAndActivity, AndroidFlags *flags);
-DOCUMENT("INTERNAL: Patch an APK to add debuggable flag.");
-extern "C" RENDERDOC_API AndroidFlags RENDERDOC_CC RENDERDOC_MakeDebuggablePackage(
- const rdcstr &URL, const rdcstr &packageAndActivity, RENDERDOC_ProgressCallback progress);
-
DOCUMENT("An interface for enumerating and controlling remote devices.");
struct IDeviceProtocolController
{
diff --git a/renderdoc/renderdoc.vcxproj b/renderdoc/renderdoc.vcxproj
index f2ce743a7..8688bbb53 100644
--- a/renderdoc/renderdoc.vcxproj
+++ b/renderdoc/renderdoc.vcxproj
@@ -108,7 +108,6 @@
-
@@ -526,8 +525,6 @@
true
-
-
diff --git a/renderdoc/renderdoc.vcxproj.filters b/renderdoc/renderdoc.vcxproj.filters
index 4b2d29f83..78323c672 100644
--- a/renderdoc/renderdoc.vcxproj.filters
+++ b/renderdoc/renderdoc.vcxproj.filters
@@ -112,9 +112,6 @@
{f9ed2873-6120-4b34-a3c2-e57b2de31eb9}
-
- {6e798cbc-0eae-4f48-9d7c-f3fe58bd32dc}
-
{0362a726-a8e6-436e-b523-893693163440}
@@ -432,9 +429,6 @@
Android
-
- 3rdparty\android
-
3rdparty\interceptor-lib
@@ -881,9 +875,6 @@
Android
-
- Android
-
Android
@@ -893,9 +884,6 @@
Android
-
- Android
-
3rdparty\interceptor-lib