androidconfigurations.cpp 48.6 KB
Newer Older
BogDan Vatra's avatar
BogDan Vatra committed
1 2
/**************************************************************************
**
3
** Copyright (c) 2014 BogDan Vatra <bog_dan_ro@yahoo.com>
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
BogDan Vatra's avatar
BogDan Vatra committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
BogDan Vatra's avatar
BogDan Vatra committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
Eike Ziller's avatar
Eike Ziller committed
13 14
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
BogDan Vatra's avatar
BogDan Vatra committed
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24 25 26
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
BogDan Vatra's avatar
BogDan Vatra committed
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
BogDan Vatra's avatar
BogDan Vatra committed
30 31 32

#include "androidconfigurations.h"
#include "androidconstants.h"
Daniel Teske's avatar
Daniel Teske committed
33 34 35
#include "androidtoolchain.h"
#include "androiddevice.h"
#include "androidgdbserverkitinformation.h"
36
#include "androidqtversion.h"
37
#include "androiddevicedialog.h"
38
#include "avddialog.h"
BogDan Vatra's avatar
BogDan Vatra committed
39 40

#include <coreplugin/icore.h>
41
#include <coreplugin/messagemanager.h>
42
#include <utils/hostosinfo.h>
BogDan Vatra's avatar
BogDan Vatra committed
43
#include <utils/persistentsettings.h>
Daniel Teske's avatar
Daniel Teske committed
44 45 46 47
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/devicesupport/devicemanager.h>
#include <projectexplorer/toolchainmanager.h>
48
#include <projectexplorer/session.h>
49
#include <debugger/debuggeritemmanager.h>
Daniel Teske's avatar
Daniel Teske committed
50 51 52 53
#include <debugger/debuggerkitinformation.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtversionmanager.h>
54
#include <utils/algorithm.h>
Daniel Teske's avatar
Daniel Teske committed
55
#include <utils/environment.h>
Orgad Shaneh's avatar
Orgad Shaneh committed
56
#include <utils/sleep.h>
BogDan Vatra's avatar
BogDan Vatra committed
57 58 59 60 61 62 63 64

#include <QDateTime>
#include <QSettings>
#include <QStringList>
#include <QProcess>
#include <QFileInfo>
#include <QDirIterator>
#include <QMetaObject>
65
#include <QApplication>
66
#include <QtConcurrentRun>
BogDan Vatra's avatar
BogDan Vatra committed
67 68 69 70

#include <QStringListModel>
#include <QMessageBox>

hjk's avatar
hjk committed
71 72
#include <functional>

hjk's avatar
hjk committed
73
using namespace ProjectExplorer;
BogDan Vatra's avatar
BogDan Vatra committed
74 75 76
using namespace Utils;

namespace Android {
77
using namespace Internal;
BogDan Vatra's avatar
BogDan Vatra committed
78 79 80 81 82 83 84 85 86

namespace {
    const QLatin1String SettingsGroup("AndroidConfigurations");
    const QLatin1String SDKLocationKey("SDKLocation");
    const QLatin1String NDKLocationKey("NDKLocation");
    const QLatin1String NDKToolchainVersionKey("NDKToolchainVersion");
    const QLatin1String AntLocationKey("AntLocation");
    const QLatin1String OpenJDKLocationKey("OpenJDKLocation");
    const QLatin1String KeystoreLocationKey("KeystoreLocation");
Daniel Teske's avatar
Daniel Teske committed
87
    const QLatin1String AutomaticKitCreationKey("AutomatiKitCreation");
BogDan Vatra's avatar
BogDan Vatra committed
88
    const QLatin1String UseGradleKey("UseGradle");
89
    const QLatin1String MakeExtraSearchDirectory("MakeExtraSearchDirectory");
90
    const QLatin1String DefaultDevice("DefaultDevice");
BogDan Vatra's avatar
BogDan Vatra committed
91
    const QLatin1String PartitionSizeKey("PartitionSize");
92
    const QLatin1String ToolchainHostKey("ToolchainHost");
BogDan Vatra's avatar
BogDan Vatra committed
93 94
    const QLatin1String ArmToolchainPrefix("arm-linux-androideabi");
    const QLatin1String X86ToolchainPrefix("x86");
BogDan Vatra's avatar
BogDan Vatra committed
95
    const QLatin1String MipsToolchainPrefix("mipsel-linux-android");
BogDan Vatra's avatar
BogDan Vatra committed
96
    const QLatin1String ArmToolsPrefix("arm-linux-androideabi");
BogDan Vatra's avatar
BogDan Vatra committed
97
    const QLatin1String X86ToolsPrefix("i686-linux-android");
BogDan Vatra's avatar
BogDan Vatra committed
98
    const QLatin1String MipsToolsPrefix("mipsel-linux-android");
BogDan Vatra's avatar
BogDan Vatra committed
99 100 101 102 103
    const QLatin1String Unknown("unknown");
    const QLatin1String keytoolName("keytool");
    const QLatin1String jarsignerName("jarsigner");
    const QLatin1String changeTimeStamp("ChangeTimeStamp");

104
    static QString sdkSettingsFileName()
BogDan Vatra's avatar
BogDan Vatra committed
105
    {
106 107
        return QFileInfo(Core::ICore::settings(QSettings::SystemScope)->fileName()).absolutePath()
                + QLatin1String("/qtcreator/android.xml");
BogDan Vatra's avatar
BogDan Vatra committed
108 109
    }

110
    bool androidDevicesLessThan(const AndroidDeviceInfo &dev1, const AndroidDeviceInfo &dev2)
BogDan Vatra's avatar
BogDan Vatra committed
111
    {
112
        if (dev1.serialNumber.contains(QLatin1String("????")) != dev2.serialNumber.contains(QLatin1String("????")))
Daniel Teske's avatar
Compile  
Daniel Teske committed
113
            return !dev1.serialNumber.contains(QLatin1String("????"));
114 115
        if (dev1.type != dev2.type)
            return dev1.type == AndroidDeviceInfo::Hardware;
116 117
        if (dev1.sdk != dev2.sdk)
            return dev1.sdk < dev2.sdk;
118

119
        return dev1.serialNumber < dev2.serialNumber;
BogDan Vatra's avatar
BogDan Vatra committed
120
    }
121 122 123 124 125 126 127 128 129 130 131 132 133

    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;
    }
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160

    static bool is32BitUserSpace()
    {
        // Do the exact same check as android's emulator is doing:
        if (Utils::HostOsInfo::isLinuxHost()) {
            if (QSysInfo::WordSize == 32 ) {
                Utils::Environment env = Utils::Environment::systemEnvironment();
                QString executable = env.searchInPath(QLatin1String("file")).toString();
                QString shell = env.value(QLatin1String("SHELL"));
                if (executable.isEmpty() || shell.isEmpty())
                    return true; // we can't detect, but creator is 32bit so assume 32bit

                QProcess proc;
                proc.setProcessChannelMode(QProcess::MergedChannels);
                proc.start(executable, QStringList() << shell);
                if (!proc.waitForFinished(2000)) {
                    proc.kill();
                    return true;
                }
                if (proc.readAll().contains("x86-64"))
                    return false;
                return true;
            }
        }
        return false;
    }

BogDan Vatra's avatar
BogDan Vatra committed
161 162
}

Daniel Teske's avatar
Daniel Teske committed
163 164 165 166 167
//////////////////////////////////
// AndroidConfig
//////////////////////////////////

Abi::Architecture AndroidConfig::architectureForToolChainPrefix(const QString& toolchainprefix)
168 169 170 171 172
{
    if (toolchainprefix == ArmToolchainPrefix)
        return Abi::ArmArchitecture;
    if (toolchainprefix == X86ToolchainPrefix)
        return Abi::X86Architecture;
BogDan Vatra's avatar
BogDan Vatra committed
173 174
    if (toolchainprefix == MipsToolchainPrefix)
        return Abi::MipsArchitecture;
175 176 177
    return Abi::UnknownArchitecture;
}

Daniel Teske's avatar
Daniel Teske committed
178
QLatin1String AndroidConfig::toolchainPrefix(Abi::Architecture architecture)
BogDan Vatra's avatar
BogDan Vatra committed
179 180
{
    switch (architecture) {
hjk's avatar
hjk committed
181
    case Abi::ArmArchitecture:
BogDan Vatra's avatar
BogDan Vatra committed
182
        return ArmToolchainPrefix;
hjk's avatar
hjk committed
183
    case Abi::X86Architecture:
BogDan Vatra's avatar
BogDan Vatra committed
184
        return X86ToolchainPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
185 186
    case Abi::MipsArchitecture:
        return MipsToolchainPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
187 188 189 190 191
    default:
        return Unknown;
    }
}

Daniel Teske's avatar
Daniel Teske committed
192
QLatin1String AndroidConfig::toolsPrefix(Abi::Architecture architecture)
BogDan Vatra's avatar
BogDan Vatra committed
193 194
{
    switch (architecture) {
hjk's avatar
hjk committed
195
    case Abi::ArmArchitecture:
BogDan Vatra's avatar
BogDan Vatra committed
196
        return ArmToolsPrefix;
hjk's avatar
hjk committed
197
    case Abi::X86Architecture:
BogDan Vatra's avatar
BogDan Vatra committed
198
        return X86ToolsPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
199 200
    case Abi::MipsArchitecture:
        return MipsToolsPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
201 202 203 204 205
    default:
        return Unknown;
    }
}

Daniel Teske's avatar
Daniel Teske committed
206
void AndroidConfig::load(const QSettings &settings)
BogDan Vatra's avatar
BogDan Vatra committed
207 208
{
    // user settings
Daniel Teske's avatar
Daniel Teske committed
209 210 211 212
    m_partitionSize = settings.value(PartitionSizeKey, 1024).toInt();
    m_sdkLocation = FileName::fromString(settings.value(SDKLocationKey).toString());
    m_ndkLocation = FileName::fromString(settings.value(NDKLocationKey).toString());
    m_antLocation = FileName::fromString(settings.value(AntLocationKey).toString());
213
    m_useGradle = settings.value(UseGradleKey, false).toBool();
Daniel Teske's avatar
Daniel Teske committed
214 215 216 217
    m_openJDKLocation = FileName::fromString(settings.value(OpenJDKLocationKey).toString());
    m_keystoreLocation = FileName::fromString(settings.value(KeystoreLocationKey).toString());
    m_toolchainHost = settings.value(ToolchainHostKey).toString();
    m_automaticKitCreation = settings.value(AutomaticKitCreationKey, true).toBool();
218
    QString extraDirectory = settings.value(MakeExtraSearchDirectory).toString();
Daniel Teske's avatar
Daniel Teske committed
219
    m_makeExtraSearchDirectories.clear();
220
    if (!extraDirectory.isEmpty())
Daniel Teske's avatar
Daniel Teske committed
221
        m_makeExtraSearchDirectories << extraDirectory;
BogDan Vatra's avatar
BogDan Vatra committed
222 223

    PersistentSettingsReader reader;
224 225
    if (reader.load(FileName::fromString(sdkSettingsFileName()))
            && settings.value(changeTimeStamp).toInt() != QFileInfo(sdkSettingsFileName()).lastModified().toMSecsSinceEpoch() / 1000) {
BogDan Vatra's avatar
BogDan Vatra committed
226
        // persisten settings
Daniel Teske's avatar
Daniel Teske committed
227 228 229 230 231 232
        m_sdkLocation = FileName::fromString(reader.restoreValue(SDKLocationKey).toString());
        m_ndkLocation = FileName::fromString(reader.restoreValue(NDKLocationKey).toString());
        m_antLocation = FileName::fromString(reader.restoreValue(AntLocationKey).toString());
        m_openJDKLocation = FileName::fromString(reader.restoreValue(OpenJDKLocationKey).toString());
        m_keystoreLocation = FileName::fromString(reader.restoreValue(KeystoreLocationKey).toString());
        m_toolchainHost = reader.restoreValue(ToolchainHostKey).toString();
Daniel Teske's avatar
Daniel Teske committed
233 234
        QVariant v = reader.restoreValue(AutomaticKitCreationKey);
        if (v.isValid())
Daniel Teske's avatar
Daniel Teske committed
235
            m_automaticKitCreation = v.toBool();
236
        QString extraDirectory = reader.restoreValue(MakeExtraSearchDirectory).toString();
Daniel Teske's avatar
Daniel Teske committed
237
        m_makeExtraSearchDirectories.clear();
238
        if (!extraDirectory.isEmpty())
Daniel Teske's avatar
Daniel Teske committed
239
            m_makeExtraSearchDirectories << extraDirectory;
BogDan Vatra's avatar
BogDan Vatra committed
240 241
        // persistent settings
    }
Daniel Teske's avatar
Daniel Teske committed
242 243
    m_availableSdkPlatformsUpToDate = false;
    m_NdkInformationUpToDate = false;
BogDan Vatra's avatar
BogDan Vatra committed
244 245 246
}

AndroidConfig::AndroidConfig()
247
    : m_useGradle(false),
BogDan Vatra's avatar
BogDan Vatra committed
248
      m_availableSdkPlatformsUpToDate(false),
Daniel Teske's avatar
Daniel Teske committed
249
      m_NdkInformationUpToDate(false)
BogDan Vatra's avatar
BogDan Vatra committed
250
{
Daniel Teske's avatar
Daniel Teske committed
251

BogDan Vatra's avatar
BogDan Vatra committed
252 253 254 255
}

void AndroidConfig::save(QSettings &settings) const
{
256
    QFileInfo fileInfo(sdkSettingsFileName());
BogDan Vatra's avatar
BogDan Vatra committed
257 258 259 260
    if (fileInfo.exists())
        settings.setValue(changeTimeStamp, fileInfo.lastModified().toMSecsSinceEpoch() / 1000);

    // user settings
Daniel Teske's avatar
Daniel Teske committed
261 262 263
    settings.setValue(SDKLocationKey, m_sdkLocation.toString());
    settings.setValue(NDKLocationKey, m_ndkLocation.toString());
    settings.setValue(AntLocationKey, m_antLocation.toString());
BogDan Vatra's avatar
BogDan Vatra committed
264
    settings.setValue(UseGradleKey, m_useGradle);
Daniel Teske's avatar
Daniel Teske committed
265 266 267 268 269
    settings.setValue(OpenJDKLocationKey, m_openJDKLocation.toString());
    settings.setValue(KeystoreLocationKey, m_keystoreLocation.toString());
    settings.setValue(PartitionSizeKey, m_partitionSize);
    settings.setValue(AutomaticKitCreationKey, m_automaticKitCreation);
    settings.setValue(ToolchainHostKey, m_toolchainHost);
270
    settings.setValue(MakeExtraSearchDirectory,
Daniel Teske's avatar
Daniel Teske committed
271 272
                      m_makeExtraSearchDirectories.isEmpty() ? QString()
                                                             : m_makeExtraSearchDirectories.at(0));
BogDan Vatra's avatar
BogDan Vatra committed
273 274
}

Daniel Teske's avatar
Daniel Teske committed
275
void AndroidConfig::updateNdkInformation() const
BogDan Vatra's avatar
BogDan Vatra committed
276
{
Daniel Teske's avatar
Daniel Teske committed
277 278
    if (m_NdkInformationUpToDate)
        return;
279
    m_availableNdkPlatforms.clear();
Daniel Teske's avatar
Daniel Teske committed
280
    FileName path = ndkLocation();
BogDan Vatra's avatar
BogDan Vatra committed
281
    QDirIterator it(path.appendPath(QLatin1String("platforms")).toString(), QStringList() << QLatin1String("android-*"), QDir::Dirs);
BogDan Vatra's avatar
BogDan Vatra committed
282 283
    while (it.hasNext()) {
        const QString &fileName = it.next();
284
        m_availableNdkPlatforms.push_back(fileName.mid(fileName.lastIndexOf(QLatin1Char('-')) + 1).toInt());
BogDan Vatra's avatar
BogDan Vatra committed
285
    }
286
    Utils::sort(m_availableNdkPlatforms, std::greater<int>());
Daniel Teske's avatar
Daniel Teske committed
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302

    // detect toolchain host
    QStringList hostPatterns;
    switch (HostOsInfo::hostOs()) {
    case OsTypeLinux:
        hostPatterns << QLatin1String("linux*");
        break;
    case OsTypeWindows:
        hostPatterns << QLatin1String("windows*");
        break;
    case OsTypeMac:
        hostPatterns << QLatin1String("darwin*");
        break;
    default: /* unknown host */ return;
    }

