diff --git a/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp b/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp index be23b3937..4e036b743 100644 --- a/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp +++ b/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp @@ -108,12 +108,20 @@ void CaptureDialog::PopulateMostRecent() if(!settings.executable.isEmpty()) { ui->loadLastCapture->setEnabled(true); - if(settings.executable.indexOf('/') < 0 && settings.executable.indexOf('\\') < 0) - ui->loadLastCapture->setText(tr("Load Last Settings - %1").arg(QString(settings.executable))); + + QString exe = settings.executable; + + // if the executable isn't a path, display the full name + bool fullName = (exe.indexOf(QLatin1Char('/')) < 0 && exe.indexOf(QLatin1Char('\\')) < 0); + + // if it's not a windows path and only contains one '/', it's an android package so also + // display the full name + fullName |= (exe.indexOf(QLatin1Char('\\')) < 0 && exe.count(QLatin1Char('/')) == 1); + + if(fullName) + ui->loadLastCapture->setText(tr("Load Last Settings - %1").arg(exe)); else - ui->loadLastCapture->setText( - tr("Load Last Settings - %1") - .arg(QFileInfo(QString(settings.executable)).completeBaseName())); + ui->loadLastCapture->setText(tr("Load Last Settings - %1").arg(QFileInfo(exe).fileName())); return; } } diff --git a/qrenderdoc/Windows/Dialogs/VirtualFileDialog.cpp b/qrenderdoc/Windows/Dialogs/VirtualFileDialog.cpp index 6bb9adf13..841e036fc 100644 --- a/qrenderdoc/Windows/Dialogs/VirtualFileDialog.cpp +++ b/qrenderdoc/Windows/Dialogs/VirtualFileDialog.cpp @@ -125,6 +125,10 @@ public: } } } + else if(roots.size() == 1 && roots[0]->file.filename == "") + { + normPath.insert(0, QLatin1Char('/')); + } // normPath is now of the form /subdir1/subdir2/subdir3/... // with ret pointing to the root directory (trivial on unix) @@ -476,6 +480,7 @@ private: child->parent = node; child->parentIndex = i; child->file = sortedFiles[i]; + child->populated = !(child->file.flags & PathProperty::Directory); node->children.push_back(child); } }); @@ -604,6 +609,8 @@ VirtualFileDialog::VirtualFileDialog(ICaptureContext &ctx, QString initialDirect QObject::connect(ui->fileList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &VirtualFileDialog::fileList_selectionChanged); + QObject::connect(ui->dirList->selectionModel(), &QItemSelectionModel::selectionChanged, this, + &VirtualFileDialog::dirList_selectionChanged); } VirtualFileDialog::~VirtualFileDialog() @@ -712,6 +719,14 @@ void VirtualFileDialog::on_dirList_clicked(const QModelIndex &index) changeCurrentDir(m_DirProxy->mapToSource(index)); } +void VirtualFileDialog::dirList_selectionChanged(const QItemSelection &selected, + const QItemSelection &deselected) +{ + QModelIndexList indices = selected.indexes(); + if(indices.count() >= 1) + on_dirList_clicked(indices[0]); +} + void VirtualFileDialog::on_fileList_doubleClicked(const QModelIndex &index) { bool isDir = m_FileProxy->data(index, RemoteFileModel::FileIsDirRole).toBool(); diff --git a/qrenderdoc/Windows/Dialogs/VirtualFileDialog.h b/qrenderdoc/Windows/Dialogs/VirtualFileDialog.h index a183e8a13..315a0a7f0 100644 --- a/qrenderdoc/Windows/Dialogs/VirtualFileDialog.h +++ b/qrenderdoc/Windows/Dialogs/VirtualFileDialog.h @@ -63,6 +63,7 @@ private slots: void on_upFolder_clicked(); // manual slots + void dirList_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); void fileList_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); private: diff --git a/renderdoc/android/android.cpp b/renderdoc/android/android.cpp index 57aaa2086..9b58ef1f9 100644 --- a/renderdoc/android/android.cpp +++ b/renderdoc/android/android.cpp @@ -176,7 +176,7 @@ int GetCurrentPID(const std::string &deviceID, const std::string &packageName) return 0; } -ExecuteResult StartAndroidPackageForCapture(const char *host, const char *package, +ExecuteResult StartAndroidPackageForCapture(const char *host, const char *packageAndActivity, const char *intentArgs, const CaptureOptions &opts) { int index = 0; @@ -187,10 +187,14 @@ ExecuteResult StartAndroidPackageForCapture(const char *host, const char *packag ret.status = ReplayStatus::UnknownError; ret.ident = RenderDoc_FirstTargetControlPort + RenderDoc_AndroidPortOffset * (index + 1); - std::string packageName = get_basename(std::string(package)); // Remove leading '/' if any + std::string packageName = GetPackageName(packageAndActivity); // Remove leading '/' if any // adb shell cmd package resolve-activity -c android.intent.category.LAUNCHER com.jake.cube1 - std::string activityName = GetDefaultActivityForPackage(deviceID, packageName); + std::string activityName = GetActivityName(packageAndActivity); + + // if the activity name isn't specified, get the default one + if(activityName.empty() || activityName == "#DefaultActivity") + activityName = GetDefaultActivityForPackage(deviceID, packageName); uint16_t jdwpPort = GetJdwpPort(); diff --git a/renderdoc/android/android.h b/renderdoc/android/android.h index df3364332..815b4329c 100644 --- a/renderdoc/android/android.h +++ b/renderdoc/android/android.h @@ -31,7 +31,9 @@ namespace Android { bool IsHostADB(const char *hostname); -ExecuteResult StartAndroidPackageForCapture(const char *host, const char *package, +std::string GetPackageName(const std::string &packageAndActivity); +std::string GetActivityName(const std::string &packageAndActivity); +ExecuteResult StartAndroidPackageForCapture(const char *host, const char *packageAndActivity, const char *intentArgs, const CaptureOptions &opts); void ResetCaptureSettings(const std::string &deviceID); void ExtractDeviceIDAndIndex(const std::string &hostname, int &index, std::string &deviceID); diff --git a/renderdoc/android/android_patch.cpp b/renderdoc/android/android_patch.cpp index 5d8550c0e..bd53ac1bf 100644 --- a/renderdoc/android/android_patch.cpp +++ b/renderdoc/android/android_patch.cpp @@ -523,9 +523,8 @@ bool IsDebuggable(const std::string &deviceID, const std::string &packageName) } }; -extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_CheckAndroidPackage(const char *hostname, - const char *packageName, - AndroidFlags *flags) +extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_CheckAndroidPackage( + const char *hostname, const char *packageAndActivity, AndroidFlags *flags) { int index = 0; std::string deviceID; @@ -534,13 +533,13 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_CheckAndroidPackage(const c // Reset the flags each time we check *flags = AndroidFlags::NoFlags; - if(Android::IsDebuggable(deviceID, get_basename(std::string(packageName)))) + if(Android::IsDebuggable(deviceID, Android::GetPackageName(packageAndActivity))) { *flags |= AndroidFlags::Debuggable; } else { - RDCLOG("%s is not debuggable", packageName); + RDCLOG("%s is not debuggable", packageAndActivity); } if(Android::HasRootAccess(deviceID)) @@ -553,10 +552,11 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_CheckAndroidPackage(const c } extern "C" RENDERDOC_API AndroidFlags RENDERDOC_CC RENDERDOC_MakeDebuggablePackage( - const char *hostname, const char *packageName, RENDERDOC_ProgressCallback progress) + const char *hostname, const char *packageAndActivity, RENDERDOC_ProgressCallback progress) { + std::string package = Android::GetPackageName(packageAndActivity); + Process::ProcessResult result = {}; - std::string package(get_basename(std::string(packageName))); int index = 0; std::string deviceID; @@ -629,17 +629,17 @@ extern "C" RENDERDOC_API AndroidFlags RENDERDOC_CC RENDERDOC_MakeDebuggablePacka progress(0.525f); - if(!Android::UninstallOriginalAPK(deviceID, packageName, tmpDir)) + if(!Android::UninstallOriginalAPK(deviceID, package, tmpDir)) return AndroidFlags::RepackagingAPKFailure; progress(0.6f); - if(!Android::ReinstallPatchedAPK(deviceID, alignedAPK, abi, packageName, tmpDir)) + if(!Android::ReinstallPatchedAPK(deviceID, alignedAPK, abi, package, tmpDir)) return AndroidFlags::RepackagingAPKFailure; progress(0.95f); - if(!Android::IsDebuggable(deviceID, packageName)) + if(!Android::IsDebuggable(deviceID, package)) return AndroidFlags::ManifestPatchFailure; progress(1.0f); diff --git a/renderdoc/android/android_utils.cpp b/renderdoc/android/android_utils.cpp index 1a1a86a86..bece1e294 100644 --- a/renderdoc/android/android_utils.cpp +++ b/renderdoc/android/android_utils.cpp @@ -59,6 +59,40 @@ void ExtractDeviceIDAndIndex(const std::string &hostname, int &index, std::strin deviceID = c; } +std::string GetPackageName(const std::string &packageAndActivity) +{ + if(packageAndActivity.empty()) + return ""; + + size_t start = 0; + if(packageAndActivity[0] == '/') + start++; + + size_t activitySep = packageAndActivity.find('/', start); + + if(activitySep == std::string::npos) + return packageAndActivity.substr(start); + + return packageAndActivity.substr(start, activitySep - start); +} + +std::string GetActivityName(const std::string &packageAndActivity) +{ + if(packageAndActivity.empty()) + return ""; + + size_t start = 0; + if(packageAndActivity[0] == '/') + start++; + + size_t activitySep = packageAndActivity.find('/', start); + + if(activitySep == std::string::npos) + return ""; + + return packageAndActivity.substr(activitySep + 1); +} + ABI GetABI(const std::string &abiName) { if(abiName == "armeabi-v7a") diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index 607a67ea5..472331607 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -2367,13 +2367,12 @@ extern "C" RENDERDOC_API ReplayStatus RENDERDOC_CC RENDERDOC_StartAndroidRemoteServer(const char *device); DOCUMENT("Internal function for checking remote Android package for requirements"); -extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_CheckAndroidPackage(const char *hostname, - const char *packageName, - AndroidFlags *flags); +extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_CheckAndroidPackage( + const char *hostname, const char *packageAndActivity, AndroidFlags *flags); 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); + const char *hostname, const char *packageAndActivity, 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/core/remote_server.cpp b/renderdoc/core/remote_server.cpp index c251adcb9..37d1b5119 100644 --- a/renderdoc/core/remote_server.cpp +++ b/renderdoc/core/remote_server.cpp @@ -1197,33 +1197,133 @@ public: std::string deviceID; Android::ExtractDeviceIDAndIndex(m_hostname, index, deviceID); - std::string adbStdout = - Android::adbExecCommand(deviceID, "shell pm list packages -3").strStdout; - - std::istringstream stdoutStream(adbStdout); - std::string line; - std::vector packages; - while(getline(stdoutStream, line)) + if(path[0] == 0 || (path[0] == '/' && path[1] == 0)) { - std::vector tokens; - split(line, tokens, ':'); - if(tokens.size() == 2 && tokens[0] == "package") - { - PathEntry package; - package.filename = trim(tokens[1]); - package.size = 0; - package.lastmod = 0; - package.flags = PathProperty::Executable; + SCOPED_TIMER("Fetching android packages and activities"); + std::string adbStdout = + Android::adbExecCommand(deviceID, "shell pm list packages -3").strStdout; + + std::vector lines; + split(adbStdout, lines, '\n'); + + std::vector packages; + for(const std::string &line : lines) + { // hide our own internal packages - if(strstr(package.filename.c_str(), "org.renderdoc.")) + if(strstr(line.c_str(), "package:org.renderdoc.")) continue; - packages.push_back(package); - } - } + if(!strncmp(line.c_str(), "package:", 8)) + { + PathEntry pkg; + pkg.filename = trim(line.substr(8)); + pkg.size = 0; + pkg.lastmod = 0; + pkg.flags = PathProperty::Directory; - return packages; + packages.push_back(pkg); + } + } + + adbStdout = Android::adbExecCommand(deviceID, "shell dumpsys package").strStdout; + + split(adbStdout, lines, '\n'); + + for(const std::string &line : lines) + { + // quick check, look for a / + if(line.find('/') == std::string::npos) + continue; + + // line should be something like: ' 78f9sba com.package.name/.NameOfActivity .....' + + const char *c = line.c_str(); + + // expect whitespace + while(*c && isspace(*c)) + c++; + + // expect hex + while(*c && ((*c >= '0' && *c <= '9') || (*c >= 'a' && *c <= 'f'))) + c++; + + // expect space + if(*c != ' ') + continue; + + c++; + + // expect the package now. Search to see if it's one of the ones we listed above + std::string package; + + for(const PathEntry &p : packages) + if(!strncmp(c, p.filename.c_str(), p.filename.size())) + package = p.filename; + + // didn't find a matching package + if(package.empty()) + continue; + + c += package.size(); + + // expect a / + if(*c != '/') + continue; + + c++; + + const char *end = strchr(c, ' '); + + if(end == NULL) + end = c + strlen(c); + + while(isspace(*(end - 1))) + end--; + + m_AndroidActivities.insert({package, rdcstr(c, end - c)}); + } + + return packages; + } + else + { + rdcstr package = path; + + if(!package.empty() && package[0] == '/') + package.erase(0); + + std::vector activities; + + for(const Activity &act : m_AndroidActivities) + { + if(act.package == package) + { + PathEntry activity; + if(act.activity[0] == '.') + activity.filename = package + act.activity; + else + activity.filename = act.activity; + activity.size = 0; + activity.lastmod = 0; + activity.flags = PathProperty::Executable; + activities.push_back(activity); + } + } + + PathEntry defaultActivity; + defaultActivity.filename = "#DefaultActivity"; + defaultActivity.size = 0; + defaultActivity.lastmod = 0; + defaultActivity.flags = PathProperty::Executable; + + // if there's only one activity listed, assume it's the default and don't add a virtual + // entry + if(activities.size() != 1) + activities.push_back(defaultActivity); + + return activities; + } } { @@ -1859,6 +1959,21 @@ private: Android::LogcatThread *m_LogcatThread; std::vector > m_Proxies; + + struct Activity + { + rdcstr package; + rdcstr activity; + + bool operator<(const Activity &o) const + { + if(package != o.package) + return package < o.package; + return activity < o.activity; + } + }; + + std::set m_AndroidActivities; }; extern "C" RENDERDOC_API ReplayStatus RENDERDOC_CC