#include "AdbUtils.h" #include #include #include #include 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 > AdbUtils::getListDevices() { QProcess process; process.start(adbPath(), {"devices"}); process.waitForFinished(); const QString output = process.readAllStandardOutput(); QList > 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(" 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 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; }