Daniel Teske's avatar
Daniel Teske committed
303
    path = ndkLocation();
Daniel Teske's avatar
Daniel Teske committed
304 305 306 307 308 309 310
    QDirIterator jt(path.appendPath(QLatin1String("prebuilt")).toString(), hostPatterns, QDir::Dirs);
    if (jt.hasNext()) {
        jt.next();
        m_toolchainHost = jt.fileName();
    }

    m_NdkInformationUpToDate = true;
BogDan Vatra's avatar
BogDan Vatra committed
311 312
}

313 314 315 316 317 318 319 320 321
bool AndroidConfig::sortSdkPlatformByApiLevel(const SdkPlatform &a, const SdkPlatform &b)
{
    if (a.apiLevel != b.apiLevel)
        return a.apiLevel > b.apiLevel;
    if (a.name != b.name)
        return a.name < b.name;
    return false;
}

Daniel Teske's avatar
Daniel Teske committed
322
void AndroidConfig::updateAvailableSdkPlatforms() const
BogDan Vatra's avatar
BogDan Vatra committed
323
{
Daniel Teske's avatar
Daniel Teske committed
324 325
    if (m_availableSdkPlatformsUpToDate)
        return;
326 327
    m_availableSdkPlatforms.clear();

BogDan Vatra's avatar
BogDan Vatra committed
328
    QProcess proc;
329
    proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
Tobias Hunger's avatar
Tobias Hunger committed
330
    proc.start(androidToolPath().toString(), QStringList() << QLatin1String("list") << QLatin1String("target")); // list avaialbe AVDs
331
    if (!proc.waitForFinished(5000)) {
BogDan Vatra's avatar
BogDan Vatra committed
332
        proc.terminate();
333
        return;
BogDan Vatra's avatar
BogDan Vatra committed
334
    }
335 336

    SdkPlatform platform;
337
    while (proc.canReadLine()) {
338
        const QString line = QString::fromLocal8Bit(proc.readLine().trimmed());
339 340 341 342 343 344 345 346
        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);
            platform.apiLevel = androidTarget.mid(androidTarget.lastIndexOf(QLatin1Char('-')) + 1).toInt();
        } else if (line.startsWith(QLatin1String("Name:"))) {
            platform.name = line.mid(6);
347 348
        } else if (line.startsWith(QLatin1String("Tag/ABIs :"))) {
            platform.abis = cleanAndroidABIs(line.mid(10).trimmed().split(QLatin1String(", ")));
349
        } else if (line.startsWith(QLatin1String("ABIs"))) {
350
            platform.abis = cleanAndroidABIs(line.mid(6).trimmed().split(QLatin1String(", ")));
351 352 353 354 355 356 357 358
        } else if (line.startsWith(QLatin1String("---")) || line.startsWith(QLatin1String("==="))) {
            if (platform.apiLevel == -1)
                continue;
            auto it = qLowerBound(m_availableSdkPlatforms.begin(), m_availableSdkPlatforms.end(),
                                  platform, sortSdkPlatformByApiLevel);
            m_availableSdkPlatforms.insert(it, platform);
            platform = SdkPlatform();
        }
