Allow choosing activities for an android program. Closes #1387

This commit is contained in:
baldurk
2019-06-04 18:18:09 +01:00
parent e8e3972953
commit 4738844122
9 changed files with 222 additions and 44 deletions
+13 -5
View File
@@ -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;
}
}
@@ -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();
@@ -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:
+7 -3
View File
@@ -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();
+3 -1
View File
@@ -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);
+10 -10
View File
@@ -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);
+34
View File
@@ -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")
+3 -4
View File
@@ -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,
+136 -21
View File
@@ -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<PathEntry> packages;
while(getline(stdoutStream, line))
if(path[0] == 0 || (path[0] == '/' && path[1] == 0))
{
std::vector<std::string> 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<std::string> lines;
split(adbStdout, lines, '\n');
std::vector<PathEntry> 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<PathEntry> 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<rdcpair<RDCDriver, std::string> > 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<Activity> m_AndroidActivities;
};
extern "C" RENDERDOC_API ReplayStatus RENDERDOC_CC