diff options
author | Zoltan Gera <zoltan.gera@qt.io> | 2023-04-12 18:11:34 +0300 |
---|---|---|
committer | Zoltan Gera <zoltan.gera@qt.io> | 2023-04-14 10:51:44 +0000 |
commit | 2d01003c4e37041efc63bd536d1cd06ce4928bf7 (patch) | |
tree | 525337b58ab60a5f62e7646fc7ad0d717ae66f5a | |
parent | 9beec994529b6ff80aff1dc7714ecfdf52d3e30b (diff) | |
download | qt-creator-2d01003c4e37041efc63bd536d1cd06ce4928bf7.tar.gz |
Android: Automotive debugging capability
Android Automotive images use special system users to run Android apps
and activities. To maintain compatibility with all Android flavors not
only the process ID but also the user ID of the process should be
detected in order to be able to start the debugger with the correct
user credentials. Failing to do so caused the debugging server to
terminate upon not being able to connect to the app to be debugged.
This also disabled the native debugging in Qt Creator.
Task-number: QTCREATORBUG-28851
Change-Id: Ib4cd0ba7f252096cb7b8b14f959c8f0c743d8bf2
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
-rw-r--r-- | src/plugins/android/androidrunnerworker.cpp | 188 | ||||
-rw-r--r-- | src/plugins/android/androidrunnerworker.h | 9 |
2 files changed, 124 insertions, 73 deletions
diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp index e702d487b0..7567cc0618 100644 --- a/src/plugins/android/androidrunnerworker.cpp +++ b/src/plugins/android/androidrunnerworker.cpp @@ -51,6 +51,7 @@ namespace Android { namespace Internal { static const QString pidPollingScript = QStringLiteral("while [ -d /proc/%1 ]; do sleep 1; done"); +static const QRegularExpression userIdPattern("u(\\d+)_a"); static int APP_START_TIMEOUT = 45000; static bool isTimedOut(const chrono::high_resolution_clock::time_point &start, @@ -77,8 +78,8 @@ static qint64 extractPID(const QString &output, const QString &packageName) return pid; } -static void findProcessPID(QFutureInterface<qint64> &fi, QStringList selector, - const QString &packageName, bool preNougat) +static void findProcessPIDAndUser(QFutureInterface<PidUserPair> &fi, QStringList selector, + const QString &packageName, bool preNougat) { if (packageName.isEmpty()) return; @@ -108,8 +109,32 @@ static void findProcessPID(QFutureInterface<qint64> &fi, QStringList selector, } while ((processPID == -1 || processPID == 0) && !isTimedOut(start) && !fi.isCanceled()); qCDebug(androidRunWorkerLog) << "PID found:" << processPID << ", PreNougat:" << preNougat; + + qint64 processUser = 0; + if (processPID > 0 && !fi.isCanceled()) { + args = {selector}; + args.append({"shell", "ps", "-o", "user", "-p"}); + args.append(QString::number(processPID)); + QtcProcess proc; + proc.setCommand({adbPath, args}); + proc.runBlocking(); + const QString out = proc.allOutput(); + if (!out.isEmpty()) { + QRegularExpressionMatch match; + qsizetype matchPos = out.indexOf(userIdPattern, 0, &match); + if (matchPos >= 0 && match.hasCaptured(1) && match.capturedLength(1) > 0) { + bool ok = false; + processUser = match.captured(1).toInt(&ok); + if (!ok) + processUser = 0; + } + } + } + + qCDebug(androidRunWorkerLog) << "USER found:" << processUser; + if (!fi.isCanceled()) - fi.reportResult(processPID); + fi.reportResult(PidUserPair(processPID, processUser)); } static void deleter(QProcess *p) @@ -325,13 +350,16 @@ bool AndroidRunnerWorker::uploadDebugServer(const QString &debugServerFileName) return false; } + QStringList adbArgs = {"shell", "run-as", m_packageName}; + if (m_processUser > 0) + adbArgs << "--user" << QString::number(m_processUser); // Copy gdbserver from temp location to app directory - if (!runAdb({"shell", "run-as", m_packageName, "cp" , tempDebugServerPath, debugServerFileName})) { + if (!runAdb(adbArgs + QStringList({"cp" , tempDebugServerPath, debugServerFileName}))) { qCDebug(androidRunWorkerLog) << "Debug server copy from temp directory failed"; return false; } - const bool ok = runAdb({"shell", "run-as", m_packageName, "chmod", "777", debugServerFileName}); + const bool ok = runAdb(adbArgs + QStringList({"chmod", "777", debugServerFileName})); QTC_ASSERT(ok, qCDebug(androidRunWorkerLog) << "Debug server chmod 777 failed."); return true; } @@ -346,7 +374,11 @@ bool AndroidRunnerWorker::deviceFileExists(const QString &filePath) bool AndroidRunnerWorker::packageFileExists(const QString &filePath) { QString output; - const bool success = runAdb({"shell", "run-as", m_packageName, "ls", filePath, "2>/dev/null"}, &output); + QStringList adbArgs = {"shell", "run-as", m_packageName}; + if (m_processUser > 0) + adbArgs << "--user" << QString::number(m_processUser); + const bool success = runAdb(adbArgs + QStringList({"ls", filePath, "2>/dev/null"}), + &output); return success && !output.trimmed().isEmpty(); } @@ -513,62 +545,8 @@ void AndroidRunnerWorker::asyncStartHelper() QStringList args({"shell", "am", "start"}); args << "-n" << m_intentName; - if (m_useCppDebugger) { + if (m_useCppDebugger) args << "-D"; - // run-as <package-name> pwd fails on API 22 so route the pwd through shell. - QString packageDir; - if (!runAdb({"shell", "run-as", m_packageName, "/system/bin/sh", "-c", "pwd"}, - &packageDir)) { - emit remoteProcessFinished(Tr::tr("Failed to find application directory.")); - return; - } - - // Add executable flag to package dir. Gdb can't connect to running server on device on - // e.g. on Android 8 with NDK 10e - runAdb({"shell", "run-as", m_packageName, "chmod", "a+x", packageDir.trimmed()}); - - if (!m_debugServerPath.exists()) { - QString msg = Tr::tr("Cannot find C++ debug server in NDK installation."); - if (m_useLldb) - msg += "\n" + Tr::tr("The lldb-server binary has not been found."); - emit remoteProcessFinished(msg); - return; - } - - QString debugServerFile; - if (m_useLldb) { - debugServerFile = "./lldb-server"; - runAdb({"shell", "run-as", m_packageName, "killall", "lldb-server"}); - if (!uploadDebugServer(debugServerFile)) { - emit remoteProcessFinished(Tr::tr("Cannot copy C++ debug server.")); - return; - } - } else { - if (packageFileExists("./lib/gdbserver")) { - debugServerFile = "./lib/gdbserver"; - qCDebug(androidRunWorkerLog) << "Found GDB server " + debugServerFile; - runAdb({"shell", "run-as", m_packageName, "killall", "gdbserver"}); - } else if (packageFileExists("./lib/libgdbserver.so")) { - debugServerFile = "./lib/libgdbserver.so"; - qCDebug(androidRunWorkerLog) << "Found GDB server " + debugServerFile; - runAdb({"shell", "run-as", m_packageName, "killall", "libgdbserver.so"}); - } else { - // Armv8. symlink lib is not available. - debugServerFile = "./gdbserver"; - // Kill the previous instances of gdbserver. Do this before copying the gdbserver. - runAdb({"shell", "run-as", m_packageName, "killall", "gdbserver"}); - if (!uploadDebugServer("./gdbserver")) { - emit remoteProcessFinished(Tr::tr("Cannot copy C++ debug server.")); - return; - } - } - } - QString debuggerServerErr; - if (!startDebuggerServer(packageDir, debugServerFile, &debuggerServerErr)) { - emit remoteProcessFinished(debuggerServerErr); - return; - } - } if (m_qmlDebugServices != QmlDebug::NoQmlDebugServices) { // currently forward to same port on device and host @@ -624,17 +602,79 @@ void AndroidRunnerWorker::asyncStartHelper() } } +void AndroidRunnerWorker::startNativeDebugging() +{ + // run-as <package-name> pwd fails on API 22 so route the pwd through shell. + QString packageDir; + QStringList adbArgs = {"shell", "run-as", m_packageName}; + if (m_processUser > 0) + adbArgs << "--user" << QString::number(m_processUser); + if (!runAdb(adbArgs + QStringList({"/system/bin/sh", "-c", "pwd"}), + &packageDir)) { + emit remoteProcessFinished(Tr::tr("Failed to find application directory.")); + return; + } + // Add executable flag to package dir. Gdb can't connect to running server on device on + // e.g. on Android 8 with NDK 10e + runAdb(adbArgs + QStringList({"chmod", "a+x", packageDir.trimmed()})); + if (!m_debugServerPath.exists()) { + QString msg = Tr::tr("Cannot find C++ debug server in NDK installation."); + if (m_useLldb) + msg += "\n" + Tr::tr("The lldb-server binary has not been found."); + emit remoteProcessFinished(msg); + return; + } + + QString debugServerFile; + if (m_useLldb) { + debugServerFile = "./lldb-server"; + runAdb(adbArgs + QStringList({"killall", "lldb-server"})); + if (!uploadDebugServer(debugServerFile)) { + emit remoteProcessFinished(Tr::tr("Cannot copy C++ debug server.")); + return; + } + } else { + if (packageFileExists("./lib/gdbserver")) { + debugServerFile = "./lib/gdbserver"; + qCDebug(androidRunWorkerLog) << "Found GDB server " + debugServerFile; + runAdb(adbArgs + QStringList({"killall", "gdbserver"})); + } else if (packageFileExists("./lib/libgdbserver.so")) { + debugServerFile = "./lib/libgdbserver.so"; + qCDebug(androidRunWorkerLog) << "Found GDB server " + debugServerFile; + runAdb(adbArgs + QStringList({"killall", "libgdbserver.so"})); + } else { + // Armv8. symlink lib is not available. + debugServerFile = "./gdbserver"; + // Kill the previous instances of gdbserver. Do this before copying the gdbserver. + runAdb(adbArgs + QStringList({"killall", "gdbserver"})); + if (!uploadDebugServer("./gdbserver")) { + emit remoteProcessFinished(Tr::tr("Cannot copy C++ debug server.")); + return; + } + } + } + QString debuggerServerErr; + if (!startDebuggerServer(packageDir, debugServerFile, &debuggerServerErr)) { + emit remoteProcessFinished(debuggerServerErr); + return; + } +} + bool AndroidRunnerWorker::startDebuggerServer(const QString &packageDir, const QString &debugServerFile, QString *errorStr) { + QStringList adbArgs = {"shell", "run-as", m_packageName}; + if (m_processUser > 0) + adbArgs << "--user" << QString::number(m_processUser); if (m_useLldb) { QString lldbServerErr; QStringList lldbServerArgs = selector(); - lldbServerArgs << "shell" << "run-as" << m_packageName << debugServerFile - << "platform" - // << "--server" // Can lead to zombie servers - << "--listen" << QString("*:%1").arg(m_localDebugServerPort.toString()); + lldbServerArgs += adbArgs; + lldbServerArgs << debugServerFile + << "platform" + // << "--server" // Can lead to zombie servers + << "--listen" << QString("*:%1").arg(m_localDebugServerPort.toString()); m_debugServerProcess.reset(AndroidManager::runAdbCommandDetached(lldbServerArgs, &lldbServerErr)); if (!m_debugServerProcess) { @@ -648,12 +688,13 @@ bool AndroidRunnerWorker::startDebuggerServer(const QString &packageDir, } else { QString gdbServerSocket = packageDir + "/debug-socket"; - runAdb({"shell", "run-as", m_packageName, "rm", gdbServerSocket}); + runAdb(adbArgs + QStringList({"rm", gdbServerSocket})); QString gdbProcessErr; QStringList gdbServerErr = selector(); - gdbServerErr << "shell" << "run-as" << m_packageName << debugServerFile - << "--multi" << "+" + gdbServerSocket; + gdbServerErr += adbArgs; + gdbServerErr << debugServerFile + << "--multi" << "+" + gdbServerSocket; m_debugServerProcess.reset(AndroidManager::runAdbCommandDetached(gdbServerErr, &gdbProcessErr)); if (!m_debugServerProcess) { @@ -683,7 +724,7 @@ void AndroidRunnerWorker::asyncStart() { asyncStartHelper(); - m_pidFinder = Utils::onResultReady(Utils::runAsync(findProcessPID, selector(), + m_pidFinder = Utils::onResultReady(Utils::runAsync(findProcessPIDAndUser, selector(), m_packageName, m_isPreNougat), bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1)); } @@ -793,13 +834,16 @@ void AndroidRunnerWorker::removeForwardPort(const QString &port) } } -void AndroidRunnerWorker::onProcessIdChanged(qint64 pid) +void AndroidRunnerWorker::onProcessIdChanged(PidUserPair pidUser) { + qint64 pid = pidUser.first; + qint64 user = pidUser.second; // Don't write to m_psProc from a different thread QTC_ASSERT(QThread::currentThread() == thread(), return); qCDebug(androidRunWorkerLog) << "Process ID changed from:" << m_processPID << "to:" << pid; m_processPID = pid; + m_processUser = user; if (pid == -1) { emit remoteProcessFinished(QLatin1String("\n\n") + Tr::tr("\"%1\" died.") .arg(m_packageName)); @@ -813,6 +857,8 @@ void AndroidRunnerWorker::onProcessIdChanged(qint64 pid) for (const QString &entry: std::as_const(m_afterFinishAdbCommands)) runAdb(entry.split(' ', Qt::SkipEmptyParts)); } else { + if (m_useCppDebugger) + startNativeDebugging(); // In debugging cases this will be funneled to the engine to actually start // and attach gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below. emit remoteProcessStarted(m_localDebugServerPort, m_qmlServer, m_processPID); @@ -824,7 +870,7 @@ void AndroidRunnerWorker::onProcessIdChanged(qint64 pid) m_psIsAlive->setObjectName("IsAliveProcess"); m_psIsAlive->setProcessChannelMode(QProcess::MergedChannels); connect(m_psIsAlive.get(), &QProcess::finished, - this, bind(&AndroidRunnerWorker::onProcessIdChanged, this, -1)); + this, bind(&AndroidRunnerWorker::onProcessIdChanged, this, PidUserPair(-1, -1))); } } diff --git a/src/plugins/android/androidrunnerworker.h b/src/plugins/android/androidrunnerworker.h index e8c340d1a3..7e41d2e9c7 100644 --- a/src/plugins/android/androidrunnerworker.h +++ b/src/plugins/android/androidrunnerworker.h @@ -10,6 +10,7 @@ #include <utils/port.h> #include <QFuture> +#include <utility> QT_BEGIN_NAMESPACE class QProcess; @@ -26,6 +27,8 @@ namespace Internal { const int MIN_SOCKET_HANDSHAKE_PORT = 20001; +using PidUserPair = std::pair<qint64, qint64>; + class AndroidRunnerWorker : public QObject { Q_OBJECT @@ -61,6 +64,7 @@ signals: private: void asyncStartHelper(); + void startNativeDebugging(); bool startDebuggerServer(const QString &packageDir, const QString &debugServerFile, QString *errorStr = nullptr); bool deviceFileExists(const QString &filePath); bool packageFileExists(const QString& filePath); @@ -72,7 +76,7 @@ private: Waiting, Settled }; - void onProcessIdChanged(qint64 pid); + void onProcessIdChanged(PidUserPair pidUser); using Deleter = void (*)(QProcess *); // Create the processes and timer in the worker thread, for correct thread affinity @@ -83,11 +87,12 @@ private: QStringList m_afterFinishAdbCommands; QStringList m_amStartExtraArgs; qint64 m_processPID = -1; + qint64 m_processUser = -1; std::unique_ptr<QProcess, Deleter> m_adbLogcatProcess; std::unique_ptr<QProcess, Deleter> m_psIsAlive; QByteArray m_stdoutBuffer; QByteArray m_stderrBuffer; - QFuture<qint64> m_pidFinder; + QFuture<PidUserPair> m_pidFinder; bool m_useCppDebugger = false; bool m_useLldb = false; // FIXME: Un-implemented currently. QmlDebug::QmlDebugServicesPreset m_qmlDebugServices; |