BogDan Vatra's avatar
BogDan Vatra committed
359
    }
360 361 362 363 364 365 366

    if (platform.apiLevel != -1) {
        auto it = qLowerBound(m_availableSdkPlatforms.begin(), m_availableSdkPlatforms.end(),
                              platform, sortSdkPlatformByApiLevel);
        m_availableSdkPlatforms.insert(it, platform);
    }

Daniel Teske's avatar
Daniel Teske committed
367
    m_availableSdkPlatformsUpToDate = true;
368 369
}

370 371
QStringList AndroidConfig::apiLevelNamesFor(const QList<SdkPlatform> &platforms)
{
372
    return Utils::transform(platforms, AndroidConfig::apiLevelNameFor);
373 374
}

375 376 377 378 379
QString AndroidConfig::apiLevelNameFor(const SdkPlatform &platform)
{
    return QLatin1String("android-") + QString::number(platform.apiLevel);
}

380
QList<SdkPlatform> AndroidConfig::sdkTargets(int minApiLevel) const
381
{
Daniel Teske's avatar
Daniel Teske committed
382
    updateAvailableSdkPlatforms();
383
    QList<SdkPlatform> result;
384
    for (int i = 0; i < m_availableSdkPlatforms.size(); ++i) {
385 386
        if (m_availableSdkPlatforms.at(i).apiLevel >= minApiLevel)
            result << m_availableSdkPlatforms.at(i);
387 388 389 390
        else
            break;
    }
    return result;
BogDan Vatra's avatar
BogDan Vatra committed
391 392
}

