androidconfigurations.cpp 45.4 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);
Daniel Teske's avatar
Daniel Teske committed
434 435 436 437 438 439
        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
440 441
        devices.push_back(dev);
    }
442

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

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

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

469
    if (!avdDialog.targetComboBox->count()) {
470
        QMessageBox::critical(parent, QApplication::translate("AndroidConfig", "Error Creating AVD"),
Daniel Teske's avatar
Daniel Teske committed
471 472
                              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
473
                              arg(minApiLevel));
474
        return QString();
BogDan Vatra's avatar
BogDan Vatra committed
475 476 477 478 479 480
    }

    QRegExp rx(QLatin1String("\\S+"));
    QRegExpValidator v(rx, 0);
    avdDialog.nameLineEdit->setValidator(&v);
    if (d.exec() != QDialog::Accepted)
481
        return QString();
482 483 484 485 486 487 488 489 490 491
    QString error;
    QString avd = createAVD(avdDialog.targetComboBox->currentText(), avdDialog.nameLineEdit->text(),
                            avdDialog.abiComboBox->currentText(), avdDialog.sizeSpinBox->value(),
                            &error);
    if (!error.isEmpty()) {
        QMessageBox::critical(parent, QApplication::translate("AndroidConfig", "Error Creating AVD"),
                              error);
    }

    return avd;
BogDan Vatra's avatar
BogDan Vatra committed
492 493
}

494
QString AndroidConfig::createAVD(const QString &target, const QString &name, const QString &abi, int sdcardSize, QString *error) const
BogDan Vatra's avatar
BogDan Vatra committed
495 496
{
    QProcess proc;
497
    proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
Tobias Hunger's avatar
Tobias Hunger committed
498
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
499
               QStringList() << QLatin1String("create") << QLatin1String("avd")
500
               << QLatin1String("-t") << target
BogDan Vatra's avatar
BogDan Vatra committed
501
               << QLatin1String("-n") << name
502
               << QLatin1String("-b") << abi
BogDan Vatra's avatar
BogDan Vatra committed
503 504
               << QLatin1String("-c") << QString::fromLatin1("%1M").arg(sdcardSize));
    if (!proc.waitForStarted())
505
        return QString();
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526

    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
527
    }
528 529 530

    proc.waitForFinished();

531 532 533 534 535
    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()) {
        *error = errorOutput;
536
        return QString();
537 538
    }

539
    return name;
BogDan Vatra's avatar
BogDan Vatra committed
540 541
}

Daniel Teske's avatar
Daniel Teske committed
542
bool AndroidConfig::removeAVD(const QString &name) const
BogDan Vatra's avatar
BogDan Vatra committed
543 544
{
    QProcess proc;
545
    proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
Tobias Hunger's avatar
Tobias Hunger committed
546
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
547 548
               QStringList() << QLatin1String("delete") << QLatin1String("avd")
               << QLatin1String("-n") << name);
549
    if (!proc.waitForFinished(5000)) {
BogDan Vatra's avatar
BogDan Vatra committed
550 551 552 553 554 555
        proc.terminate();
        return false;
    }
    return !proc.exitCode();
}

Daniel Teske's avatar
Daniel Teske committed
556
QVector<AndroidDeviceInfo> AndroidConfig::androidVirtualDevices() const
BogDan Vatra's avatar
BogDan Vatra committed
557
{
558
    QVector<AndroidDeviceInfo> devices;
BogDan Vatra's avatar
BogDan Vatra committed
559
    QProcess proc;
560
    proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
Tobias Hunger's avatar
Tobias Hunger committed
561
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
562
               QStringList() << QLatin1String("list") << QLatin1String("avd")); // list available AVDs
563
    if (!proc.waitForFinished(5000)) {
BogDan Vatra's avatar
BogDan Vatra committed
564 565 566 567
        proc.terminate();
        return devices;
    }
    QList<QByteArray> avds = proc.readAll().trimmed().split('\n');
BogDan Vatra's avatar
BogDan Vatra committed
568 569 570 571 572 573 574
    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

575
    AndroidDeviceInfo dev;
