androidconfigurations.cpp 48.8 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
227
228
229
230
231
232
233
        m_sdkLocation = FileName::fromString(reader.restoreValue(SDKLocationKey, m_sdkLocation.toString()).toString());
        m_ndkLocation = FileName::fromString(reader.restoreValue(NDKLocationKey, m_ndkLocation.toString()).toString());
        m_antLocation = FileName::fromString(reader.restoreValue(AntLocationKey, m_antLocation.toString()).toString());
        m_openJDKLocation = FileName::fromString(reader.restoreValue(OpenJDKLocationKey, m_openJDKLocation.toString()).toString());
        m_keystoreLocation = FileName::fromString(reader.restoreValue(KeystoreLocationKey, m_keystoreLocation.toString()).toString());
        m_toolchainHost = reader.restoreValue(ToolchainHostKey, m_toolchainHost).toString();
        m_automaticKitCreation = reader.restoreValue(AutomaticKitCreationKey, m_automaticKitCreation).toBool();
234
        QString extraDirectory = reader.restoreValue(MakeExtraSearchDirectory).toString();
Daniel Teske's avatar
Daniel Teske committed
235
        m_makeExtraSearchDirectories.clear();
236
        if (!extraDirectory.isEmpty())
Daniel Teske's avatar
Daniel Teske committed
237
            m_makeExtraSearchDirectories << extraDirectory;
BogDan Vatra's avatar
BogDan Vatra committed
238
239
        // persistent settings
    }
Daniel Teske's avatar
Daniel Teske committed
240
241
    m_availableSdkPlatformsUpToDate = false;
    m_NdkInformationUpToDate = false;
BogDan Vatra's avatar
BogDan Vatra committed
242
243
244
}

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

BogDan Vatra's avatar
BogDan Vatra committed
250
251
252
253
}

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

    // user settings
Daniel Teske's avatar
Daniel Teske committed
259
260
261
    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
262
    settings.setValue(UseGradleKey, m_useGradle);
Daniel Teske's avatar
Daniel Teske committed
263
264
265
266
267
    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);
268
    settings.setValue(MakeExtraSearchDirectory,
Daniel Teske's avatar
Daniel Teske committed
269
270
                      m_makeExtraSearchDirectories.isEmpty() ? QString()
                                                             : m_makeExtraSearchDirectories.at(0));
BogDan Vatra's avatar
BogDan Vatra committed
271
272
}

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

    // 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
301
    path = ndkLocation();
Daniel Teske's avatar
Daniel Teske committed
302
303
304
305
306
307
308
    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
309
310
}

311
312
313
314
315
316
317
318
319
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
320
void AndroidConfig::updateAvailableSdkPlatforms() const
BogDan Vatra's avatar
BogDan Vatra committed
321
{
Daniel Teske's avatar
Daniel Teske committed
322
323
    if (m_availableSdkPlatformsUpToDate)
        return;
324
325
    m_availableSdkPlatforms.clear();

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

    SdkPlatform platform;
335
    while (proc.canReadLine()) {
336
        const QString line = QString::fromLocal8Bit(proc.readLine().trimmed());
337
338
339
340
341
342
343
344
        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);
345
346
        } else if (line.startsWith(QLatin1String("Tag/ABIs :"))) {
            platform.abis = cleanAndroidABIs(line.mid(10).trimmed().split(QLatin1String(", ")));
347
        } else if (line.startsWith(QLatin1String("ABIs"))) {
348
            platform.abis = cleanAndroidABIs(line.mid(6).trimmed().split(QLatin1String(", ")));
349
350
351
352
353
354
355
356
        } 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
357
    }
358
359
360
361
362
363
364

    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
365
    m_availableSdkPlatformsUpToDate = true;
366
367
}

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

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

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

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

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

