forked from qt-creator/qt-creator
Android: Add Android tool manager
Refactor the use of android tool and groundwork for the new sdk and avd management tool's integration Task-number: QTCREATORBUG-17814 Change-Id: I6a5920f9ba92508f904cd8cf28bf62c82de2d820 Reviewed-by: BogDan Vatra <bogdan@kdab.com>
This commit is contained in:
@@ -28,8 +28,10 @@
|
||||
#include "androidtoolchain.h"
|
||||
#include "androiddevice.h"
|
||||
#include "androidgdbserverkitinformation.h"
|
||||
#include "androidmanager.h"
|
||||
#include "androidqtversion.h"
|
||||
#include "androiddevicedialog.h"
|
||||
#include "androidtoolmanager.h"
|
||||
#include "avddialog.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
@@ -113,33 +115,6 @@ namespace {
|
||||
+ QLatin1String("/qtcreator/android.xml");
|
||||
}
|
||||
|
||||
bool androidDevicesLessThan(const AndroidDeviceInfo &dev1, const AndroidDeviceInfo &dev2)
|
||||
{
|
||||
if (dev1.serialNumber.contains(QLatin1String("????")) != dev2.serialNumber.contains(QLatin1String("????")))
|
||||
return !dev1.serialNumber.contains(QLatin1String("????"));
|
||||
if (dev1.type != dev2.type)
|
||||
return dev1.type == AndroidDeviceInfo::Hardware;
|
||||
if (dev1.sdk != dev2.sdk)
|
||||
return dev1.sdk < dev2.sdk;
|
||||
if (dev1.avdname != dev2.avdname)
|
||||
return dev1.avdname < dev2.avdname;
|
||||
|
||||
return dev1.serialNumber < dev2.serialNumber;
|
||||
}
|
||||
|
||||
static QStringList cleanAndroidABIs(const QStringList &abis)
|
||||
{
|
||||
QStringList res;
|
||||
foreach (const QString &abi, abis) {
|
||||
int index = abi.lastIndexOf(QLatin1Char('/'));
|
||||
if (index == -1)
|
||||
res << abi;
|
||||
else
|
||||
res << abi.mid(index + 1);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool is32BitUserSpace()
|
||||
{
|
||||
// Do the exact same check as android's emulator is doing:
|
||||
@@ -162,25 +137,6 @@ namespace {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Some preview sdks use a non integer version
|
||||
int apiLevelFromAndroidList(const QString &string)
|
||||
{
|
||||
bool ok;
|
||||
int result = string.toInt(&ok);
|
||||
if (ok)
|
||||
return result;
|
||||
Utils::FileName sdkLocation = AndroidConfigurations::currentConfig().sdkLocation();
|
||||
sdkLocation.appendPath(QLatin1String("/platforms/android-") + string + QLatin1String("/source.properties"));
|
||||
result = QSettings(sdkLocation.toString(), QSettings::IniFormat).value(QLatin1String("AndroidVersion.ApiLevel")).toInt(&ok);
|
||||
if (ok)
|
||||
return result;
|
||||
if (string == QLatin1String("L"))
|
||||
return 21;
|
||||
if (string == QLatin1String("MNC"))
|
||||
return 22;
|
||||
return 23; // At least
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
@@ -372,48 +328,11 @@ void AndroidConfig::updateAvailableSdkPlatforms() const
|
||||
{
|
||||
if (m_availableSdkPlatformsUpToDate)
|
||||
return;
|
||||
|
||||
m_availableSdkPlatforms.clear();
|
||||
|
||||
SynchronousProcess proc;
|
||||
proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
|
||||
SynchronousProcessResponse response
|
||||
= proc.runBlocking(androidToolPath().toString(),
|
||||
QStringList({"list", "target"})); // list available AVDs
|
||||
if (response.result != SynchronousProcessResponse::Finished)
|
||||
return;
|
||||
|
||||
SdkPlatform platform;
|
||||
foreach (const QString &l, response.allOutput().split('\n')) {
|
||||
const QString line = l.trimmed();
|
||||
if (line.startsWith(QLatin1String("id:")) && line.contains(QLatin1String("android-"))) {
|
||||
int index = line.indexOf(QLatin1String("\"android-"));
|
||||
if (index == -1)
|
||||
continue;
|
||||
QString androidTarget = line.mid(index + 1, line.length() - index - 2);
|
||||
const QString tmp = androidTarget.mid(androidTarget.lastIndexOf(QLatin1Char('-')) + 1);
|
||||
platform.apiLevel = apiLevelFromAndroidList(tmp);
|
||||
} else if (line.startsWith(QLatin1String("Name:"))) {
|
||||
platform.name = line.mid(6);
|
||||
} else if (line.startsWith(QLatin1String("Tag/ABIs :"))) {
|
||||
platform.abis = cleanAndroidABIs(line.mid(10).trimmed().split(QLatin1String(", ")));
|
||||
} else if (line.startsWith(QLatin1String("ABIs"))) {
|
||||
platform.abis = cleanAndroidABIs(line.mid(6).trimmed().split(QLatin1String(", ")));
|
||||
} else if (line.startsWith(QLatin1String("---")) || line.startsWith(QLatin1String("==="))) {
|
||||
if (platform.apiLevel == -1)
|
||||
continue;
|
||||
auto it = std::lower_bound(m_availableSdkPlatforms.begin(), m_availableSdkPlatforms.end(),
|
||||
platform, sortSdkPlatformByApiLevel);
|
||||
m_availableSdkPlatforms.insert(it, platform);
|
||||
platform = SdkPlatform();
|
||||
}
|
||||
}
|
||||
|
||||
if (platform.apiLevel != -1) {
|
||||
auto it = std::lower_bound(m_availableSdkPlatforms.begin(), m_availableSdkPlatforms.end(),
|
||||
platform, sortSdkPlatformByApiLevel);
|
||||
m_availableSdkPlatforms.insert(it, platform);
|
||||
}
|
||||
|
||||
AndroidToolManager toolManager(*this);
|
||||
m_availableSdkPlatforms = toolManager.availableSdkPlatforms();
|
||||
Utils::sort(m_availableSdkPlatforms, sortSdkPlatformByApiLevel);
|
||||
m_availableSdkPlatformsUpToDate = true;
|
||||
}
|
||||
|
||||
@@ -446,18 +365,6 @@ FileName AndroidConfig::adbToolPath() const
|
||||
return path.appendPath(QLatin1String("platform-tools/adb" QTC_HOST_EXE_SUFFIX));
|
||||
}
|
||||
|
||||
Environment AndroidConfig::androidToolEnvironment() const
|
||||
{
|
||||
Environment env = Environment::systemEnvironment();
|
||||
if (!m_openJDKLocation.isEmpty()) {
|
||||
env.set(QLatin1String("JAVA_HOME"), m_openJDKLocation.toUserOutput());
|
||||
Utils::FileName binPath = m_openJDKLocation;
|
||||
binPath.appendPath(QLatin1String("bin"));
|
||||
env.prependOrSetPath(binPath.toUserOutput());
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
FileName AndroidConfig::androidToolPath() const
|
||||
{
|
||||
if (HostOsInfo::isWindowsHost()) {
|
||||
@@ -583,7 +490,7 @@ QVector<AndroidDeviceInfo> AndroidConfig::connectedDevices(const QString &adbToo
|
||||
devices.push_back(dev);
|
||||
}
|
||||
|
||||
Utils::sort(devices, androidDevicesLessThan);
|
||||
Utils::sort(devices);
|
||||
if (devices.isEmpty() && error)
|
||||
*error = QApplication::translate("AndroidConfiguration",
|
||||
"No devices found in output of: %1")
|
||||
@@ -605,157 +512,6 @@ AndroidConfig::CreateAvdInfo AndroidConfig::gatherCreateAVDInfo(QWidget *parent,
|
||||
return result;
|
||||
}
|
||||
|
||||
QFuture<AndroidConfig::CreateAvdInfo> AndroidConfig::createAVD(CreateAvdInfo info) const
|
||||
{
|
||||
return Utils::runAsync(&AndroidConfig::createAVDImpl, info,
|
||||
androidToolPath(), androidToolEnvironment());
|
||||
}
|
||||
|
||||
AndroidConfig::CreateAvdInfo AndroidConfig::createAVDImpl(CreateAvdInfo info, FileName androidToolPath, Environment env)
|
||||
{
|
||||
QProcess proc;
|
||||
proc.setProcessEnvironment(env.toProcessEnvironment());
|
||||
QStringList arguments;
|
||||
arguments << QLatin1String("create") << QLatin1String("avd")
|
||||
<< QLatin1String("-t") << info.target
|
||||
<< QLatin1String("-n") << info.name
|
||||
<< QLatin1String("-b") << info.abi;
|
||||
if (info.sdcardSize > 0)
|
||||
arguments << QLatin1String("-c") << QString::fromLatin1("%1M").arg(info.sdcardSize);
|
||||
proc.start(androidToolPath.toString(), arguments);
|
||||
if (!proc.waitForStarted()) {
|
||||
info.error = QApplication::translate("AndroidConfig", "Could not start process \"%1 %2\"")
|
||||
.arg(androidToolPath.toString(), arguments.join(QLatin1Char(' ')));
|
||||
return info;
|
||||
}
|
||||
QTC_CHECK(proc.state() == QProcess::Running);
|
||||
proc.write(QByteArray("yes\n")); // yes to "Do you wish to create a custom hardware profile"
|
||||
|
||||
QByteArray question;
|
||||
while (true) {
|
||||
proc.waitForReadyRead(500);
|
||||
question += proc.readAllStandardOutput();
|
||||
if (question.endsWith(QByteArray("]:"))) {
|
||||
// truncate to last line
|
||||
int index = question.lastIndexOf(QByteArray("\n"));
|
||||
if (index != -1)
|
||||
question = question.mid(index);
|
||||
if (question.contains("hw.gpu.enabled"))
|
||||
proc.write(QByteArray("yes\n"));
|
||||
else
|
||||
proc.write(QByteArray("\n"));
|
||||
question.clear();
|
||||
}
|
||||
|
||||
if (proc.state() != QProcess::Running)
|
||||
break;
|
||||
}
|
||||
QTC_CHECK(proc.state() == QProcess::NotRunning);
|
||||
|
||||
QString errorOutput = QString::fromLocal8Bit(proc.readAllStandardError());
|
||||
// The exit code is always 0, so we need to check stderr
|
||||
// For now assume that any output at all indicates a error
|
||||
if (!errorOutput.isEmpty()) {
|
||||
info.error = errorOutput;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
bool AndroidConfig::removeAVD(const QString &name) const
|
||||
{
|
||||
SynchronousProcess proc;
|
||||
proc.setTimeoutS(5);
|
||||
proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
|
||||
SynchronousProcessResponse response
|
||||
= proc.runBlocking(androidToolPath().toString(), QStringList({"delete", "avd", "-n", name}));
|
||||
return response.result == SynchronousProcessResponse::Finished && response.exitCode == 0;
|
||||
}
|
||||
|
||||
QFuture<QVector<AndroidDeviceInfo>> AndroidConfig::androidVirtualDevicesFuture() const
|
||||
{
|
||||
return Utils::runAsync(&AndroidConfig::androidVirtualDevices,
|
||||
androidToolPath().toString(), androidToolEnvironment());
|
||||
}
|
||||
|
||||
QVector<AndroidDeviceInfo> AndroidConfig::androidVirtualDevices(const QString &androidTool, const Environment &environment)
|
||||
{
|
||||
QVector<AndroidDeviceInfo> devices;
|
||||
SynchronousProcess proc;
|
||||
proc.setTimeoutS(20);
|
||||
proc.setProcessEnvironment(environment.toProcessEnvironment());
|
||||
SynchronousProcessResponse response = proc.run(androidTool, {"list", "avd"}); // list available AVDs
|
||||
if (response.result != SynchronousProcessResponse::Finished)
|
||||
return devices;
|
||||
|
||||
QStringList avds = response.allOutput().split('\n');
|
||||
if (avds.empty())
|
||||
return devices;
|
||||
|
||||
while (avds.first().startsWith(QLatin1String("* daemon")))
|
||||
avds.removeFirst(); // remove the daemon logs
|
||||
avds.removeFirst(); // remove "List of devices attached" header line
|
||||
|
||||
bool nextLineIsTargetLine = false;
|
||||
|
||||
AndroidDeviceInfo dev;
|
||||
for (int i = 0; i < avds.size(); i++) {
|
||||
QString line = avds.at(i);
|
||||
if (!line.contains(QLatin1String("Name:")))
|
||||
continue;
|
||||
|
||||
int index = line.indexOf(QLatin1Char(':')) + 2;
|
||||
if (index >= line.size())
|
||||
break;
|
||||
dev.avdname = line.mid(index).trimmed();
|
||||
dev.sdk = -1;
|
||||
dev.cpuAbi.clear();
|
||||
++i;
|
||||
for (; i < avds.size(); ++i) {
|
||||
line = avds.at(i);
|
||||
if (line.contains(QLatin1String("---------")))
|
||||
break;
|
||||
|
||||
if (line.contains(QLatin1String("Target:")) || nextLineIsTargetLine) {
|
||||
if (line.contains(QLatin1String("Google APIs"))) {
|
||||
nextLineIsTargetLine = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
nextLineIsTargetLine = false;
|
||||
|
||||
int lastIndex = line.lastIndexOf(QLatin1Char(' '));
|
||||
if (lastIndex == -1) // skip line
|
||||
break;
|
||||
QString tmp = line.mid(lastIndex).remove(QLatin1Char(')')).trimmed();
|
||||
dev.sdk = apiLevelFromAndroidList(tmp);
|
||||
}
|
||||
if (line.contains(QLatin1String("Tag/ABI:"))) {
|
||||
int lastIndex = line.lastIndexOf(QLatin1Char('/')) + 1;
|
||||
if (lastIndex >= line.size())
|
||||
break;
|
||||
dev.cpuAbi = QStringList(line.mid(lastIndex));
|
||||
} else if (line.contains(QLatin1String("ABI:"))) {
|
||||
int lastIndex = line.lastIndexOf(QLatin1Char(' ')) + 1;
|
||||
if (lastIndex >= line.size())
|
||||
break;
|
||||
dev.cpuAbi = QStringList(line.mid(lastIndex).trimmed());
|
||||
}
|
||||
}
|
||||
// armeabi-v7a devices can also run armeabi code
|
||||
if (dev.cpuAbi == QStringList("armeabi-v7a"))
|
||||
dev.cpuAbi << QLatin1String("armeabi");
|
||||
dev.state = AndroidDeviceInfo::OkState;
|
||||
dev.type = AndroidDeviceInfo::Emulator;
|
||||
if (dev.cpuAbi.isEmpty() || dev.sdk == -1)
|
||||
continue;
|
||||
devices.push_back(dev);
|
||||
}
|
||||
Utils::sort(devices, androidDevicesLessThan);
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
QString AndroidConfig::startAVD(const QString &name) const
|
||||
{
|
||||
if (!findAvd(name).isEmpty() || startAVDAsync(name))
|
||||
@@ -1353,6 +1109,20 @@ QStringList AndroidDeviceInfo::adbSelector(const QString &serialNumber)
|
||||
return QStringList({"-s", serialNumber});
|
||||
}
|
||||
|
||||
bool AndroidDeviceInfo::operator<(const AndroidDeviceInfo &other) const
|
||||
{
|
||||
if (serialNumber.contains("????") != other.serialNumber.contains("????"))
|
||||
return !serialNumber.contains("????");
|
||||
if (type != other.type)
|
||||
return type == AndroidDeviceInfo::Hardware;
|
||||
if (sdk != other.sdk)
|
||||
return sdk < other.sdk;
|
||||
if (avdname != other.avdname)
|
||||
return avdname < other.avdname;
|
||||
|
||||
return serialNumber < other.serialNumber;
|
||||
}
|
||||
|
||||
const AndroidConfig &AndroidConfigurations::currentConfig()
|
||||
{
|
||||
return m_instance->m_config; // ensure that m_instance is initialized
|
||||
|
||||
Reference in New Issue
Block a user