androidconfigurations.cpp 43.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 13 14
** 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
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
BogDan Vatra's avatar
BogDan Vatra committed
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** 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
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
BogDan Vatra's avatar
BogDan Vatra committed
29 30 31

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

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

#include <QDateTime>
#include <QSettings>
#include <QStringList>
#include <QProcess>
#include <QFileInfo>
#include <QDirIterator>
#include <QMetaObject>

#include <QStringListModel>
#include <QMessageBox>

hjk's avatar
hjk committed
67
using namespace ProjectExplorer;
BogDan Vatra's avatar
BogDan Vatra committed
68 69 70 71 72 73 74 75 76 77 78 79 80
using namespace Utils;

namespace Android {
namespace Internal {

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
81
    const QLatin1String AutomaticKitCreationKey("AutomatiKitCreation");
82
    const QLatin1String MakeExtraSearchDirectory("MakeExtraSearchDirectory");
83
    const QLatin1String DefaultDevice("DefaultDevice");
BogDan Vatra's avatar
BogDan Vatra committed
84
    const QLatin1String PartitionSizeKey("PartitionSize");
85
    const QLatin1String ToolchainHostKey("ToolchainHost");
BogDan Vatra's avatar
BogDan Vatra committed
86 87
    const QLatin1String ArmToolchainPrefix("arm-linux-androideabi");
    const QLatin1String X86ToolchainPrefix("x86");
BogDan Vatra's avatar
BogDan Vatra committed
88
    const QLatin1String MipsToolchainPrefix("mipsel-linux-android");
BogDan Vatra's avatar
BogDan Vatra committed
89
    const QLatin1String ArmToolsPrefix("arm-linux-androideabi");
BogDan Vatra's avatar
BogDan Vatra committed
90
    const QLatin1String X86ToolsPrefix("i686-linux-android");
BogDan Vatra's avatar
BogDan Vatra committed
91
    const QLatin1String MipsToolsPrefix("mipsel-linux-android");
BogDan Vatra's avatar
BogDan Vatra committed
92 93 94 95 96
    const QLatin1String Unknown("unknown");
    const QLatin1String keytoolName("keytool");
    const QLatin1String jarsignerName("jarsigner");
    const QLatin1String changeTimeStamp("ChangeTimeStamp");

97
    static QString sdkSettingsFileName()
BogDan Vatra's avatar
BogDan Vatra committed
98
    {
99 100
        return QFileInfo(Core::ICore::settings(QSettings::SystemScope)->fileName()).absolutePath()
                + QLatin1String("/qtcreator/android.xml");
BogDan Vatra's avatar
BogDan Vatra committed
101 102
    }

103
    bool androidDevicesLessThan(const AndroidDeviceInfo &dev1, const AndroidDeviceInfo &dev2)
BogDan Vatra's avatar
BogDan Vatra committed
104
    {
Daniel Teske's avatar
Compile  
Daniel Teske committed
105 106
        if (dev1.serialNumber.contains(QLatin1String("????")) == dev2.serialNumber.contains(QLatin1String("????")))
            return !dev1.serialNumber.contains(QLatin1String("????"));
107 108
        if (dev1.type != dev2.type)
            return dev1.type == AndroidDeviceInfo::Hardware;
109 110
        if (dev1.sdk != dev2.sdk)
            return dev1.sdk < dev2.sdk;
111

112
        return dev1.serialNumber < dev2.serialNumber;
BogDan Vatra's avatar
BogDan Vatra committed
113 114 115
    }
}

Daniel Teske's avatar
Daniel Teske committed
116 117 118 119 120
//////////////////////////////////
// AndroidConfig
//////////////////////////////////

Abi::Architecture AndroidConfig::architectureForToolChainPrefix(const QString& toolchainprefix)
121 122 123 124 125
{
    if (toolchainprefix == ArmToolchainPrefix)
        return Abi::ArmArchitecture;
    if (toolchainprefix == X86ToolchainPrefix)
        return Abi::X86Architecture;
BogDan Vatra's avatar
BogDan Vatra committed
126 127
    if (toolchainprefix == MipsToolchainPrefix)
        return Abi::MipsArchitecture;
128 129 130
    return Abi::UnknownArchitecture;
}

Daniel Teske's avatar
Daniel Teske committed
131
QLatin1String AndroidConfig::toolchainPrefix(Abi::Architecture architecture)
BogDan Vatra's avatar
BogDan Vatra committed
132 133
{
    switch (architecture) {
hjk's avatar
hjk committed
134
    case Abi::ArmArchitecture:
BogDan Vatra's avatar
BogDan Vatra committed
135
        return ArmToolchainPrefix;
hjk's avatar
hjk committed
136
    case Abi::X86Architecture:
BogDan Vatra's avatar
BogDan Vatra committed
137
        return X86ToolchainPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
138 139
    case Abi::MipsArchitecture:
        return MipsToolchainPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
140 141 142 143 144
    default:
        return Unknown;
    }
}

Daniel Teske's avatar
Daniel Teske committed
145
QLatin1String AndroidConfig::toolsPrefix(Abi::Architecture architecture)
BogDan Vatra's avatar
BogDan Vatra committed
146 147
{
    switch (architecture) {
hjk's avatar
hjk committed
148
    case Abi::ArmArchitecture:
BogDan Vatra's avatar
BogDan Vatra committed
149
        return ArmToolsPrefix;
hjk's avatar
hjk committed
150
    case Abi::X86Architecture:
BogDan Vatra's avatar
BogDan Vatra committed
151
        return X86ToolsPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
152 153
    case Abi::MipsArchitecture:
        return MipsToolsPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
154 155 156 157 158
    default:
        return Unknown;
    }
}

Daniel Teske's avatar
Daniel Teske committed
159
void AndroidConfig::load(const QSettings &settings)
BogDan Vatra's avatar
BogDan Vatra committed
160 161
{
    // user settings
Daniel Teske's avatar
Daniel Teske committed
162 163 164 165 166 167 168 169
    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());
    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();
170
    QString extraDirectory = settings.value(MakeExtraSearchDirectory).toString();
Daniel Teske's avatar
Daniel Teske committed
171
    m_makeExtraSearchDirectories.clear();
172
    if (!extraDirectory.isEmpty())
Daniel Teske's avatar
Daniel Teske committed
173
        m_makeExtraSearchDirectories << extraDirectory;
BogDan Vatra's avatar
BogDan Vatra committed
174 175