BogDan Vatra's avatar
BogDan Vatra committed
576 577 578 579 580 581 582 583 584 585 586 587 588
    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();
589 590 591
            if (line.contains(QLatin1String("Tag/ABI:")))
                dev.cpuAbi = QStringList() << line.mid(line.lastIndexOf(QLatin1Char('/')) +1);
            else if (line.contains(QLatin1String("ABI:")))
592
                dev.cpuAbi = QStringList() << line.mid(line.lastIndexOf(QLatin1Char(' '))).trimmed();
BogDan Vatra's avatar
BogDan Vatra committed
593
        }
594 595 596
        // 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
597
        dev.state = AndroidDeviceInfo::OkState;
598
        dev.type = AndroidDeviceInfo::Emulator;
BogDan Vatra's avatar
BogDan Vatra committed
599 600 601 602 603 604 605
        devices.push_back(dev);
    }
    qSort(devices.begin(), devices.end(), androidDevicesLessThan);

    return devices;
}

Daniel Teske's avatar
Daniel Teske committed
606
QString AndroidConfig::startAVD(const QString &name, int apiLevel, QString cpuAbi) const
607
{
608
    if (!findAvd(apiLevel, cpuAbi).isEmpty() || startAVDAsync(name))
609 610 611
        return waitForAvd(apiLevel, cpuAbi);
    return QString();
}
BogDan Vatra's avatar
BogDan Vatra committed
612

Daniel Teske's avatar
Daniel Teske committed
613
bool AndroidConfig::startAVDAsync(const QString &avdName) const
614 615
{
    QProcess *avdProcess = new QProcess();
Daniel Teske's avatar
Daniel Teske committed
616
    avdProcess->connect(avdProcess, SIGNAL(finished(int)), avdProcess, SLOT(deleteLater()));
BogDan Vatra's avatar
BogDan Vatra committed
617 618

    // start the emulator
Orgad Shaneh's avatar
Orgad Shaneh committed
619
    avdProcess->start(emulatorToolPath().toString(),
Daniel Teske's avatar
Daniel Teske committed
620
                        QStringList() << QLatin1String("-partition-size") << QString::number(partitionSize())
BogDan Vatra's avatar
BogDan Vatra committed
621
                        << QLatin1String("-avd") << avdName);
Orgad Shaneh's avatar
Orgad Shaneh committed
622 623
    if (!avdProcess->waitForStarted(-1)) {
        delete avdProcess;
624
        return false;
BogDan Vatra's avatar
BogDan Vatra committed
625
    }
626 627 628
    return true;
}

Daniel Teske's avatar
Daniel Teske committed
629
QString AndroidConfig::findAvd(int apiLevel, const QString &cpuAbi) const
630 631 632 633 634 635 636 637 638
{
    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;
639
        return device.serialNumber;
640
    }
641
    return QString();
642 643
}

Daniel Teske's avatar
Daniel Teske committed
644
bool AndroidConfig::isConnected(const QString &serialNumber) const
645
{
Daniel Teske's avatar
Daniel Teske committed
646 647 648 649 650 651 652
    QVector<AndroidDeviceInfo> devices = connectedDevices();
    foreach (AndroidDeviceInfo device, devices) {
        if (device.serialNumber == serialNumber)
            return true;
    }
    return false;
}
653

