diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp index e702d487b01..7567cc06183 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 &fi, QStringList selector, - const QString &packageName, bool preNougat) +static void findProcessPIDAndUser(QFutureInterface &fi, QStringList selector, + const QString &packageName, bool preNougat) { if (packageName.isEmpty()) return; @@ -108,8 +109,32 @@ static void findProcessPID(QFutureInterface &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 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 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 e8c340d1a37..7e41d2e9c73 100644 --- a/src/plugins/android/androidrunnerworker.h +++ b/src/plugins/android/androidrunnerworker.h @@ -10,6 +10,7 @@ #include #include +#include QT_BEGIN_NAMESPACE class QProcess; @@ -26,6 +27,8 @@ namespace Internal { const int MIN_SOCKET_HANDSHAKE_PORT = 20001; +using PidUserPair = std::pair; + 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 m_adbLogcatProcess; std::unique_ptr m_psIsAlive; QByteArray m_stdoutBuffer; QByteArray m_stderrBuffer; - QFuture m_pidFinder; + QFuture m_pidFinder; bool m_useCppDebugger = false; bool m_useLldb = false; // FIXME: Un-implemented currently. QmlDebug::QmlDebugServicesPreset m_qmlDebugServices;