Daniel Teske's avatar
Daniel Teske committed
393
FileName AndroidConfig::adbToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
394
{
Daniel Teske's avatar
Daniel Teske committed
395
    Utils::FileName path = m_sdkLocation;
396
    return path.appendPath(QLatin1String("platform-tools/adb" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
397 398
}

Daniel Teske's avatar
Daniel Teske committed
399
Utils::Environment AndroidConfig::androidToolEnvironment() const
400 401
{
    Utils::Environment env = Utils::Environment::systemEnvironment();
Daniel Teske's avatar
Daniel Teske committed
402 403
    if (!m_openJDKLocation.isEmpty())
        env.set(QLatin1String("JAVA_HOME"), m_openJDKLocation.toUserOutput());
404 405 406
    return env;
}

Daniel Teske's avatar
Daniel Teske committed
407
FileName AndroidConfig::androidToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
408
{
409 410 411
    if (HostOsInfo::isWindowsHost()) {
        // I want to switch from using android.bat to using an executable. All it really does is call
        // Java and I've made some progress on it. So if android.exe exists, return that instead.
Daniel Teske's avatar
Daniel Teske committed
412
        FileName path = m_sdkLocation;
413
        path.appendPath(QLatin1String("tools/android" QTC_HOST_EXE_SUFFIX));
hjk's avatar
hjk committed
414
        if (path.exists())
415
            return path;
Daniel Teske's avatar
Daniel Teske committed
416
        path = m_sdkLocation;
417
        return path.appendPath(QLatin1String("tools/android" ANDROID_BAT_SUFFIX));
418
    } else {
Daniel Teske's avatar
Daniel Teske committed
419
        FileName path = m_sdkLocation;
420 421
        return path.appendPath(QLatin1String("tools/android"));
    }
BogDan Vatra's avatar
BogDan Vatra committed
422 423
}

Daniel Teske's avatar
Daniel Teske committed
424
FileName AndroidConfig::antToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
425
{
Daniel Teske's avatar
Daniel Teske committed
426 427
    if (!m_antLocation.isEmpty())
        return m_antLocation;
BogDan Vatra's avatar
BogDan Vatra committed
428
    else
429
        return FileName::fromLatin1("ant");
BogDan Vatra's avatar
BogDan Vatra committed
430 431
}

Daniel Teske's avatar
Daniel Teske committed
432
FileName AndroidConfig::emulatorToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
433
{
Daniel Teske's avatar
Daniel Teske committed
434
    FileName path = m_sdkLocation;
435
    return path.appendPath(QLatin1String("tools/emulator" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
436 437
}

Daniel Teske's avatar
Daniel Teske committed
438
FileName AndroidConfig::toolPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
439
{
Daniel Teske's avatar
Daniel Teske committed
440
    FileName path = m_ndkLocation;
Tobias Hunger's avatar
Tobias Hunger committed
441
    return path.appendPath(QString::fromLatin1("toolchains/%1-%2/prebuilt/%3/bin/%4")
BogDan Vatra's avatar
BogDan Vatra committed
442
            .arg(toolchainPrefix(architecture))
443
            .arg(ndkToolChainVersion)
Daniel Teske's avatar
Daniel Teske committed
444
            .arg(toolchainHost())
Tobias Hunger's avatar
Tobias Hunger committed
445
            .arg(toolsPrefix(architecture)));
BogDan Vatra's avatar
BogDan Vatra committed
446 447
}

Daniel Teske's avatar
Daniel Teske committed
448
FileName AndroidConfig::gccPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
449
{
450
    return toolPath(architecture, ndkToolChainVersion).appendString(QLatin1String("-gcc" QTC_HOST_EXE_SUFFIX));
451 452
}

Daniel Teske's avatar
Daniel Teske committed
453
FileName AndroidConfig::gdbPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
454
{
455
    return toolPath(architecture, ndkToolChainVersion).appendString(QLatin1String("-gdb" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
456 457
}

Daniel Teske's avatar
Daniel Teske committed
458
FileName AndroidConfig::openJDKBinPath() const
BogDan Vatra's avatar
BogDan Vatra committed
459
{
Daniel Teske's avatar
Daniel Teske committed
460
    FileName path = m_openJDKLocation;
Tobias Hunger's avatar
Tobias Hunger committed
461 462 463
    if (!path.isEmpty())
        return path.appendPath(QLatin1String("bin"));
    return path;
BogDan Vatra's avatar
BogDan Vatra committed
464 465
}

Daniel Teske's avatar
Daniel Teske committed
466
FileName AndroidConfig::keytoolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
467
{
Tobias Hunger's avatar
Tobias Hunger committed
468
    return openJDKBinPath().appendPath(keytoolName);
BogDan Vatra's avatar
BogDan Vatra committed
469 470
}

Daniel Teske's avatar
Daniel Teske committed
471
QVector<AndroidDeviceInfo> AndroidConfig::connectedDevices(QString *error) const
BogDan Vatra's avatar
BogDan Vatra committed
472
{
473
    QVector<AndroidDeviceInfo> devices;
BogDan Vatra's avatar
BogDan Vatra committed
474
    QProcess adbProc;
Tobias Hunger's avatar
Tobias Hunger committed
475
    adbProc.start(adbToolPath().toString(), QStringList() << QLatin1String("devices"));
476
    if (!adbProc.waitForFinished(10000)) {
477
        adbProc.kill();
478
        if (error)
Daniel Teske's avatar
Daniel Teske committed
479 480 481
            *error = QApplication::translate("AndroidConfiguration",
                                             "Could not run: %1")
                .arg(adbToolPath().toString() + QLatin1String(" devices"));
BogDan Vatra's avatar
BogDan Vatra committed
482 483 484
        return devices;
    }
    QList<QByteArray> adbDevs = adbProc.readAll().trimmed().split('\n');
BogDan Vatra's avatar
BogDan Vatra committed
485 486 487 488 489 490
    if (adbDevs.empty())
        return devices;

    while (adbDevs.first().startsWith("* daemon"))
        adbDevs.removeFirst(); // remove the daemon logs
    adbDevs.removeFirst(); // remove "List of devices attached" header line
491 492 493

    // workaround for '????????????' serial numbers:
    // can use "adb -d" when only one usb device attached
BogDan Vatra's avatar
BogDan Vatra committed
494
    foreach (const QByteArray &device, adbDevs) {
495 496
        const QString serialNo = QString::fromLatin1(device.left(device.indexOf('\t')).trimmed());
        const QString deviceType = QString::fromLatin1(device.mid(device.indexOf('\t'))).trimmed();
497 498
        if (isBootToQt(serialNo))
            continue;
499
        AndroidDeviceInfo dev;
500
        dev.serialNumber = serialNo;
501
        dev.type = serialNo.startsWith(QLatin1String("emulator")) ? AndroidDeviceInfo::Emulator : AndroidDeviceInfo::Hardware;
BogDan Vatra's avatar
BogDan Vatra committed
502
        dev.sdk = getSDKVersion(dev.serialNumber);
503
        dev.cpuAbi = getAbis(dev.serialNumber);
Daniel Teske's avatar
Daniel Teske committed
504 505 506 507 508 509
        if (deviceType == QLatin1String("unauthorized"))
            dev.state = AndroidDeviceInfo::UnAuthorizedState;
        else if (deviceType == QLatin1String("offline"))
            dev.state = AndroidDeviceInfo::OfflineState;
        else
            dev.state = AndroidDeviceInfo::OkState;
BogDan Vatra's avatar
BogDan Vatra committed
510 511
        devices.push_back(dev);
    }
512

513
    Utils::sort(devices, androidDevicesLessThan);
514
    if (devices.isEmpty() && error)
Daniel Teske's avatar
Daniel Teske committed
515 516 517
        *error = QApplication::translate("AndroidConfiguration",
                                         "No devices found in output of: %1")
            .arg(adbToolPath().toString() + QLatin1String(" devices"));
BogDan Vatra's avatar
BogDan Vatra committed
518 519 520
    return devices;
}

521
AndroidConfig::CreateAvdInfo AndroidConfig::gatherCreateAVDInfo(QWidget *parent, int minApiLevel, QString targetArch) const
BogDan Vatra's avatar
BogDan Vatra committed
522
{
523
    CreateAvdInfo result;
524 525
    AvdDialog d(minApiLevel, targetArch, this, parent);
    if (d.exec() != QDialog::Accepted || !d.isValid())
526
        return result;
527

528 529 530 531 532 533
    result.target = d.target();
    result.name = d.name();
    result.abi = d.abi();
    result.sdcardSize = d.sdcardSize();
    return result;
}
534

535 536 537
QFuture<AndroidConfig::CreateAvdInfo> AndroidConfig::createAVD(CreateAvdInfo info) const
{
    return QtConcurrent::run(&AndroidConfig::createAVDImpl, info, androidToolPath(), androidToolEnvironment());
BogDan Vatra's avatar
BogDan Vatra committed
538 539
}

540
AndroidConfig::CreateAvdInfo AndroidConfig::createAVDImpl(CreateAvdInfo info, Utils::FileName androidToolPath, Utils::Environment env)
BogDan Vatra's avatar
BogDan Vatra committed
541 542
{
    QProcess proc;
543 544 545 546 547 548 549 550 551 552
    proc.setProcessEnvironment(env.toProcessEnvironment());
    QStringList arguments;
    arguments << QLatin1String("create") << QLatin1String("avd")
              << QLatin1String("-t") << info.target
              << QLatin1String("-n") << info.name
              << QLatin1String("-b") << info.abi
              << 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\"")
hjk's avatar
hjk committed
553
                .arg(androidToolPath.toString(), arguments.join(QLatin1Char(' ')));
554 555
        return info;
    }
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576

    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;
BogDan Vatra's avatar
BogDan Vatra committed
577
    }
578 579 580

    proc.waitForFinished();

581 582 583 584
    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()) {
585
        info.error = errorOutput;
586 587
    }

588
    return info;
BogDan Vatra's avatar
BogDan Vatra committed
589 590
}

Daniel Teske's avatar
Daniel Teske committed
591
bool AndroidConfig::removeAVD(const QString &name) const
BogDan Vatra's avatar
BogDan Vatra committed
592 593
{
    QProcess proc;
594
    proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
Tobias Hunger's avatar
Tobias Hunger committed
595
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
596 597
               QStringList() << QLatin1String("delete") << QLatin1String("avd")
               << QLatin1String("-n") << name);
598
    if (!proc.waitForFinished(5000)) {
BogDan Vatra's avatar
BogDan Vatra committed
599 600 601 602 603 604
        proc.terminate();
        return false;
    }
    return !proc.exitCode();
}

Daniel Teske's avatar
Daniel Teske committed
605
QVector<AndroidDeviceInfo> AndroidConfig::androidVirtualDevices() const
BogDan Vatra's avatar
BogDan Vatra committed
606
{
607
    QVector<AndroidDeviceInfo> devices;
BogDan Vatra's avatar
BogDan Vatra committed
608
    QProcess proc;
609
    proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
Tobias Hunger's avatar
Tobias Hunger committed
610
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
611
               QStringList() << QLatin1String("list") << QLatin1String("avd")); // list available AVDs
612
    if (!proc.waitForFinished(5000)) {
BogDan Vatra's avatar
BogDan Vatra committed
613 614 615 616
        proc.terminate();
        return devices;
    }
    QList<QByteArray> avds = proc.readAll().trimmed().split('\n');
BogDan Vatra's avatar
BogDan Vatra committed
617 618 619 620 621 622 623
    if (avds.empty())
        return devices;

    while (avds.first().startsWith("* daemon"))
        avds.removeFirst(); // remove the daemon logs
    avds.removeFirst(); // remove "List of devices attached" header line

624
    AndroidDeviceInfo dev;
BogDan Vatra's avatar
BogDan Vatra committed
625
    for (int i = 0; i < avds.size(); i++) {
626
        QString line = QLatin1String(avds.at(i));
BogDan Vatra's avatar
BogDan Vatra committed
627 628 629
        if (!line.contains(QLatin1String("Name:")))
            continue;

630 631 632 633 634 635
        int index = line.indexOf(QLatin1Char(':')) + 2;
        if (index >= line.size())
            break;
        dev.serialNumber = line.mid(index).trimmed();
        dev.sdk = -1;
        dev.cpuAbi.clear();
BogDan Vatra's avatar
BogDan Vatra committed
636 637 638 639 640
        ++i;
        for (; i < avds.size(); ++i) {
            line = QLatin1String(avds[i]);
            if (line.contains(QLatin1String("---------")))
                break;
641
            if (line.contains(QLatin1String("Target:"))) {
642 643 644 645
                int lastIndex = line.lastIndexOf(QLatin1Char(' '));
                if (lastIndex == -1) // skip line
                    break;
                QString tmp = line.mid(lastIndex).remove(QLatin1Char(')')).trimmed();
646 647 648 649 650
                if (tmp == QLatin1String("L")) // HACK for android-L preview
                    dev.sdk = 20;
                else
                    dev.sdk = tmp.toInt();
            }
651 652 653 654 655 656 657 658 659 660 661
            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();
            }
BogDan Vatra's avatar
BogDan Vatra committed
662
        }
663 664 665
        // armeabi-v7a devices can also run armeabi code
        if (dev.cpuAbi == QStringList(QLatin1String("armeabi-v7a")))
            dev.cpuAbi << QLatin1String("armeabi");
Daniel Teske's avatar
Daniel Teske committed
666
        dev.state = AndroidDeviceInfo::OkState;
667
        dev.type = AndroidDeviceInfo::Emulator;
668 669
        if (dev.cpuAbi.isEmpty() || dev.sdk == -1)
            continue;
BogDan Vatra's avatar
BogDan Vatra committed
670 671
        devices.push_back(dev);
    }