Daniel Teske's avatar
Daniel Teske committed
654
bool AndroidConfig::waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const
Daniel Teske's avatar
Daniel Teske committed
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
{
    // 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
671
QString AndroidConfig::waitForAvd(int apiLevel, const QString &cpuAbi, const QFutureInterface<bool> &fi) const
Daniel Teske's avatar
Daniel Teske committed
672 673 674
{
    // 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
675
    QString serialNumber;
Daniel Teske's avatar
Daniel Teske committed
676 677
    for (int i = 0; i < 60; ++i) {
        if (fi.isCanceled())
Orgad Shaneh's avatar
Orgad Shaneh committed
678
            return QString();
Daniel Teske's avatar
Daniel Teske committed
679 680 681 682
        serialNumber = findAvd(apiLevel, cpuAbi);
        if (!serialNumber.isEmpty())
            return waitForBooted(serialNumber, fi) ?  serialNumber : QString();
        Utils::sleep(2000);
BogDan Vatra's avatar
BogDan Vatra committed
683 684 685 686
    }
    return QString();
}

Daniel Teske's avatar
Daniel Teske committed
687
bool AndroidConfig::isBootToQt(const QString &device) const
688 689 690 691 692 693 694 695
{
    // 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);
696
    if (!adbProc.waitForFinished(5000)) {
697
        adbProc.kill();
698
        return false;
699 700 701 702
    }
    return adbProc.readAll().contains("Boot2Qt");
}

Daniel Teske's avatar
Daniel Teske committed
703
int AndroidConfig::getSDKVersion(const QString &device) const
BogDan Vatra's avatar
BogDan Vatra committed
704
{
705 706 707 708
    // 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
709 710

    QProcess adbProc;
711
    adbProc.start(adbToolPath().toString(), arguments);
712
    if (!adbProc.waitForFinished(5000)) {
713
        adbProc.kill();
BogDan Vatra's avatar
BogDan Vatra committed
714 715 716 717 718
        return -1;
    }
    return adbProc.readAll().trimmed().toInt();
}

719 720 721 722 723
//!
//! \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
724
QString AndroidConfig::getProductModel(const QString &device) const
725
{
726 727
    if (m_serialNumberToDeviceName.contains(device))
        return m_serialNumberToDeviceName.value(device);
728 729 730 731 732 733 734
    // 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);
735
    if (!adbProc.waitForFinished(5000)) {
736 737 738 739 740 741
        adbProc.kill();
        return device;
    }
    QString model = QString::fromLocal8Bit(adbProc.readAll().trimmed());
    if (model.isEmpty())
        return device;
742 743
    if (!device.startsWith(QLatin1String("????")))
        m_serialNumberToDeviceName.insert(device, model);
744 745 746
    return model;
}

Daniel Teske's avatar
Daniel Teske committed
747
bool AndroidConfig::hasFinishedBooting(const QString &device) const
748 749 750 751 752 753 754
{
    QStringList arguments = AndroidDeviceInfo::adbSelector(device);
    arguments << QLatin1String("shell") << QLatin1String("getprop")
              << QLatin1String("init.svc.bootanim");

    QProcess adbProc;
    adbProc.start(adbToolPath().toString(), arguments);
755
    if (!adbProc.waitForFinished(5000)) {
756 757 758 759 760 761 762 763 764
        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
765
QStringList AndroidConfig::getAbis(const QString &device) const
766 767
{
    QStringList result;
768
    for (int i = 1; i < 6; ++i) {
769 770 771 772 773 774 775 776 777
        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);
778
        if (!adbProc.waitForFinished(5000)) {
779 780 781 782 783 784 785 786 787 788 789
            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
790
QString AndroidConfig::highestAndroidSdk() const
791
{
Daniel Teske's avatar
Daniel Teske committed
792
    updateAvailableSdkPlatforms();
793
    if (m_availableSdkPlatforms.isEmpty())
794
        return QString();
795
    return QLatin1String("android-") + QString::number(m_availableSdkPlatforms.first());
796 797
}

Daniel Teske's avatar
Daniel Teske committed
798
QString AndroidConfig::bestNdkPlatformMatch(const QString &targetAPI) const
BogDan Vatra's avatar
BogDan Vatra committed
799
{
Daniel Teske's avatar
Daniel Teske committed
800
    updateNdkInformation();
BogDan Vatra's avatar
BogDan Vatra committed
801
    int target = targetAPI.mid(targetAPI.lastIndexOf(QLatin1Char('-')) + 1).toInt();
802
    foreach (int apiLevel, m_availableNdkPlatforms) {
BogDan Vatra's avatar
BogDan Vatra committed
803 804 805 806 807 808
        if (apiLevel <= target)
            return QString::fromLatin1("android-%1").arg(apiLevel);
    }
    return QLatin1String("android-8");
}

Daniel Teske's avatar
Daniel Teske committed
809