Daniel Teske's avatar
Daniel Teske committed
405
FileName AndroidConfig::androidToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
406
{
407
408
409
    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
410
        FileName path = m_sdkLocation;
411
        path.appendPath(QLatin1String("tools/android" QTC_HOST_EXE_SUFFIX));
hjk's avatar
hjk committed
412
        if (path.exists())
413
            return path;
Daniel Teske's avatar
Daniel Teske committed
414
        path = m_sdkLocation;
415
        return path.appendPath(QLatin1String("tools/android" ANDROID_BAT_SUFFIX));
416
    } else {
Daniel Teske's avatar
Daniel Teske committed
417
        FileName path = m_sdkLocation;
418
419
        return path.appendPath(QLatin1String("tools/android"));
    }
BogDan Vatra's avatar
BogDan Vatra committed
420
421
}

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

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

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

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

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

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

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

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

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

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

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

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

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

538
AndroidConfig::CreateAvdInfo AndroidConfig::createAVDImpl(CreateAvdInfo info, Utils::FileName androidToolPath, Utils::Environment env)
BogDan Vatra's avatar
BogDan Vatra committed
539
540
{
    QProcess proc;
541
542
543
544
545
546
547
548
549
550
    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
551
                .arg(androidToolPath.toString(), arguments.join(QLatin1Char(' ')));
552
553
        return info;
    }
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574

    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
575
    }
576
577
578

    proc.waitForFinished();

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

586
    return info;
BogDan Vatra's avatar
BogDan Vatra committed
587
588
}

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

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

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

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

    return devices;
}

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

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

    // start the emulator
688
689
690
691
692
693
694
    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
695
696
    if (!avdProcess->waitForStarted(-1)) {
        delete avdProcess;
697
        return false;
BogDan Vatra's avatar
BogDan Vatra committed
698
    }
699
700
701
    return true;
}

Daniel Teske's avatar
Daniel Teske committed
702
QString AndroidConfig::findAvd(int apiLevel, const QString &cpuAbi) const
703
704
705
706
707
708
709
710
711
{
    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;
712
        return device.serialNumber;
713
    }
714
    return QString();
715
716
}

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