672
    Utils::sort(devices, androidDevicesLessThan);
BogDan Vatra's avatar
BogDan Vatra committed
673 674 675 676

    return devices;
}

Daniel Teske's avatar
Daniel Teske committed
677
QString AndroidConfig::startAVD(const QString &name, int apiLevel, QString cpuAbi) const
678
{
679
    if (!findAvd(apiLevel, cpuAbi).isEmpty() || startAVDAsync(name))
680 681 682
        return waitForAvd(apiLevel, cpuAbi);
    return QString();
}
BogDan Vatra's avatar
BogDan Vatra committed
683

Daniel Teske's avatar
Daniel Teske committed
684
bool AndroidConfig::startAVDAsync(const QString &avdName) const
685 686
{
    QProcess *avdProcess = new QProcess();
Daniel Teske's avatar
Daniel Teske committed
687
    avdProcess->connect(avdProcess, SIGNAL(finished(int)), avdProcess, SLOT(deleteLater()));
BogDan Vatra's avatar
BogDan Vatra committed
688 689

    // start the emulator
690 691 692 693 694 695 696
    QStringList arguments;
    if (AndroidConfigurations::force32bitEmulator())
        arguments << QLatin1String("-force-32bit");

    arguments << QLatin1String("-partition-size") << QString::number(partitionSize())
              << QLatin1String("-avd") << avdName;
    avdProcess->start(emulatorToolPath().toString(), arguments);
Orgad Shaneh's avatar
Orgad Shaneh committed
697 698
    if (!avdProcess->waitForStarted(-1)) {
        delete avdProcess;
699
        return false;
BogDan Vatra's avatar
BogDan Vatra committed
700
    }
701 702 703
    return true;
}