    PersistentSettingsReader reader;
176 177
    if (reader.load(FileName::fromString(sdkSettingsFileName()))
            && settings.value(changeTimeStamp).toInt() != QFileInfo(sdkSettingsFileName()).lastModified().toMSecsSinceEpoch() / 1000) {
BogDan Vatra's avatar
BogDan Vatra committed
178
        // persisten settings
Daniel Teske's avatar
Daniel Teske committed
179 180 181 182 183 184
        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
185 186
        QVariant v = reader.restoreValue(AutomaticKitCreationKey);
        if (v.isValid())
Daniel Teske's avatar
Daniel Teske committed
187
            m_automaticKitCreation = v.toBool();
188
        QString extraDirectory = reader.restoreValue(MakeExtraSearchDirectory).toString();
Daniel Teske's avatar
Daniel Teske committed
189
        m_makeExtraSearchDirectories.clear();
190
        if (!extraDirectory.isEmpty())
Daniel Teske's avatar
Daniel Teske committed
191
            m_makeExtraSearchDirectories << extraDirectory;
BogDan Vatra's avatar
BogDan Vatra committed
192 193
        // persistent settings
    }
Daniel Teske's avatar
Daniel Teske committed
194 195
    m_availableSdkPlatformsUpToDate = false;
    m_NdkInformationUpToDate = false;
BogDan Vatra's avatar
BogDan Vatra committed
196 197 198
}

AndroidConfig::AndroidConfig()
Daniel Teske's avatar
Daniel Teske committed
199 200
    : m_availableSdkPlatformsUpToDate(false),
      m_NdkInformationUpToDate(false)
BogDan Vatra's avatar
BogDan Vatra committed
201
{
Daniel Teske's avatar
Daniel Teske committed
202

BogDan Vatra's avatar
BogDan Vatra committed
203 204 205 206
}

void AndroidConfig::save(QSettings &settings) const
{
207
    QFileInfo fileInfo(sdkSettingsFileName());
BogDan Vatra's avatar
BogDan Vatra committed
208 209 210 211
    if (fileInfo.exists())
        settings.setValue(changeTimeStamp, fileInfo.lastModified().toMSecsSinceEpoch() / 1000);

    // user settings
Daniel Teske's avatar
Daniel Teske committed
212 213 214 215 216 217 218 219
    settings.setValue(SDKLocationKey, m_sdkLocation.toString());
    settings.setValue(NDKLocationKey, m_ndkLocation.toString());
    settings.setValue(AntLocationKey, m_antLocation.toString());
    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);
220
    settings.setValue(MakeExtraSearchDirectory,
Daniel Teske's avatar
Daniel Teske committed
221 222
                      m_makeExtraSearchDirectories.isEmpty() ? QString()
                                                             : m_makeExtraSearchDirectories.at(0));