Daniel Teske's avatar
Daniel Teske committed
727
bool AndroidConfig::waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const
Daniel Teske's avatar
Daniel Teske committed
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
{
    // 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
744
QString AndroidConfig::waitForAvd(int apiLevel, const QString &cpuAbi, const QFutureInterface<bool> &fi) const
Daniel Teske's avatar
Daniel Teske committed
745
746
747
{
    // 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
748
    QString serialNumber;
Daniel Teske's avatar
Daniel Teske committed
749
750
    for (int i = 0; i < 60; ++i) {
        if (fi.isCanceled())
Orgad Shaneh's avatar
Orgad Shaneh committed
751
            return QString();
Daniel Teske's avatar
Daniel Teske committed
752
753
754
755
        serialNumber = findAvd(apiLevel, cpuAbi);
        if (!serialNumber.isEmpty())
            return waitForBooted(serialNumber, fi) ?  serialNumber : QString();
        Utils::sleep(2000);
BogDan Vatra's avatar
BogDan Vatra committed
756
757
758
759
    }
    return QString();
}

Daniel Teske's avatar
Daniel Teske committed
760
bool AndroidConfig::isBootToQt(const QString &device) const
761
762
763
764
765
766
767
768
{
    // 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);
769
    if (!adbProc.waitForFinished(10000)) {
770
        adbProc.kill();
771
        return false;
772
773
774
775
    }
    return adbProc.readAll().contains("Boot2Qt");
}

Daniel Teske's avatar
Daniel Teske committed
776
int AndroidConfig::getSDKVersion(const QString &device) const
BogDan Vatra's avatar
BogDan Vatra committed
777
{
778
779
780
781
    // 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
782
783

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

792
793
794
795
796
//!
//! \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
797
QString AndroidConfig::getProductModel(const QString &device) const
798
{
799
800
    if (m_serialNumberToDeviceName.contains(device))
        return m_serialNumberToDeviceName.value(device);
801
802
803
804
805
806
807
    // 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);
808
    if (!adbProc.waitForFinished(10000)) {
809
810
811
812
813
814
        adbProc.kill();
        return device;
    }
    QString model = QString::fromLocal8Bit(adbProc.readAll().trimmed());
    if (model.isEmpty())
        return device;
815
816
    if (!device.startsWith(QLatin1String("????")))
        m_serialNumberToDeviceName.insert(device, model);
817
818
819
    return model;
}

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

    QProcess adbProc;
    adbProc.start(adbToolPath().toString(), arguments);
828
    if (!adbProc.waitForFinished(10000)) {
829
830
831
832
833
834
835
836
837
        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
838
QStringList AndroidConfig::getAbis(const QString &device) const
839
840
{
    QStringList result;
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
    // First try via ro.product.cpu.abilist
    QStringList arguments = AndroidDeviceInfo::adbSelector(device);
    arguments << QLatin1String("shell") << QLatin1String("getprop");
    arguments << QLatin1String("ro.product.cpu.abilist");
    QProcess adbProc;
    adbProc.start(adbToolPath().toString(), arguments);
    if (!adbProc.waitForFinished(10000)) {
        adbProc.kill();
        return result;
    }
    QString output = QString::fromLocal8Bit(adbProc.readAll().trimmed());
    if (!output.isEmpty()) {
        QStringList result = output.split(QLatin1Char(','));
        if (!result.isEmpty())
            return result;
    }

    // Fall back to ro.product.cpu.abi, ro.product.cpu.abi2 ...
859
    for (int i = 1; i < 6; ++i) {
860
861
862
863
864
865
866
867
868
        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);
869
        if (!adbProc.waitForFinished(10000)) {
870
871
872
873
874
875
876
877
878
879
880
            adbProc.kill();
            return result;
        }
        QString abi = QString::fromLocal8Bit(adbProc.readAll().trimmed());
        if (abi.isEmpty())
            break;
        result << abi;
    }
    return result;
}

881
SdkPlatform AndroidConfig::highestAndroidSdk() const
882
{
Daniel Teske's avatar
Daniel Teske committed
883
    updateAvailableSdkPlatforms();
884
    if (m_availableSdkPlatforms.isEmpty())
885
886
        return SdkPlatform();
    return m_availableSdkPlatforms.first();
887
888
}

889
QString AndroidConfig::bestNdkPlatformMatch(int target) const
BogDan Vatra's avatar
BogDan Vatra committed
890
{
891
    target = std::max(9, target);
892
    updateNdkInformation();
893
    foreach (int apiLevel, m_availableNdkPlatforms) {
BogDan Vatra's avatar
BogDan Vatra committed
894
895
896
        if (apiLevel <= target)
            return QString::fromLatin1("android-%1").arg(apiLevel);
    }
897
    return QLatin1String("android-9");
BogDan Vatra's avatar
BogDan Vatra committed
898
899
}

Daniel Teske's avatar
Daniel Teske committed
900
FileName AndroidConfig::sdkLocation() const
901
{
Daniel Teske's avatar
Daniel Teske committed
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
    return m_sdkLocation;
}

void AndroidConfig::setSdkLocation(const FileName &sdkLocation)
{
    m_sdkLocation = sdkLocation;
    m_availableSdkPlatformsUpToDate = false;
}

FileName AndroidConfig::ndkLocation() const
{
    return m_ndkLocation;
}

void AndroidConfig::setNdkLocation(const FileName &ndkLocation)
{
    m_ndkLocation = ndkLocation;
    m_NdkInformationUpToDate = false;
}

FileName AndroidConfig::antLocation() const
{
    return m_antLocation;
}

void AndroidConfig::setAntLocation(const FileName &antLocation)
{
    m_antLocation = antLocation;
}

FileName AndroidConfig::openJDKLocation() const
{
    return m_openJDKLocation;
}

void AndroidConfig::setOpenJDKLocation(const FileName &openJDKLocation)
{
    m_openJDKLocation = openJDKLocation;
    m_availableSdkPlatformsUpToDate = false;
}

FileName AndroidConfig::keystoreLocation() const
{
    return m_keystoreLocation;
}

void AndroidConfig::setKeystoreLocation(const FileName &keystoreLocation)
{
    m_keystoreLocation = keystoreLocation;
}

QString AndroidConfig::toolchainHost() const
{
    updateNdkInformation();
    return m_toolchainHost;
}

QStringList AndroidConfig::makeExtraSearchDirectories() const
{
    return m_makeExtraSearchDirectories;
}

unsigned AndroidConfig::partitionSize() const
{
    return m_partitionSize;
}

void AndroidConfig::setPartitionSize(unsigned partitionSize)
{
    m_partitionSize = partitionSize;
}

bool AndroidConfig::automaticKitCreation() const
{
    return m_automaticKitCreation;
}

void AndroidConfig::setAutomaticKitCreation(bool b)
{
    m_automaticKitCreation = b;
}

BogDan Vatra's avatar
BogDan Vatra committed
984
985
986
987
988
989
990
991
992
993
bool AndroidConfig::useGrandle() const
{
    return m_useGradle;
}

void AndroidConfig::setUseGradle(bool b)
{
    m_useGradle = b;
}

Daniel Teske's avatar
Daniel Teske committed
994
995
996
997
998
999
1000
1001
1002
///////////////////////////////////
// AndroidConfigurations
///////////////////////////////////
void AndroidConfigurations::setConfig(const AndroidConfig &devConfigs)
{
    m_instance->m_config = devConfigs;

    m_instance->save();
    m_instance->updateAndroidDevice();
1003
1004
    m_instance->updateToolChainList();
    m_instance->updateAutomaticKitList();
Daniel Teske's avatar
Daniel Teske committed
1005
1006
1007
    emit m_instance->updated();
}

1008
1009
1010
AndroidDeviceInfo AndroidConfigurations::showDeviceDialog(ProjectExplorer::Project *project,
                                                          int apiLevel, const QString &abi,
                                                          Options options)
Daniel Teske's avatar
Daniel Teske committed
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
{
    QString serialNumber = defaultDevice(project, abi);
    if (!serialNumber.isEmpty()) {
        // search for that device
        foreach (const AndroidDeviceInfo &info, AndroidConfigurations::currentConfig().connectedDevices())
            if (info.serialNumber == serialNumber
                    && info.sdk >= apiLevel)
                return info;

        foreach (const AndroidDeviceInfo &info, AndroidConfigurations::currentConfig().androidVirtualDevices())
            if (info.serialNumber == serialNumber
                    && info.sdk >= apiLevel)
                return info;
    }

1026
    AndroidDeviceDialog dialog(apiLevel, abi, options, Core::ICore::mainWindow());
Daniel Teske's avatar
Daniel Teske committed
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
    if (dialog.exec() == QDialog::Accepted) {
        AndroidDeviceInfo info = dialog.device();
        if (dialog.saveDeviceSelection()) {
            if (!info.serialNumber.isEmpty())
                AndroidConfigurations::setDefaultDevice(project, abi, info.serialNumber);
        }
        return info;
    }
    return AndroidDeviceInfo();
}

void AndroidConfigurations::clearDefaultDevices(ProjectExplorer::Project *project)
{
    if (m_instance->m_defaultDeviceForAbi.contains(project))
        m_instance->m_defaultDeviceForAbi.remove(project);
}

void AndroidConfigurations::setDefaultDevice(ProjectExplorer::Project *project, const QString &abi, const QString &serialNumber)
{
    m_instance->m_defaultDeviceForAbi[project][abi] = serialNumber;
}

QString AndroidConfigurations::defaultDevice(Project *project, const QString &abi)
{
    if (!m_instance->m_defaultDeviceForAbi.contains(project))
        return QString();
    const QMap<QString, QString> &map = m_instance->m_defaultDeviceForAbi.value(project);
    if (!map.contains(abi))
        return QString();
    return map.value(abi);
1057
1058
}

1059
static bool equalKits(Kit *a, Kit *b)