Daniel Teske's avatar
Daniel Teske committed
704
QString AndroidConfig::findAvd(int apiLevel, const QString &cpuAbi) const
705 706 707 708 709 710 711 712 713
{
    QVector<AndroidDeviceInfo> devices = connectedDevices();
    foreach (AndroidDeviceInfo device, devices) {
        if (!device.serialNumber.startsWith(QLatin1String("emulator")))
            continue;
        if (!device.cpuAbi.contains(cpuAbi))
            continue;
        if (device.sdk != apiLevel)
            continue;
714
        return device.serialNumber;
715
    }
716
    return QString();
717 718
}

Daniel Teske's avatar
Daniel Teske committed
719
bool AndroidConfig::isConnected(const QString &serialNumber) const
720
{
Daniel Teske's avatar
Daniel Teske committed
721 722 723 724 725 726 727
    QVector<AndroidDeviceInfo> devices = connectedDevices();
    foreach (AndroidDeviceInfo device, devices) {
        if (device.serialNumber == serialNumber)
            return true;
    }
    return false;
}
728

Daniel Teske's avatar
Daniel Teske committed
729
bool AndroidConfig::waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const
Daniel Teske's avatar
Daniel Teske committed
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
{
    // found a serial number, now wait until it's done booting...
    for (int i = 0; i < 60; ++i) {
        if (fi.isCanceled())
            return false;
        if (hasFinishedBooting(serialNumber)) {
            return true;
        } else {
            Utils::sleep(2000);
            if (!isConnected(serialNumber)) // device was disconnected
                return false;
        }
    }
    return false;
}