BogDan Vatra's avatar
BogDan Vatra committed
223 224
}

Daniel Teske's avatar
Daniel Teske committed
225
void AndroidConfig::updateNdkInformation() const
BogDan Vatra's avatar
BogDan Vatra committed
226
{
Daniel Teske's avatar
Daniel Teske committed
227 228
    if (m_NdkInformationUpToDate)
        return;
229
    m_availableNdkPlatforms.clear();
Daniel Teske's avatar
Daniel Teske committed
230
    FileName path = ndkLocation();
BogDan Vatra's avatar
BogDan Vatra committed
231
    QDirIterator it(path.appendPath(QLatin1String("platforms")).toString(), QStringList() << QLatin1String("android-*"), QDir::Dirs);
BogDan Vatra's avatar
BogDan Vatra committed
232 233
    while (it.hasNext()) {
        const QString &fileName = it.next();
234
        m_availableNdkPlatforms.push_back(fileName.mid(fileName.lastIndexOf(QLatin1Char('-')) + 1).toInt());
BogDan Vatra's avatar
BogDan Vatra committed
235
    }
236
    qSort(m_availableNdkPlatforms.begin(), m_availableNdkPlatforms.end(), qGreater<int>());
Daniel Teske's avatar
Daniel Teske committed
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252

    // 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
253
    path = ndkLocation();
Daniel Teske's avatar
Daniel Teske committed
254 255 256 257 258 259 260
    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
261 262
}

Daniel Teske's avatar
Daniel Teske committed
263
void AndroidConfig::updateAvailableSdkPlatforms() const
BogDan Vatra's avatar
BogDan Vatra committed
264
{
Daniel Teske's avatar
Daniel Teske committed
265 266
    if (m_availableSdkPlatformsUpToDate)
        return;
267 268
    m_availableSdkPlatforms.clear();

BogDan Vatra's avatar
BogDan Vatra committed
269
    QProcess proc;
270
    proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
Tobias Hunger's avatar
Tobias Hunger committed
271
    proc.start(androidToolPath().toString(), QStringList() << QLatin1String("list") << QLatin1String("target")); // list avaialbe AVDs
272
    if (!proc.waitForFinished(5000)) {
BogDan Vatra's avatar
BogDan Vatra committed
273
        proc.terminate();
274
        return;
BogDan Vatra's avatar
BogDan Vatra committed
275
    }
276
    while (proc.canReadLine()) {
277
        const QString line = QString::fromLocal8Bit(proc.readLine().trimmed());
BogDan Vatra's avatar
BogDan Vatra committed
278 279 280
        int index = line.indexOf(QLatin1String("\"android-"));
        if (index == -1)
            continue;
281 282 283 284
        QString androidTarget = line.mid(index + 1, line.length() - index - 2);
        int apiLevel = androidTarget.mid(androidTarget.lastIndexOf(QLatin1Char('-')) + 1).toInt();
        QVector<int>::iterator it = qLowerBound(m_availableSdkPlatforms.begin(), m_availableSdkPlatforms.end(), apiLevel, qGreater<int>());
        m_availableSdkPlatforms.insert(it, apiLevel);
BogDan Vatra's avatar
BogDan Vatra committed
285
    }
Daniel Teske's avatar
Daniel Teske committed
286
    m_availableSdkPlatformsUpToDate = true;
287 288
}

Daniel Teske's avatar
Daniel Teske committed
289
QStringList AndroidConfig::sdkTargets(int minApiLevel) const
290
{
Daniel Teske's avatar
Daniel Teske committed
291
    updateAvailableSdkPlatforms();
292 293 294 295 296 297 298 299
    QStringList result;
    for (int i = 0; i < m_availableSdkPlatforms.size(); ++i) {
        if (m_availableSdkPlatforms.at(i) >= minApiLevel)
            result << QLatin1String("android-") + QString::number(m_availableSdkPlatforms.at(i));
        else
            break;
    }
    return result;
BogDan Vatra's avatar
BogDan Vatra committed
300 301
}

