1
0
forked from BRT/arc
arc/android/adb/AdbUtils.cpp
trnsmkot 8bc198a787 Add SettingsDAO implementation, schema migration, and related updates
Implemented `SettingsDAO` with `get`, `set`, `remove`, and related methods for key-value storage. Added database schema migration for a new `settings` table. Updated `EventDAO` with error handling improvements and `AbstractDAO` with helper utilities. Enhanced Ozon scripts with ad banner detection and handling.
2026-03-09 15:53:35 +07:00

343 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "AdbUtils.h"
#include <QProcess>
#include <qDebug>
#include <QString>
#include <QCoreApplication>
AdbUtils::AdbUtils(QObject *parent) : QObject(parent) {
}
QString AdbUtils::adbPath() {
#ifdef Q_OS_WIN
return QCoreApplication::applicationDirPath() + "/tools/adb/windows/adb.exe";
#else
return QCoreApplication::applicationDirPath() + "/tools/adb/macos/adb";
#endif
}
bool AdbUtils::startProcess(const QString &program, const QStringList &args,
QString *stdOut, QString *stdErr) {
QProcess process;
process.start(program, args);
if (!process.waitForFinished() ||
process.exitStatus() != QProcess::NormalExit ||
process.exitCode() != 0) {
if (stdErr) {
*stdErr = QString::fromUtf8(process.readAllStandardError());
}
return false;
}
if (stdOut) {
*stdOut = QString::fromUtf8(process.readAllStandardOutput());
}
return true;
}
// adb shell dumpsys package ru.rshb.dbo | grep permission
// TODO доделать обработку пермишинов
// adb shell pm revoke ru.rshb.dbo android.permission.WRITE_CONTACTS; adb shell pm revoke ru.rshb.dbo android.permission.READ_CONTACTS; adb shell pm revoke ru.rshb.dbo android.permission.ACCESS_COARSE_LOCATION; adb shell pm revoke ru.rshb.dbo android.permission.ACCESS_FINE_LOCATION
// adb shell cmd appops set ru.rshb.dbo POST_NOTIFICATION ignore
/**
* Возвращает список найденных устройств в виде пар (ID устройства и Статус)
* @return [(id, status), ...]
*/
QList<QPair<QString, QString> > AdbUtils::getListDevices() {
QProcess process;
process.start(adbPath(), {"devices"});
process.waitForFinished();
const QString output = process.readAllStandardOutput();
QList<QPair<QString, QString> > devices;
const QStringList lines = output.split('\n');
for (int i = 1; i < lines.size(); ++i) {
QString line = lines.at(i).trimmed();
if (line.isEmpty()) {
continue;
}
/**
* device — устройство готово к использованию.
* offline — устройство подключено, но не может быть использовано.
* unauthorized — устройство не авторизовано для работы с ADB (нужно подтвердить запрос на подключение на самом устройстве).
* recovery — устройство в режиме восстановления.
*/
if (QStringList parts = line.split('\t'); parts.size() == 2) {
const QString &id = parts.at(0);
const QString &status = parts.at(1);
devices.append(qMakePair(id, status));
}
}
return devices;
}
QString AdbUtils::tryToGetRunningAppPid(const QString &deviceId, const QString &packageName) {
QProcess process;
process.start(adbPath(), {
"-s", deviceId,
"shell", "pidof", packageName
});
process.waitForFinished();
QString pid = process.readAllStandardOutput();
return pid.replace("\n", "");
}
bool AdbUtils::tryToStartApplication(const QString &deviceId, const QString &packageName) {
QProcess process;
process.start(adbPath(), {
"-s", deviceId,
"shell", "monkey",
"-p", packageName,
"-c", "android.intent.category.LAUNCHER",
"1"
});
process.waitForFinished();
const QString output = process.readAllStandardOutput();
// Если процесс запустился, там будет такая строка.
// Если нет, то: "** No activities found to run, monkey aborted."
if (output.contains("Events injected: 1")) {
return true;
}
// Тут надо в логи писать, почему не стартует
qDebug() << "Start app output:" << output;
return false;
}
bool AdbUtils::tryToKillApplication(const QString &deviceId, const QString &packageName) {
QProcess process;
process.start(adbPath(), {
"-s", deviceId,
"shell", "am",
"force-stop", packageName
});
process.waitForFinished();
return process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0;
}
// idx - временный лог
// adb -s 6edc4a47 shell 'uiautomator dump /sdcard/uidump.xml' && adb -s 6edc4a47 pull /sdcard/uidump.xml step_1.xml && adb -s 6edc4a47 shell 'rm /sdcard/uidump.xml'
QString AdbUtils::getScreenDumpAsXml(const QString &deviceId, int idx, bool screenshot) {
const QString adb = adbPath();
QString output;
// 1. Dump UI hierarchy
if (!startProcess(adb, {"-s", deviceId, "shell", "uiautomator", "dump", "/sdcard/uidump.xml"})) {
qWarning() << "Failed to dump UI hierarchy.";
return {};
}
// 2. Optionally pull and save the XML file
if (screenshot) {
if (!startProcess(adb, {"-s", deviceId, "pull", "/sdcard/uidump.xml",
QString("step_%1.xml").arg(idx)})) {
qWarning() << "Failed to pull UIDump XML.";
return {};
}
}
// 3. Read the XML content
QString xmlOut;
if (!startProcess(adb, {"-s", deviceId, "shell", "cat", "/sdcard/uidump.xml"}, &xmlOut)) {
qWarning() << "Failed to read UIDump XML.";
return {};
}
// 4. Remove temp XML file
startProcess(adb, {"-s", deviceId, "shell", "rm", "/sdcard/uidump.xml"}); // неважно, успешна ли команда
// 5. Screenshot (if needed)
if (screenshot) {
startProcess(adb, {"-s", deviceId, "shell", "screencap", "-p", "/sdcard/screen.png"});
startProcess(adb, {"-s", deviceId, "pull", "/sdcard/screen.png",
QString("screen_step_%1.png").arg(idx)});
startProcess(adb, {"-s", deviceId, "shell", "rm", "/sdcard/screen.png"});
}
// 6. Обрезка к началу XML
QByteArray xmlBytes = xmlOut.toUtf8();
if (const int start = xmlBytes.indexOf("<?xml"); start != -1) {
xmlBytes = xmlBytes.mid(start);
}
return QString::fromUtf8(xmlBytes);
}
bool AdbUtils::takeScreenshot(const QString &deviceId, const QString &name) {
const QString adb = adbPath();
struct Step { QStringList args; };
Step steps[] = {
{{"-s", deviceId, "shell", "screencap", "-p", "/sdcard/screen.png"}},
{{"-s", deviceId, "pull", "/sdcard/screen.png", name + ".png"}},
{{"-s", deviceId, "shell", "rm", "/sdcard/screen.png"}}
};
for (const auto &step : steps) {
QString stdErr;
if (!startProcess(adb, step.args, nullptr, &stdErr)) {
qWarning() << "ADB command failed:" << step.args.join(' ');
qWarning() << "Error:" << stdErr;
return false;
}
}
return true;
}
QByteArray AdbUtils::captureScreenshotBytes(const QString &deviceId) {
QProcess process;
process.start(adbPath(), {"-s", deviceId, "exec-out", "screencap", "-p"});
if (!process.waitForFinished(10000) || process.exitCode() != 0) {
qWarning() << "[AdbUtils] captureScreenshotBytes failed for" << deviceId;
return {};
}
return process.readAllStandardOutput();
}
bool AdbUtils::takeXmlDump(const QString &deviceId, const QString &name) {
const QString adb = adbPath();
struct Step { QStringList args; };
Step steps[] = {
{{"-s", deviceId, "shell", "uiautomator", "dump", "/sdcard/uidump.xml"}},
{{"-s", deviceId, "pull", "/sdcard/uidump.xml", name + ".xml"}},
{{"-s", deviceId, "shell", "cat", "/sdcard/uidump.xml"}},
{{"-s", deviceId, "shell", "rm", "/sdcard/uidump.xml"}}
};
for (const auto &step : steps) {
QString stdErr;
if (!startProcess(adb, step.args, nullptr, &stdErr)) {
qWarning() << "ADB command failed:" << step.args.join(' ');
qWarning() << "Error:" << stdErr;
return false;
}
}
return true;
}
bool AdbUtils::makeTap(const QString &deviceId, const int x, const int y) {
QProcess process;
process.start(adbPath(), {
"-s", deviceId,
"shell", "input",
"tap", QString::number(x), QString::number(y)
});
process.waitForFinished();
return process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0;
}
bool AdbUtils::enableAdbIMEKeyboard(const QString &deviceId) {
const QString adb = adbPath();
struct Step { QStringList args; };
Step steps[] = {
{{"-s", deviceId, "shell", "ime", "enable", "com.android.adbkeyboard/.AdbIME"}},
{{"-s", deviceId, "shell", "ime", "set", "com.android.adbkeyboard/.AdbIME"}}
};
for (const auto &step : steps) {
QString stdErr;
if (!startProcess(adb, step.args, nullptr, &stdErr)) {
qWarning() << "ADB command failed:" << step.args.join(' ');
qWarning() << "Error:" << stdErr;
return false;
}
}
return true;
}
bool AdbUtils::inputAdbIMEText(const QString &deviceId, const QString &text) {
QProcess process;
process.start(adbPath(), {
"-s", deviceId,
"shell", "am", "broadcast",
"-a", "ADB_INPUT_TEXT",
"--es", "msg", text
});
process.waitForFinished();
qDebug() << "[AdbUtils] inputAdbIMEText result:" << process.exitCode()
<< "output:" << process.readAllStandardOutput().trimmed()
<< "error:" << process.readAllStandardError().trimmed();
return process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0;
}
bool AdbUtils::disableAdbIMEKeyboard(const QString &deviceId) {
QProcess process;
process.start(adbPath(), {"-s", deviceId, "shell", "ime", "disable",
"com.android.adbkeyboard/.AdbIME"});
process.waitForFinished();
return process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0;
}
bool AdbUtils::makeSwipe(const QString &deviceId, const int x1, const int y1, const int x2, const int y2) {
QProcess process;
process.start(
adbPath(),
{
"-s", deviceId,
"shell", "input",
"swipe",
QString::number(x1), QString::number(y1),
QString::number(x2), QString::number(y2)
}
);
process.waitForFinished();
return process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0;
}
bool AdbUtils::inputText(const QString &deviceId, const QString &text) {
QProcess process;
process.start(adbPath(), {
"-s", deviceId,
"shell", "input",
"text", text
});
process.waitForFinished();
return process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0;
}
bool AdbUtils::hideKeyboard(const QString &deviceId) {
QProcess process;
process.start(adbPath(), {
"-s", deviceId,
"shell", "input",
"keyevent", "111"
});
process.waitForFinished();
return process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0;
}
bool AdbUtils::goBack(const QString &deviceId) {
QProcess process;
process.start(adbPath(), {
"-s", deviceId,
"shell", "input",
"keyevent", "4"
});
process.waitForFinished();
return process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0;
}
QSet<QString> AdbUtils::getListApps(const QString &deviceId) {
QProcess process;
process.start(adbPath(), {
"-s", deviceId,
"shell", "pm list",
"packages", "-3"
});
process.waitForFinished();
const QString output = process.readAllStandardOutput();
QSet<QString> apps;
const QStringList lines = output.split("\n");
for (int i = 0; i < lines.size(); ++i) {
QString line = lines.at(i).trimmed().replace("package:", "");
if (line.isEmpty()) {
continue;
}
apps.insert(line);
}
return apps;
}