Daniel Teske's avatar
Daniel Teske committed
746
QString AndroidConfig::waitForAvd(int apiLevel, const QString &cpuAbi, const QFutureInterface<bool> &fi) const
Daniel Teske's avatar
Daniel Teske committed
747 748 749
{
    // we cannot use adb -e wait-for-device, since that doesn't work if a emulator is already running
    // 60 rounds of 2s sleeping, two minutes for the avd to start
750
    QString serialNumber;
Daniel Teske's avatar
Daniel Teske committed
751 752
    for (int i = 0; i < 60; ++i) {
        if (fi.isCanceled())
Orgad Shaneh's avatar
Orgad Shaneh committed
753
            return QString();
Daniel Teske's avatar
Daniel Teske committed
754 755 756 757
        serialNumber = findAvd(apiLevel, cpuAbi);
        if (!serialNumber.isEmpty())
            return waitForBooted(serialNumber, fi) ?  serialNumber : QString();
        Utils::sleep(2000);
BogDan Vatra's avatar
BogDan Vatra committed
758 759 760 761
    }
    return QString();
}

Daniel Teske's avatar
Daniel Teske committed
762
bool AndroidConfig::isBootToQt(const QString &device) const
763 764 765 766 767 768 769 770
{
    // workaround for '????????????' serial numbers
    QStringList arguments = AndroidDeviceInfo::adbSelector(device);
    arguments << QLatin1String("shell")
              << QLatin1String("ls -l /system/bin/appcontroller || ls -l /usr/bin/appcontroller && echo Boot2Qt");

    QProcess adbProc;
    adbProc.start(adbToolPath().toString(), arguments);
771
    if (!adbProc.waitForFinished(10000)) {
772
        adbProc.kill();
773
        return false;
774 775 776 777
    }
    return adbProc.readAll().contains("Boot2Qt");
}