Daniel Teske's avatar
Daniel Teske committed
302
FileName AndroidConfig::adbToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
303
{
Daniel Teske's avatar
Daniel Teske committed
304
    Utils::FileName path = m_sdkLocation;
305
    return path.appendPath(QLatin1String("platform-tools/adb" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
306 307
}

Daniel Teske's avatar
Daniel Teske committed
308
Utils::Environment AndroidConfig::androidToolEnvironment() const
309 310
{
    Utils::Environment env = Utils::Environment::systemEnvironment();
Daniel Teske's avatar
Daniel Teske committed
311 312
    if (!m_openJDKLocation.isEmpty())
        env.set(QLatin1String("JAVA_HOME"), m_openJDKLocation.toUserOutput());
313 314 315
    return env;
}

Daniel Teske's avatar
Daniel Teske committed
316
FileName AndroidConfig::androidToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
317
{
318 319 320
    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
321
        FileName path = m_sdkLocation;
322
        path.appendPath(QLatin1String("tools/android" QTC_HOST_EXE_SUFFIX));
323 324
        if (path.toFileInfo().exists())
            return path;
Daniel Teske's avatar
Daniel Teske committed
325
        path = m_sdkLocation;
326
        return path.appendPath(QLatin1String("tools/android" ANDROID_BAT_SUFFIX));
327
    } else {
Daniel Teske's avatar
Daniel Teske committed
328
        FileName path = m_sdkLocation;
329 330
        return path.appendPath(QLatin1String("tools/android"));
    }
BogDan Vatra's avatar
BogDan Vatra committed
331 332
}

Daniel Teske's avatar
Daniel Teske committed
333
FileName AndroidConfig::antToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
334
{
Daniel Teske's avatar
Daniel Teske committed
335 336
    if (!m_antLocation.isEmpty())
        return m_antLocation;
BogDan Vatra's avatar
BogDan Vatra committed
337
    else
338
        return FileName::fromLatin1("ant");
BogDan Vatra's avatar
BogDan Vatra committed
339 340
}

Daniel Teske's avatar
Daniel Teske committed
341
FileName AndroidConfig::emulatorToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
342
{
Daniel Teske's avatar
Daniel Teske committed
343
    FileName path = m_sdkLocation;
344
    return path.appendPath(QLatin1String("tools/emulator" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
345 346
}

Daniel Teske's avatar
Daniel Teske committed
347
FileName AndroidConfig::toolPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
348
{
Daniel Teske's avatar
Daniel Teske committed
349
    FileName path = m_ndkLocation;
Tobias Hunger's avatar
Tobias Hunger committed
350
    return path.appendPath(QString::fromLatin1("toolchains/%1-%2/prebuilt/%3/bin/%4")
BogDan Vatra's avatar
BogDan Vatra committed
351
            .arg(toolchainPrefix(architecture))
352
            .arg(ndkToolChainVersion)
Daniel Teske's avatar
Daniel Teske committed
353
            .arg(toolchainHost())
Tobias Hunger's avatar
Tobias Hunger committed
354
            .arg(toolsPrefix(architecture)));
BogDan Vatra's avatar
BogDan Vatra committed
355 356
}

Daniel Teske's avatar
Daniel Teske committed
357
FileName AndroidConfig::stripPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
358
{
359
    return toolPath(architecture, ndkToolChainVersion).appendString(QLatin1String("-strip" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
360 361
}

Daniel Teske's avatar
Daniel Teske committed
362
FileName AndroidConfig::readelfPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
363
{
364
    return toolPath(architecture, ndkToolChainVersion).appendString(QLatin1String("-readelf" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
365 366
}

Daniel Teske's avatar
Daniel Teske committed
367
FileName AndroidConfig::gccPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
368
{
369
    return toolPath(architecture, ndkToolChainVersion).appendString(QLatin1String("-gcc" QTC_HOST_EXE_SUFFIX));
370 371
}

Daniel Teske's avatar
Daniel Teske committed
372
FileName AndroidConfig::gdbPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
373
{
374
    return toolPath(architecture, ndkToolChainVersion).appendString(QLatin1String("-gdb" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
375 376
}

Daniel Teske's avatar
Daniel Teske committed
377
FileName AndroidConfig::openJDKBinPath() const
BogDan Vatra's avatar
BogDan Vatra committed
378
{
Daniel Teske's avatar
Daniel Teske committed
379
    FileName path = m_openJDKLocation;
Tobias Hunger's avatar
Tobias Hunger committed
380 381 382
    if (!path.isEmpty())
        return path.appendPath(QLatin1String("bin"));
    return path;
BogDan Vatra's avatar
BogDan Vatra committed
383 384
}

Daniel Teske's avatar
Daniel Teske committed
385
FileName AndroidConfig::keytoolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
386
{
Tobias Hunger's avatar
Tobias Hunger committed
387
    return openJDKBinPath().appendPath(keytoolName);
BogDan Vatra's avatar
BogDan Vatra committed
388 389
}

Daniel Teske's avatar
Daniel Teske committed
390
FileName AndroidConfig::jarsignerPath() const
BogDan Vatra's avatar
BogDan Vatra committed
391
{
Tobias Hunger's avatar
Tobias Hunger committed
392
    return openJDKBinPath().appendPath(jarsignerName);
BogDan Vatra's avatar
BogDan Vatra committed
393 394
}

Daniel Teske's avatar
Daniel Teske committed
395
FileName AndroidConfig::zipalignPath() const
396
{
Daniel Teske's avatar
Daniel Teske committed
397
    FileName path = m_sdkLocation;
398
    return path.appendPath(QLatin1String("tools/zipalign" QTC_HOST_EXE_SUFFIX));
399 400
}

Daniel Teske's avatar
Daniel Teske committed
401
QVector<AndroidDeviceInfo> AndroidConfig::connectedDevices(QString *error) const
BogDan Vatra's avatar
BogDan Vatra committed
402
{
403
    QVector<AndroidDeviceInfo> devices;
BogDan Vatra's avatar
BogDan Vatra committed
404
    QProcess adbProc;
Tobias Hunger's avatar
Tobias Hunger committed
405
    adbProc.start(adbToolPath().toString(), QStringList() << QLatin1String("devices"));
406
    if (!adbProc.waitForFinished(5000)) {
407
        adbProc.kill();
408
        if (error)
Daniel Teske's avatar
Daniel Teske committed
409 410 411
            *error = QApplication::translate("AndroidConfiguration",
                                             "Could not run: %1")
                .arg(adbToolPath().toString() + QLatin1String(" devices"));
BogDan Vatra's avatar
BogDan Vatra committed
412 413 414
        return devices;
    }
    QList<QByteArray> adbDevs = adbProc.readAll().trimmed().split('\n');
BogDan Vatra's avatar
BogDan Vatra committed
415 416 417 418 419 420
    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
421 422 423

    // workaround for '????????????' serial numbers:
    // can use "adb -d" when only one usb device attached
BogDan Vatra's avatar
BogDan Vatra committed
424
    foreach (const QByteArray &device, adbDevs) {
425 426
        const QString serialNo = QString::fromLatin1(device.left(device.indexOf('\t')).trimmed());
        const QString deviceType = QString::fromLatin1(device.mid(device.indexOf('\t'))).trimmed();
427 428
        if (isBootToQt(serialNo))
            continue;
429
        AndroidDeviceInfo dev;
430
        dev.serialNumber = serialNo;
431
        dev.type = serialNo.startsWith(QLatin1String("emulator")) ? AndroidDeviceInfo::Emulator : AndroidDeviceInfo::Hardware;
BogDan Vatra's avatar
BogDan Vatra committed
432
        dev.sdk = getSDKVersion(dev.serialNumber);
433
        dev.cpuAbi = getAbis(dev.serialNumber);
434
        dev.unauthorized = (deviceType == QLatin1String("unauthorized"));
BogDan Vatra's avatar
BogDan Vatra committed
435 436
        devices.push_back(dev);
    }
437

BogDan Vatra's avatar
BogDan Vatra committed
438
    qSort(devices.begin(), devices.end(), androidDevicesLessThan);
439
    if (devices.isEmpty() && error)
Daniel Teske's avatar
Daniel Teske committed
440 441 442
        *error = QApplication::translate("AndroidConfiguration",
                                         "No devices found in output of: %1")
            .arg(adbToolPath().toString() + QLatin1String(" devices"));
BogDan Vatra's avatar
BogDan Vatra committed
443 444 445
    return devices;
}

Daniel Teske's avatar
Daniel Teske committed
446
QString AndroidConfig::createAVD(QWidget *parent, int minApiLevel, QString targetArch) const
BogDan Vatra's avatar
BogDan Vatra committed
447
{
448
    QDialog d(parent);
BogDan Vatra's avatar
BogDan Vatra committed
449 450
    Ui::AddNewAVDDialog avdDialog;
    avdDialog.setupUi(&d);
451 452
    // NOTE: adb list targets does actually include information on which abis are supported per apilevel
    // we aren't using that information here
453
    avdDialog.targetComboBox->addItems(sdkTargets(minApiLevel));
454 455 456 457 458 459 460 461 462 463

    if (targetArch.isEmpty())
        avdDialog.abiComboBox->addItems(QStringList()
                                        << QLatin1String("armeabi-v7a")
                                        << QLatin1String("armeabi")
                                        << QLatin1String("x86")
                                        << QLatin1String("mips"));
    else
        avdDialog.abiComboBox->addItems(QStringList(targetArch));

464
    if (!avdDialog.targetComboBox->count()) {
Daniel Teske's avatar
Daniel Teske committed
465 466 467
        QMessageBox::critical(0, QApplication::translate("AndroidConfig", "Error Creating AVD"),
                              QApplication::translate("AndroidConfig", "Cannot create a new AVD. No sufficiently recent Android SDK available.\n"
                                                      "Please install an SDK of at least API version %1.").
Friedemann Kleint's avatar
Friedemann Kleint committed
468
                              arg(minApiLevel));
469
        return QString();
BogDan Vatra's avatar
BogDan Vatra committed
470 471 472 473 474 475
    }

    QRegExp rx(QLatin1String("\\S+"));
    QRegExpValidator v(rx, 0);
    avdDialog.nameLineEdit->setValidator(&v);
    if (d.exec() != QDialog::Accepted)
476
        return QString();
Daniel Teske's avatar
Daniel Teske committed
477 478
    return createAVD(avdDialog.targetComboBox->currentText(), avdDialog.nameLineEdit->text(),
                     avdDialog.abiComboBox->currentText(), avdDialog.sizeSpinBox->value());
BogDan Vatra's avatar
BogDan Vatra committed
479 480
}

Daniel Teske's avatar
Daniel Teske committed
481
QString AndroidConfig::createAVD(const QString &target, const QString &name, const QString &abi, int sdcardSize) const
BogDan Vatra's avatar
BogDan Vatra committed
482 483
{
    QProcess proc;
484
    proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
Tobias Hunger's avatar
Tobias Hunger committed
485
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
486
               QStringList() << QLatin1String("create") << QLatin1String("avd")
487
               << QLatin1String("-t") << target
BogDan Vatra's avatar
BogDan Vatra committed
488
               << QLatin1String("-n") << name
489
               << QLatin1String("-b") << abi
BogDan Vatra's avatar
BogDan Vatra committed
490 491
               << QLatin1String("-c") << QString::fromLatin1("%1M").arg(sdcardSize));
    if (!proc.waitForStarted())
492
        return QString();
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513

    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
514
    }
515

516 517
    Core::MessageManager::write(QString::fromLocal8Bit(question), Core::MessageManager::Flash);

518 519
    proc.waitForFinished();

520 521 522
    if (proc.exitCode()) // error!
        return QString();
    return name;
BogDan Vatra's avatar
BogDan Vatra committed
523 524
}

Daniel Teske's avatar
Daniel Teske committed
525
bool AndroidConfig::removeAVD(const QString &name) const
BogDan Vatra's avatar
BogDan Vatra committed
526 527
{
    QProcess proc;
528
    proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
Tobias Hunger's avatar
Tobias Hunger committed
529
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
530 531
               QStringList() << QLatin1String("delete") << QLatin1String("avd")
               << QLatin1String("-n") << name);
532
    if (!proc.waitForFinished(5000)) {
BogDan Vatra's avatar
BogDan Vatra committed
533 534 535 536 537 538
        proc.terminate();
        return false;
    }
    return !proc.exitCode();
}

Daniel Teske's avatar
Daniel Teske committed
539
QVector<AndroidDeviceInfo> AndroidConfig::androidVirtualDevices() const
BogDan Vatra's avatar
BogDan Vatra committed
540
{
541
    QVector<AndroidDeviceInfo> devices;
BogDan Vatra's avatar
BogDan Vatra committed
542
    QProcess proc;
543
    proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
Tobias Hunger's avatar
Tobias Hunger committed
544
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
545
               QStringList() << QLatin1String("list") << QLatin1String("avd")); // list available AVDs
546
    if (!proc.waitForFinished(5000)) {
BogDan Vatra's avatar
BogDan Vatra committed
547 548 549 550
        proc.terminate();
        return devices;
    }
    QList<QByteArray> avds = proc.readAll().trimmed().split('\n');
BogDan Vatra's avatar
BogDan Vatra committed
551 552 553 554 555 556 557
    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

558
    AndroidDeviceInfo dev;
BogDan Vatra's avatar
BogDan Vatra committed
559 560 561 562 563 564 565 566 567 568 569 570 571 572
    for (int i = 0; i < avds.size(); i++) {
        QString line = QLatin1String(avds[i]);
        if (!line.contains(QLatin1String("Name:")))
            continue;

        dev.serialNumber = line.mid(line.indexOf(QLatin1Char(':')) + 2).trimmed();
        ++i;
        for (; i < avds.size(); ++i) {
            line = QLatin1String(avds[i]);
            if (line.contains(QLatin1String("---------")))
                break;
            if (line.contains(QLatin1String("Target:")))
                dev.sdk = line.mid(line.lastIndexOf(QLatin1Char(' '))).remove(QLatin1Char(')')).toInt();
            if (line.contains(QLatin1String("ABI:")))
573
                dev.cpuAbi = QStringList() << line.mid(line.lastIndexOf(QLatin1Char(' '))).trimmed();
BogDan Vatra's avatar
BogDan Vatra committed
574
        }
575 576 577
        // armeabi-v7a devices can also run armeabi code
        if (dev.cpuAbi == QStringList(QLatin1String("armeabi-v7a")))
            dev.cpuAbi << QLatin1String("armeabi");
578
        dev.unauthorized = false;
579
        dev.type = AndroidDeviceInfo::Emulator;
BogDan Vatra's avatar
BogDan Vatra committed
580 581 582 583 584 585 586
        devices.push_back(dev);
    }
    qSort(devices.begin(), devices.end(), androidDevicesLessThan);

    return devices;
}

Daniel Teske's avatar
Daniel Teske committed
587
QString AndroidConfig::startAVD(const QString &name, int apiLevel, QString cpuAbi) const
588
{
589
    if (!findAvd(apiLevel, cpuAbi).isEmpty() || startAVDAsync(name))
590 591 592
        return waitForAvd(apiLevel, cpuAbi);
    return QString();
}
BogDan Vatra's avatar
BogDan Vatra committed
593

Daniel Teske's avatar
Daniel Teske committed
594
bool AndroidConfig::startAVDAsync(const QString &avdName) const
595 596
{
    QProcess *avdProcess = new QProcess();
Daniel Teske's avatar
Daniel Teske committed
597
    avdProcess->connect(avdProcess, SIGNAL(finished(int)), avdProcess, SLOT(deleteLater()));
BogDan Vatra's avatar
BogDan Vatra committed
598 599

    // start the emulator
Orgad Shaneh's avatar
Orgad Shaneh committed
600
    avdProcess->start(emulatorToolPath().toString(),
Daniel Teske's avatar
Daniel Teske committed
601
                        QStringList() << QLatin1String("-partition-size") << QString::number(partitionSize())
BogDan Vatra's avatar
BogDan Vatra committed
602
                        << QLatin1String("-avd") << avdName);
Orgad Shaneh's avatar
Orgad Shaneh committed
603 604
    if (!avdProcess->waitForStarted(-1)) {
        delete avdProcess;
605
        return false;
BogDan Vatra's avatar
BogDan Vatra committed
606
    }
607 608 609
    return true;
}

Daniel Teske's avatar
Daniel Teske committed
610
QString AndroidConfig::findAvd(int apiLevel, const QString &cpuAbi) const
611 612 613 614 615 616 617 618 619
{
    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;
620
        return device.serialNumber;
621
    }
622
    return QString();
623 624
}

Daniel Teske's avatar
Daniel Teske committed
625
bool AndroidConfig::isConnected(const QString &serialNumber) const
626
{
Daniel Teske's avatar
Daniel Teske committed
627 628 629 630 631 632 633
    QVector<AndroidDeviceInfo> devices = connectedDevices();
    foreach (AndroidDeviceInfo device, devices) {
        if (device.serialNumber == serialNumber)
            return true;
    }
    return false;
}
634

Daniel Teske's avatar
Daniel Teske committed
635
bool AndroidConfig::waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const
Daniel Teske's avatar
Daniel Teske committed
636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
{
    // 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
652
QString AndroidConfig::waitForAvd(int apiLevel, const QString &cpuAbi, const QFutureInterface<bool> &fi) const
Daniel Teske's avatar
Daniel Teske committed
653 654 655
{
    // 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
656
    QString serialNumber;
Daniel Teske's avatar
Daniel Teske committed
657 658
    for (int i = 0; i < 60; ++i) {
        if (fi.isCanceled())
Orgad Shaneh's avatar
Orgad Shaneh committed
659
            return QString();
Daniel Teske's avatar
Daniel Teske committed
660 661 662 663
        serialNumber = findAvd(apiLevel, cpuAbi);
        if (!serialNumber.isEmpty())
            return waitForBooted(serialNumber, fi) ?  serialNumber : QString();
        Utils::sleep(2000);
BogDan Vatra's avatar
BogDan Vatra committed
664 665 666 667
    }
    return QString();
}

Daniel Teske's avatar
Daniel Teske committed
668
bool AndroidConfig::isBootToQt(const QString &device) const
669 670 671 672 673 674 675 676
{
    // 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);
677
    if (!adbProc.waitForFinished(5000)) {
678
        adbProc.kill();
679
        return false;
680 681 682 683
    }
    return adbProc.readAll().contains("Boot2Qt");
}

Daniel Teske's avatar
Daniel Teske committed
684
int AndroidConfig::getSDKVersion(const QString &device) const
BogDan Vatra's avatar
BogDan Vatra committed
685
{
686 687 688 689
    // 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
690 691

    QProcess adbProc;
692
    adbProc.start(adbToolPath().toString(), arguments);
693
    if (!adbProc.waitForFinished(5000)) {
694
        adbProc.kill();
BogDan Vatra's avatar
BogDan Vatra committed
695 696 697 698 699
        return -1;
    }
    return adbProc.readAll().trimmed().toInt();
}

700 701 702 703 704
//!
//! \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
705
QString AndroidConfig::getProductModel(const QString &device) const
706
{
707 708
    if (m_serialNumberToDeviceName.contains(device))
        return m_serialNumberToDeviceName.value(device);
709 710 711 712 713 714 715
    // 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);
716
    if (!adbProc.waitForFinished(5000)) {
717 718 719 720 721 722
        adbProc.kill();
        return device;
    }
    QString model = QString::fromLocal8Bit(adbProc.readAll().trimmed());
    if (model.isEmpty())
        return device;
723 724
    if (!device.startsWith(QLatin1String("????")))
        m_serialNumberToDeviceName.insert(device, model);
725 726 727
    return model;
}

Daniel Teske's avatar
Daniel Teske committed
728
bool AndroidConfig::hasFinishedBooting(const QString &device) const
729 730 731 732 733 734 735
{
    QStringList arguments = AndroidDeviceInfo::adbSelector(device);
    arguments << QLatin1String("shell") << QLatin1String("getprop")
              << QLatin1String("init.svc.bootanim");

    QProcess adbProc;
    adbProc.start(adbToolPath().toString(), arguments);
736
    if (!adbProc.waitForFinished(5000)) {
737 738 739 740 741 742 743 744 745
        adbProc.kill();
        return false;
    }
    QString value = QString::fromLocal8Bit(adbProc.readAll().trimmed());
    if (value == QLatin1String("stopped"))
        return true;
    return false;
}

Daniel Teske's avatar
Daniel Teske committed
746
QStringList AndroidConfig::getAbis(const QString &device) const
747 748
{
    QStringList result;
749
    for (int i = 1; i < 6; ++i) {
750 751 752 753 754 755 756 757 758
        QStringList arguments = AndroidDeviceInfo::adbSelector(device);
        arguments << QLatin1String("shell") << QLatin1String("getprop");
        if (i == 1)
            arguments << QLatin1String("ro.product.cpu.abi");
        else
            arguments << QString::fromLatin1("ro.product.cpu.abi%1").arg(i);

        QProcess adbProc;
        adbProc.start(adbToolPath().toString(), arguments);
759
        if (!adbProc.waitForFinished(5000)) {
760 761 762 763 764 765 766 767 768 769 770
            adbProc.kill();
            return result;
        }
        QString abi = QString::fromLocal8Bit(adbProc.readAll().trimmed());
        if (abi.isEmpty())
            break;
        result << abi;
    }
    return result;
}

Daniel Teske's avatar
Daniel Teske committed
771
QString AndroidConfig::highestAndroidSdk() const
772
{
Daniel Teske's avatar
Daniel Teske committed
773
    updateAvailableSdkPlatforms();
774
    if (m_availableSdkPlatforms.isEmpty())
775
        return QString();
776
    return QLatin1String("android-") + QString::number(m_availableSdkPlatforms.first());
777 778
}

Daniel Teske's avatar
Daniel Teske committed
779
QString AndroidConfig::bestNdkPlatformMatch(const QString &targetAPI) const
BogDan Vatra's avatar
BogDan Vatra committed
780
{
Daniel Teske's avatar
Daniel Teske committed
781
    updateNdkInformation();
BogDan Vatra's avatar
BogDan Vatra committed
782
    int target = targetAPI.mid(targetAPI.lastIndexOf(QLatin1Char('-')) + 1).toInt();
783
    foreach (int apiLevel, m_availableNdkPlatforms) {
BogDan Vatra's avatar
BogDan Vatra committed
784 785 786 787 788 789
        if (apiLevel <= target)
            return QString::fromLatin1("android-%1").arg(apiLevel);
    }
    return QLatin1String("android-8");
}

Daniel Teske's avatar
Daniel Teske committed
790
FileName AndroidConfig::sdkLocation() const
791
{
Daniel Teske's avatar
Daniel Teske committed
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828