Daniel Teske's avatar
Daniel Teske committed
778
int AndroidConfig::getSDKVersion(const QString &device) const
BogDan Vatra's avatar
BogDan Vatra committed
779
{
780 781 782 783
    // workaround for '????????????' serial numbers
    QStringList arguments = AndroidDeviceInfo::adbSelector(device);
    arguments << QLatin1String("shell") << QLatin1String("getprop")
              << QLatin1String("ro.build.version.sdk");
BogDan Vatra's avatar
BogDan Vatra committed
784 785

    QProcess adbProc;
786
    adbProc.start(adbToolPath().toString(), arguments);
787
    if (!adbProc.waitForFinished(10000)) {
788
        adbProc.kill();
BogDan Vatra's avatar
BogDan Vatra committed
789 790 791 792 793
        return -1;
    }
    return adbProc.readAll().trimmed().toInt();
}

794 795 796 797 798
//!
//! \brief AndroidConfigurations::getProductModel
//! \param device serial number
//! \return the produce model of the device or if that cannot be read the serial number
//!
Daniel Teske's avatar
Daniel Teske committed
799
QString AndroidConfig::getProductModel(const QString &device) const
800
{
801 802
    if (m_serialNumberToDeviceName.contains(device))
        return m_serialNumberToDeviceName.value(device);
803 804 805 806 807 808 809
    // workaround for '????????????' serial numbers
    QStringList arguments = AndroidDeviceInfo::adbSelector(device);
    arguments << QLatin1String("shell") << QLatin1String("getprop")
              << QLatin1String("ro.product.model");

    QProcess adbProc;
    adbProc.start(adbToolPath().toString(), arguments);
810
    if (!adbProc.waitForFinished(10000)) {
811 812 813 814 815 816
        adbProc.kill();
        return device;
    }
    QString model = QString::fromLocal8Bit(adbProc.readAll().trimmed());
    if (model.isEmpty())
        return device;
817 818
    if (!device.startsWith(QLatin1String("????")))
        m_serialNumberToDeviceName.insert(device, model);
819 820 821
    return model;
}

Daniel Teske's avatar
Daniel Teske committed
822
bool AndroidConfig::hasFinishedBooting(const QString &device) const
823 824 825 826 827 828 829
{
    QStringList arguments = AndroidDeviceInfo::adbSelector(device);
    arguments << QLatin1String("shell") << QLatin1String("getprop")
              << QLatin1String("init.svc.bootanim");

    QProcess adbProc;
    adbProc.start(adbToolPath().toString(), arguments);
Daniel Teske's avatar