androidconfigurations.cpp 41.3 KB
Newer Older
BogDan Vatra's avatar
BogDan Vatra committed
1
2
/**************************************************************************
**
3
** Copyright (c) 2013 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 <utils/hostosinfo.h>
BogDan Vatra's avatar
BogDan Vatra committed
41
#include <utils/persistentsettings.h>
Daniel Teske's avatar
Daniel Teske committed
42
43
44
45
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/devicesupport/devicemanager.h>
#include <projectexplorer/toolchainmanager.h>
46
#include <projectexplorer/session.h>
47
#include <debugger/debuggeritemmanager.h>
Daniel Teske's avatar
Daniel Teske committed
48
49
50
51
#include <debugger/debuggerkitinformation.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtversionmanager.h>
Daniel Teske's avatar
Daniel Teske committed
52
#include <utils/environment.h>
Orgad Shaneh's avatar
Orgad Shaneh committed
53
#include <utils/sleep.h>
BogDan Vatra's avatar
BogDan Vatra committed
54
55
56
57
58
59
60
61
62
63
64
65

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

#include <QStringListModel>
#include <QMessageBox>

hjk's avatar
hjk committed
66
using namespace ProjectExplorer;
BogDan Vatra's avatar
BogDan Vatra committed
67
68
69
70
71
72
73
74
75
76
77
78
79
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
80
    const QLatin1String AutomaticKitCreationKey("AutomatiKitCreation");
81
    const QLatin1String MakeExtraSearchDirectory("MakeExtraSearchDirectory");
82
    const QLatin1String DefaultDevice("DefaultDevice");
BogDan Vatra's avatar
BogDan Vatra committed
83
    const QLatin1String PartitionSizeKey("PartitionSize");
84
    const QLatin1String ToolchainHostKey("ToolchainHost");
BogDan Vatra's avatar
BogDan Vatra committed
85
86
    const QLatin1String ArmToolchainPrefix("arm-linux-androideabi");
    const QLatin1String X86ToolchainPrefix("x86");
BogDan Vatra's avatar
BogDan Vatra committed
87
    const QLatin1String MipsToolchainPrefix("mipsel-linux-android");
BogDan Vatra's avatar
BogDan Vatra committed
88
    const QLatin1String ArmToolsPrefix("arm-linux-androideabi");
BogDan Vatra's avatar
BogDan Vatra committed
89
    const QLatin1String X86ToolsPrefix("i686-linux-android");
BogDan Vatra's avatar
BogDan Vatra committed
90
    const QLatin1String MipsToolsPrefix("mipsel-linux-android");
BogDan Vatra's avatar
BogDan Vatra committed
91
92
93
94
95
    const QLatin1String Unknown("unknown");
    const QLatin1String keytoolName("keytool");
    const QLatin1String jarsignerName("jarsigner");
    const QLatin1String changeTimeStamp("ChangeTimeStamp");

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

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

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

115
116
117
118
119
120
Abi::Architecture AndroidConfigurations::architectureForToolChainPrefix(const QString& toolchainprefix)
{
    if (toolchainprefix == ArmToolchainPrefix)
        return Abi::ArmArchitecture;
    if (toolchainprefix == X86ToolchainPrefix)
        return Abi::X86Architecture;
BogDan Vatra's avatar
BogDan Vatra committed
121
122
    if (toolchainprefix == MipsToolchainPrefix)
        return Abi::MipsArchitecture;
123
124
125
    return Abi::UnknownArchitecture;
}

hjk's avatar
hjk committed
126
QLatin1String AndroidConfigurations::toolchainPrefix(Abi::Architecture architecture)
BogDan Vatra's avatar
BogDan Vatra committed
127
128
{
    switch (architecture) {
hjk's avatar
hjk committed
129
    case Abi::ArmArchitecture:
BogDan Vatra's avatar
BogDan Vatra committed
130
        return ArmToolchainPrefix;
hjk's avatar
hjk committed
131
    case Abi::X86Architecture:
BogDan Vatra's avatar
BogDan Vatra committed
132
        return X86ToolchainPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
133
134
    case Abi::MipsArchitecture:
        return MipsToolchainPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
135
136
137
138
139
    default:
        return Unknown;
    }
}

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

AndroidConfig::AndroidConfig(const QSettings &settings)
{
    // user settings
    partitionSize = settings.value(PartitionSizeKey, 1024).toInt();
hjk's avatar
hjk committed
158
159
160
161
162
    sdkLocation = FileName::fromString(settings.value(SDKLocationKey).toString());
    ndkLocation = FileName::fromString(settings.value(NDKLocationKey).toString());
    antLocation = FileName::fromString(settings.value(AntLocationKey).toString());
    openJDKLocation = FileName::fromString(settings.value(OpenJDKLocationKey).toString());
    keystoreLocation = FileName::fromString(settings.value(KeystoreLocationKey).toString());
163
    toolchainHost = settings.value(ToolchainHostKey).toString();
Daniel Teske's avatar
Daniel Teske committed
164
    automaticKitCreation = settings.value(AutomaticKitCreationKey, true).toBool();
165
    QString extraDirectory = settings.value(MakeExtraSearchDirectory).toString();
166
167
    makeExtraSearchDirectories.clear();
    if (!extraDirectory.isEmpty())
168
        makeExtraSearchDirectories << extraDirectory;
BogDan Vatra's avatar
BogDan Vatra committed
169
170

    PersistentSettingsReader reader;
171
172
    if (reader.load(FileName::fromString(sdkSettingsFileName()))
            && settings.value(changeTimeStamp).toInt() != QFileInfo(sdkSettingsFileName()).lastModified().toMSecsSinceEpoch() / 1000) {
BogDan Vatra's avatar
BogDan Vatra committed
173
        // persisten settings
hjk's avatar
hjk committed
174
175
176
177
178
        sdkLocation = FileName::fromString(reader.restoreValue(SDKLocationKey).toString());
        ndkLocation = FileName::fromString(reader.restoreValue(NDKLocationKey).toString());
        antLocation = FileName::fromString(reader.restoreValue(AntLocationKey).toString());
        openJDKLocation = FileName::fromString(reader.restoreValue(OpenJDKLocationKey).toString());
        keystoreLocation = FileName::fromString(reader.restoreValue(KeystoreLocationKey).toString());
179
        toolchainHost = reader.restoreValue(ToolchainHostKey).toString();
Daniel Teske's avatar
Daniel Teske committed
180
181
182
        QVariant v = reader.restoreValue(AutomaticKitCreationKey);
        if (v.isValid())
            automaticKitCreation = v.toBool();
183
        QString extraDirectory = reader.restoreValue(MakeExtraSearchDirectory).toString();
184
185
        makeExtraSearchDirectories.clear();
        if (!extraDirectory.isEmpty())
186
            makeExtraSearchDirectories << extraDirectory;
BogDan Vatra's avatar
BogDan Vatra committed
187
188
189
190
191
192
193
194
195
196
197
198
        // persistent settings
    }

}

AndroidConfig::AndroidConfig()
{
    partitionSize = 1024;
}

void AndroidConfig::save(QSettings &settings) const
{
199
    QFileInfo fileInfo(sdkSettingsFileName());
BogDan Vatra's avatar
BogDan Vatra committed
200
201
202
203
    if (fileInfo.exists())
        settings.setValue(changeTimeStamp, fileInfo.lastModified().toMSecsSinceEpoch() / 1000);

    // user settings
Tobias Hunger's avatar
Tobias Hunger committed
204
205
206
207
208
    settings.setValue(SDKLocationKey, sdkLocation.toString());
    settings.setValue(NDKLocationKey, ndkLocation.toString());
    settings.setValue(AntLocationKey, antLocation.toString());
    settings.setValue(OpenJDKLocationKey, openJDKLocation.toString());
    settings.setValue(KeystoreLocationKey, keystoreLocation.toString());
BogDan Vatra's avatar
BogDan Vatra committed
209
    settings.setValue(PartitionSizeKey, partitionSize);
Daniel Teske's avatar
Daniel Teske committed
210
    settings.setValue(AutomaticKitCreationKey, automaticKitCreation);
211
    settings.setValue(ToolchainHostKey, toolchainHost);
212
213
214
    settings.setValue(MakeExtraSearchDirectory,
                      makeExtraSearchDirectories.isEmpty() ? QString()
                                                           : makeExtraSearchDirectories.at(0));
BogDan Vatra's avatar
BogDan Vatra committed
215
216
217
218
219
}

void AndroidConfigurations::setConfig(const AndroidConfig &devConfigs)
{
    m_config = devConfigs;
220
221
222
223

    if (m_config.toolchainHost.isEmpty())
        detectToolchainHost();

BogDan Vatra's avatar
BogDan Vatra committed
224
    save();
225
    updateAvailableNdkPlatforms();
226
    updateAvailableSdkPlatforms();
Daniel Teske's avatar
Daniel Teske committed
227
    updateAutomaticKitList();
228
    updateAndroidDevice();
BogDan Vatra's avatar
BogDan Vatra committed
229
230
231
    emit updated();
}

232
void AndroidConfigurations::updateAvailableNdkPlatforms()
BogDan Vatra's avatar
BogDan Vatra committed
233
{
234
    m_availableNdkPlatforms.clear();
hjk's avatar
hjk committed
235
    FileName path = m_config.ndkLocation;
BogDan Vatra's avatar
BogDan Vatra committed
236
    QDirIterator it(path.appendPath(QLatin1String("platforms")).toString(), QStringList() << QLatin1String("android-*"), QDir::Dirs);
BogDan Vatra's avatar
BogDan Vatra committed
237
238
    while (it.hasNext()) {
        const QString &fileName = it.next();
239
        m_availableNdkPlatforms.push_back(fileName.mid(fileName.lastIndexOf(QLatin1Char('-')) + 1).toInt());
BogDan Vatra's avatar
BogDan Vatra committed
240
    }
241
    qSort(m_availableNdkPlatforms.begin(), m_availableNdkPlatforms.end(), qGreater<int>());
BogDan Vatra's avatar
BogDan Vatra committed
242
243
}

244
void AndroidConfigurations::updateAvailableSdkPlatforms()
BogDan Vatra's avatar
BogDan Vatra committed
245
{
246
247
    m_availableSdkPlatforms.clear();

BogDan Vatra's avatar
BogDan Vatra committed
248
    QProcess proc;
249
    proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
Tobias Hunger's avatar
Tobias Hunger committed
250
    proc.start(androidToolPath().toString(), QStringList() << QLatin1String("list") << QLatin1String("target")); // list avaialbe AVDs
BogDan Vatra's avatar
BogDan Vatra committed
251
252
    if (!proc.waitForFinished(-1)) {
        proc.terminate();
253
        return;
BogDan Vatra's avatar
BogDan Vatra committed
254
    }
255
    while (proc.canReadLine()) {
256
        const QString line = QString::fromLocal8Bit(proc.readLine().trimmed());
BogDan Vatra's avatar
BogDan Vatra committed
257
258
259
        int index = line.indexOf(QLatin1String("\"android-"));
        if (index == -1)
            continue;
260
261
262
263
        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
264
    }
265
266
267
268
269
270
271
272
273
274
275
276
}

QStringList AndroidConfigurations::sdkTargets(int minApiLevel) const
{
    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
277
278
}

hjk's avatar
hjk committed
279
FileName AndroidConfigurations::adbToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
280
{
hjk's avatar
hjk committed
281
    FileName path = m_config.sdkLocation;
282
    return path.appendPath(QLatin1String("platform-tools/adb" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
283
284
}

285
286
287
288
289
290
291
292
Utils::Environment AndroidConfigurations::androidToolEnvironment() const
{
    Utils::Environment env = Utils::Environment::systemEnvironment();
    if (!m_config.openJDKLocation.isEmpty())
        env.set(QLatin1String("JAVA_HOME"), m_config.openJDKLocation.toUserOutput());
    return env;
}

hjk's avatar
hjk committed
293
FileName AndroidConfigurations::androidToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
294
{
295
296
297
298
    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.
        FileName path = m_config.sdkLocation;
299
        path.appendPath(QLatin1String("tools/android" QTC_HOST_EXE_SUFFIX));
300
301
302
        if (path.toFileInfo().exists())
            return path;
        path = m_config.sdkLocation;
303
        return path.appendPath(QLatin1String("tools/android" ANDROID_BAT_SUFFIX));
304
305
306
307
    } else {
        FileName path = m_config.sdkLocation;
        return path.appendPath(QLatin1String("tools/android"));
    }
BogDan Vatra's avatar
BogDan Vatra committed
308
309
}

hjk's avatar
hjk committed
310
FileName AndroidConfigurations::antToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
311
{
Tobias Hunger's avatar
Tobias Hunger committed
312
    if (!m_config.antLocation.isEmpty())
BogDan Vatra's avatar
BogDan Vatra committed
313
314
        return m_config.antLocation;
    else
hjk's avatar
hjk committed
315
        return FileName::fromString(QLatin1String("ant"));
BogDan Vatra's avatar
BogDan Vatra committed
316
317
}

hjk's avatar
hjk committed
318
FileName AndroidConfigurations::emulatorToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
319
{
hjk's avatar
hjk committed
320
    FileName path = m_config.sdkLocation;
321
    return path.appendPath(QLatin1String("tools/emulator" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
322
323
}

324
FileName AndroidConfigurations::toolPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
325
{
hjk's avatar
hjk committed
326
    FileName path = m_config.ndkLocation;
Tobias Hunger's avatar
Tobias Hunger committed
327
    return path.appendPath(QString::fromLatin1("toolchains/%1-%2/prebuilt/%3/bin/%4")
BogDan Vatra's avatar
BogDan Vatra committed
328
            .arg(toolchainPrefix(architecture))
329
            .arg(ndkToolChainVersion)
330
            .arg(m_config.toolchainHost)
Tobias Hunger's avatar
Tobias Hunger committed
331
            .arg(toolsPrefix(architecture)));
BogDan Vatra's avatar
BogDan Vatra committed
332
333
}

334
FileName AndroidConfigurations::stripPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
335
{
336
    return toolPath(architecture, ndkToolChainVersion).append(QLatin1String("-strip" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
337
338
}

339
FileName AndroidConfigurations::readelfPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
340
{
341
    return toolPath(architecture, ndkToolChainVersion).append(QLatin1String("-readelf" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
342
343
}

344
345
346
347
348
FileName AndroidConfigurations::gccPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
{
    return toolPath(architecture, ndkToolChainVersion).append(QLatin1String("-gcc" QTC_HOST_EXE_SUFFIX));
}

349
FileName AndroidConfigurations::gdbPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
350
{
351
    return toolPath(architecture, ndkToolChainVersion).append(QLatin1String("-gdb" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
352
353
}

hjk's avatar
hjk committed
354
FileName AndroidConfigurations::openJDKPath() const
BogDan Vatra's avatar
BogDan Vatra committed
355
356
357
358
{
    return m_config.openJDKLocation;
}

359
360
361
362
void AndroidConfigurations::detectToolchainHost()
{
    QStringList hostPatterns;
    switch (HostOsInfo::hostOs()) {
363
    case OsTypeLinux:
364
365
        hostPatterns << QLatin1String("linux*");
        break;
366
    case OsTypeWindows:
367
368
        hostPatterns << QLatin1String("windows*");
        break;
369
    case OsTypeMac:
370
371
372
373
374
375
376
377
378
379
380
381
382
        hostPatterns << QLatin1String("darwin*");
        break;
    default: /* unknown host */ return;
    }

    FileName path = m_config.ndkLocation;
    QDirIterator it(path.appendPath(QLatin1String("prebuilt")).toString(), hostPatterns, QDir::Dirs);
    if (it.hasNext()) {
        it.next();
        m_config.toolchainHost = it.fileName();
    }
}

hjk's avatar
hjk committed
383
FileName AndroidConfigurations::openJDKBinPath() const
BogDan Vatra's avatar
BogDan Vatra committed
384
{
hjk's avatar
hjk committed
385
    FileName path = m_config.openJDKLocation;
Tobias Hunger's avatar
Tobias Hunger committed
386
387
388
    if (!path.isEmpty())
        return path.appendPath(QLatin1String("bin"));
    return path;
BogDan Vatra's avatar
BogDan Vatra committed
389
390
}

hjk's avatar
hjk committed
391
FileName AndroidConfigurations::keytoolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
392
{
Tobias Hunger's avatar
Tobias Hunger committed
393
    return openJDKBinPath().appendPath(keytoolName);
BogDan Vatra's avatar
BogDan Vatra committed
394
395
}

hjk's avatar
hjk committed
396
FileName AndroidConfigurations::jarsignerPath() const
BogDan Vatra's avatar
BogDan Vatra committed
397
{
Tobias Hunger's avatar
Tobias Hunger committed
398
    return openJDKBinPath().appendPath(jarsignerName);
BogDan Vatra's avatar
BogDan Vatra committed
399
400
}

hjk's avatar
hjk committed
401
FileName AndroidConfigurations::zipalignPath() const
402
{
403
    FileName path = m_config.sdkLocation;
404
    return path.appendPath(QLatin1String("tools/zipalign" QTC_HOST_EXE_SUFFIX));
405
406
}

407
AndroidDeviceInfo AndroidConfigurations::showDeviceDialog(ProjectExplorer::Project *project, int apiLevel, const QString &abi)
BogDan Vatra's avatar
BogDan Vatra committed
408
{
409
410
411
412
413
414
415
    QString serialNumber = defaultDevice(project, abi);
    if (!serialNumber.isEmpty()) {
        // search for that device
        foreach (const AndroidDeviceInfo &info, AndroidConfigurations::instance().connectedDevices())
            if (info.serialNumber == serialNumber
                    && info.sdk >= apiLevel)
                return info;
BogDan Vatra's avatar
BogDan Vatra committed
416

417
418
419
420
421
422
        foreach (const AndroidDeviceInfo &info, AndroidConfigurations::instance().androidVirtualDevices())
            if (info.serialNumber == serialNumber
                    && info.sdk >= apiLevel)
                return info;
    }

423
    AndroidDeviceDialog dialog(apiLevel, abi, Core::ICore::mainWindow());
424
425
426
427
428
    if (dialog.exec() == QDialog::Accepted) {
        AndroidDeviceInfo info = dialog.device();
        if (dialog.saveDeviceSelection()) {
            if (!info.serialNumber.isEmpty())
                AndroidConfigurations::instance().setDefaultDevice(project, abi, info.serialNumber);
BogDan Vatra's avatar
BogDan Vatra committed
429
        }
430
        return info;
Tobias Hunger's avatar
Tobias Hunger committed
431
    }
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
    return AndroidDeviceInfo();
}

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

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

QString AndroidConfigurations::defaultDevice(Project *project, const QString &abi) const
{
    if (!m_defaultDeviceForAbi.contains(project))
        return QString();
    const QMap<QString, QString> &map = m_defaultDeviceForAbi.value(project);
    if (!map.contains(abi))
        return QString();
    return map.value(abi);
BogDan Vatra's avatar
BogDan Vatra committed
454
455
}

456
QVector<AndroidDeviceInfo> AndroidConfigurations::connectedDevices(QString *error) const
BogDan Vatra's avatar
BogDan Vatra committed
457
{
458
    QVector<AndroidDeviceInfo> devices;
BogDan Vatra's avatar
BogDan Vatra committed
459
    QProcess adbProc;
Tobias Hunger's avatar
Tobias Hunger committed
460
    adbProc.start(adbToolPath().toString(), QStringList() << QLatin1String("devices"));
BogDan Vatra's avatar
BogDan Vatra committed
461
    if (!adbProc.waitForFinished(-1)) {
462
        adbProc.kill();
463
464
        if (error)
            *error = tr("Could not run: %1").arg(adbToolPath().toString() + QLatin1String(" devices"));
BogDan Vatra's avatar
BogDan Vatra committed
465
466
467
        return devices;
    }
    QList<QByteArray> adbDevs = adbProc.readAll().trimmed().split('\n');
BogDan Vatra's avatar
BogDan Vatra committed
468
469
470
471
472
473
    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
474
475
476

    // workaround for '????????????' serial numbers:
    // can use "adb -d" when only one usb device attached
BogDan Vatra's avatar
BogDan Vatra committed
477
    foreach (const QByteArray &device, adbDevs) {
478
479
        const QString serialNo = QString::fromLatin1(device.left(device.indexOf('\t')).trimmed());
        const QString deviceType = QString::fromLatin1(device.mid(device.indexOf('\t'))).trimmed();
480
481
        if (isBootToQt(serialNo))
            continue;
482
        AndroidDeviceInfo dev;
483
        dev.serialNumber = serialNo;
484
        dev.type = serialNo.startsWith(QLatin1String("emulator")) ? AndroidDeviceInfo::Emulator : AndroidDeviceInfo::Hardware;
BogDan Vatra's avatar
BogDan Vatra committed
485
        dev.sdk = getSDKVersion(dev.serialNumber);
486
        dev.cpuAbi = getAbis(dev.serialNumber);
487
        dev.unauthorized = (deviceType == QLatin1String("unauthorized"));
BogDan Vatra's avatar
BogDan Vatra committed
488
489
        devices.push_back(dev);
    }
490

BogDan Vatra's avatar
BogDan Vatra committed
491
    qSort(devices.begin(), devices.end(), androidDevicesLessThan);
492
493
    if (devices.isEmpty() && error)
        *error = tr("No devices found in output of: %1").arg(adbToolPath().toString() + QLatin1String(" devices"));
BogDan Vatra's avatar
BogDan Vatra committed
494
495
496
    return devices;
}

497
QString AndroidConfigurations::createAVD(QWidget *parent, int minApiLevel, QString targetArch) const
BogDan Vatra's avatar
BogDan Vatra committed
498
{
499
    QDialog d(parent);
BogDan Vatra's avatar
BogDan Vatra committed
500
501
    Ui::AddNewAVDDialog avdDialog;
    avdDialog.setupUi(&d);
502
503
    // NOTE: adb list targets does actually include information on which abis are supported per apilevel
    // we aren't using that information here
504
    avdDialog.targetComboBox->addItems(sdkTargets(minApiLevel));
505
506
507
508
509
510
511
512
513
514

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

515
    if (!avdDialog.targetComboBox->count()) {
Friedemann Kleint's avatar
Friedemann Kleint committed
516
        QMessageBox::critical(0, tr("Error Creating AVD"),
517
                              tr("Cannot create a new AVD. No sufficiently recent Android SDK available.\n"
Friedemann Kleint's avatar
Friedemann Kleint committed
518
                                 "Please install an SDK of at least API version %1.").
Friedemann Kleint's avatar
Friedemann Kleint committed
519
                              arg(minApiLevel));
520
        return QString();
BogDan Vatra's avatar
BogDan Vatra committed
521
522
523
524
525
526
    }

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

531
QString AndroidConfigurations::createAVD(const QString &target, const QString &name, const QString &abi, int sdcardSize ) const
BogDan Vatra's avatar
BogDan Vatra committed
532
533
{
    QProcess proc;
534
    proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
Tobias Hunger's avatar
Tobias Hunger committed
535
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
536
               QStringList() << QLatin1String("create") << QLatin1String("avd")
537
               << QLatin1String("-t") << target
BogDan Vatra's avatar
BogDan Vatra committed
538
               << QLatin1String("-n") << name
539
               << QLatin1String("-b") << abi
BogDan Vatra's avatar
BogDan Vatra committed
540
541
               << QLatin1String("-c") << QString::fromLatin1("%1M").arg(sdcardSize));
    if (!proc.waitForStarted())
542
        return QString();
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563

    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
564
    }
565
566
567

    proc.waitForFinished();

568
569
570
    if (proc.exitCode()) // error!
        return QString();
    return name;
BogDan Vatra's avatar
BogDan Vatra committed
571
572
573
574
575
}

bool AndroidConfigurations::removeAVD(const QString &name) const
{
    QProcess proc;
576
    proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
Tobias Hunger's avatar
Tobias Hunger committed
577
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
578
579
580
581
582
583
584
585
586
               QStringList() << QLatin1String("delete") << QLatin1String("avd")
               << QLatin1String("-n") << name);
    if (!proc.waitForFinished(-1)) {
        proc.terminate();
        return false;
    }
    return !proc.exitCode();
}

587
QVector<AndroidDeviceInfo> AndroidConfigurations::androidVirtualDevices() const
BogDan Vatra's avatar
BogDan Vatra committed
588
{
589
    QVector<AndroidDeviceInfo> devices;
BogDan Vatra's avatar
BogDan Vatra committed
590
    QProcess proc;
591
    proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
Tobias Hunger's avatar
Tobias Hunger committed
592
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
593
594
595
596
597
598
               QStringList() << QLatin1String("list") << QLatin1String("avd")); // list available AVDs
    if (!proc.waitForFinished(-1)) {
        proc.terminate();
        return devices;
    }
    QList<QByteArray> avds = proc.readAll().trimmed().split('\n');
BogDan Vatra's avatar
BogDan Vatra committed
599
600
601
602
603
604
605
    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

606
    AndroidDeviceInfo dev;
BogDan Vatra's avatar
BogDan Vatra committed
607
608
609
610
611
612
613
614
615
616
617
618
619
620
    for (int i = 0; i < avds.size(); i++) {
        QString line = QLatin1String(avds[i]);
        if (!line.contains(QLatin1String("Name:")))
            continue;

        dev.serialNumber = line.mid(line.indexOf(QLatin1Char(':')) + 2).trimmed();
        ++i;
        for (; i < avds.size(); ++i) {
            line = QLatin1String(avds[i]);
            if (line.contains(QLatin1String("---------")))
                break;
            if (line.contains(QLatin1String("Target:")))
                dev.sdk = line.mid(line.lastIndexOf(QLatin1Char(' '))).remove(QLatin1Char(')')).toInt();
            if (line.contains(QLatin1String("ABI:")))
621
                dev.cpuAbi = QStringList() << line.mid(line.lastIndexOf(QLatin1Char(' '))).trimmed();
BogDan Vatra's avatar
BogDan Vatra committed
622
        }
623
624
625
        // armeabi-v7a devices can also run armeabi code
        if (dev.cpuAbi == QStringList(QLatin1String("armeabi-v7a")))
            dev.cpuAbi << QLatin1String("armeabi");
626
        dev.unauthorized = false;
627
        dev.type = AndroidDeviceInfo::Emulator;
BogDan Vatra's avatar
BogDan Vatra committed
628
629
630
631
632
633
634
        devices.push_back(dev);
    }
    qSort(devices.begin(), devices.end(), androidDevicesLessThan);

    return devices;
}

635
636
QString AndroidConfigurations::startAVD(const QString &name, int apiLevel, QString cpuAbi) const
{
637
    if (findAvd(apiLevel, cpuAbi) || startAVDAsync(name))
638
639
640
        return waitForAvd(apiLevel, cpuAbi);
    return QString();
}
BogDan Vatra's avatar
BogDan Vatra committed
641

642
643
644
645
646
bool AndroidConfigurations::startAVDAsync(const QString &avdName) const
{
    QProcess *avdProcess = new QProcess();
    connect(this, SIGNAL(destroyed()), avdProcess, SLOT(deleteLater()));
    connect(avdProcess, SIGNAL(finished(int)), avdProcess, SLOT(deleteLater()));
BogDan Vatra's avatar
BogDan Vatra committed
647
648

    // start the emulator
Orgad Shaneh's avatar
Orgad Shaneh committed
649
    avdProcess->start(emulatorToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
650
651
                        QStringList() << QLatin1String("-partition-size") << QString::number(config().partitionSize)
                        << QLatin1String("-avd") << avdName);
Orgad Shaneh's avatar
Orgad Shaneh committed
652
653
    if (!avdProcess->waitForStarted(-1)) {
        delete avdProcess;
654
        return false;
BogDan Vatra's avatar
BogDan Vatra committed
655
    }
656
657
658
    return true;
}

659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
bool AndroidConfigurations::findAvd(int apiLevel, const QString &cpuAbi) const
{
    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;
        return true;
    }
    return false;
}

674
675
676
677
678
679
680
681
682
683
684
685
686
QString AndroidConfigurations::waitForAvd(int apiLevel, const QString &cpuAbi) const
{
    // we cannot use adb -e wait-for-device, since that doesn't work if a emulator is already running

    // 15 rounds of 8s sleeping, a minute for the avd to start
    QString serialNumber;
    for (int i = 0; i < 15; ++i) {
        QVector<AndroidDeviceInfo> devices = connectedDevices();
        foreach (AndroidDeviceInfo device, devices) {
            if (!device.serialNumber.startsWith(QLatin1String("emulator")))
                continue;
            if (!device.cpuAbi.contains(cpuAbi))
                continue;
687
            if (device.sdk != apiLevel)
688
689
690
691
692
693
694
                continue;
            serialNumber = device.serialNumber;
            // found a serial number, now wait until it's done booting...
            for (int i = 0; i < 15; ++i) {
                if (hasFinishedBooting(serialNumber))
                    return serialNumber;
                else
Orgad Shaneh's avatar
Orgad Shaneh committed
695
                    Utils::sleep(8000);
696
            }
Orgad Shaneh's avatar
Orgad Shaneh committed
697
698
            return QString();
        }
Orgad Shaneh's avatar
Orgad Shaneh committed
699
        Utils::sleep(8000);
BogDan Vatra's avatar
BogDan Vatra committed
700
701
702
703
    }
    return QString();
}

704
705
706
707
708
709
710
711
712
713
714
bool AndroidConfigurations::isBootToQt(const QString &device) const
{
    // 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);
    if (!adbProc.waitForFinished(-1)) {
        adbProc.kill();
715
        return false;
716
717
718
719
    }
    return adbProc.readAll().contains("Boot2Qt");
}

BogDan Vatra's avatar
BogDan Vatra committed
720
721
int AndroidConfigurations::getSDKVersion(const QString &device) const
{
722
723
724
725
    // 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
726
727

    QProcess adbProc;
728
    adbProc.start(adbToolPath().toString(), arguments);
BogDan Vatra's avatar
BogDan Vatra committed
729
    if (!adbProc.waitForFinished(-1)) {
730
        adbProc.kill();
BogDan Vatra's avatar
BogDan Vatra committed
731
732
733
734
735
        return -1;
    }
    return adbProc.readAll().trimmed().toInt();
}

736
737
738
739
740
741
742
//!
//! \brief AndroidConfigurations::getProductModel
//! \param device serial number
//! \return the produce model of the device or if that cannot be read the serial number
//!
QString AndroidConfigurations::getProductModel(const QString &device) const
{
743
744
    if (m_serialNumberToDeviceName.contains(device))
        return m_serialNumberToDeviceName.value(device);
745
746
747
748
749
750
751
752
753
754
755
756
757
758
    // 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);
    if (!adbProc.waitForFinished(-1)) {
        adbProc.kill();
        return device;
    }
    QString model = QString::fromLocal8Bit(adbProc.readAll().trimmed());
    if (model.isEmpty())
        return device;
759
760
    if (!device.startsWith(QLatin1String("????")))
        m_serialNumberToDeviceName.insert(device, model);
761
762
763
    return model;
}

764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
bool AndroidConfigurations::hasFinishedBooting(const QString &device) const
{
    QStringList arguments = AndroidDeviceInfo::adbSelector(device);
    arguments << QLatin1String("shell") << QLatin1String("getprop")
              << QLatin1String("init.svc.bootanim");

    QProcess adbProc;
    adbProc.start(adbToolPath().toString(), arguments);
    if (!adbProc.waitForFinished(-1)) {
        adbProc.kill();
        return false;
    }
    QString value = QString::fromLocal8Bit(adbProc.readAll().trimmed());
    if (value == QLatin1String("stopped"))
        return true;
    return false;
}

782
783
784
QStringList AndroidConfigurations::getAbis(const QString &device) const
{
    QStringList result;
785
    for (int i = 1; i < 6; ++i) {
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
        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);
        if (!adbProc.waitForFinished(-1)) {
            adbProc.kill();
            return result;
        }
        QString abi = QString::fromLocal8Bit(adbProc.readAll().trimmed());
        if (abi.isEmpty())
            break;
        result << abi;
    }
    return result;
}

807
QString AndroidConfigurations::highestAndroidSdk() const
808
{
809
    if (m_availableSdkPlatforms.isEmpty())
810
        return QString();
811
    return QLatin1String("android-") + QString::number(m_availableSdkPlatforms.first());
812
813
}

814
QString AndroidConfigurations::bestNdkPlatformMatch(const QString &targetAPI) const
BogDan Vatra's avatar
BogDan Vatra committed
815
816
{
    int target = targetAPI.mid(targetAPI.lastIndexOf(QLatin1Char('-')) + 1).toInt();
817
    foreach (int apiLevel, m_availableNdkPlatforms) {
BogDan Vatra's avatar
BogDan Vatra committed
818
819
820
821
822
823
        if (apiLevel <= target)
            return QString::fromLatin1("android-%1").arg(apiLevel);
    }
    return QLatin1String("android-8");
}

824
825
826
827
828
QStringList AndroidConfigurations::makeExtraSearchDirectories() const
{
    return m_config.makeExtraSearchDirectories;
}

829
static bool equalKits(Kit *a, Kit *b)
Daniel Teske's avatar
Daniel Teske committed
830
831
832
833
834
835
836
837
838
839
840
{
    return ToolChainKitInformation::toolChain(a) == ToolChainKitInformation::toolChain(b)
            && QtSupport::QtKitInformation::qtVersion(a) == QtSupport::QtKitInformation::qtVersion(b);
}

void AndroidConfigurations::updateAutomaticKitList()
{
    QList<AndroidToolChain *> toolchains;
    if (AndroidConfigurations::instance().config().automaticKitCreation) {
        // having a empty toolchains list will remove all autodetected kits for android
        // exactly what we want in that case
hjk's avatar
hjk committed
841
        foreach (ToolChain *tc, ToolChainManager::toolChains()) {
Daniel Teske's avatar
Daniel Teske committed
842
843
844
845
846
847
848
849
850
851
            if (!tc->isAutoDetected())
                continue;
            if (tc->type() != QLatin1String(Constants::ANDROID_TOOLCHAIN_TYPE))
                continue;
            toolchains << static_cast<AndroidToolChain *>(tc);
        }
    }

    QList<Kit *> existingKits;

852
853
    foreach (Kit *k, KitManager::kits()) {
        if (DeviceKitInformation::deviceId(k) != Core::Id(Constants::ANDROID_DEVICE_ID))
Daniel Teske's avatar
Daniel Teske committed
854
855
856
            continue;
        if (!k->isAutoDetected())
            continue;
Daniel Teske's avatar
Daniel Teske committed
857
858
        if (k->isSdkProvided())
            continue;
Daniel Teske's avatar
Daniel Teske committed
859

860
861
862
863
864
865
866
867
868
869
870
871
        // Update code for 3.0 beta, which shipped with a bug for the debugger settings
        ProjectExplorer::ToolChain *tc =ToolChainKitInformation::toolChain(k);
        if (tc && Debugger::DebuggerKitInformation::debuggerCommand(k) != tc->suggestedDebugger()) {
            Debugger::DebuggerItem debugger;
            debugger.setCommand(tc->suggestedDebugger());
            debugger.setEngineType(Debugger::GdbEngineType);
            debugger.setDisplayName(tr("Android Debugger for %1").arg(tc->displayName()));
            debugger.setAutoDetected(true);
            debugger.setAbi(tc->targetAbi());
            QVariant id = Debugger::DebuggerItemManager::registerDebugger(debugger);
            Debugger::DebuggerKitInformation::setDebugger(k, id);
        }
Daniel Teske's avatar
Daniel Teske committed
872
873
874
        existingKits << k;
    }

875
    QMap<Abi::Architecture, QList<QtSupport::BaseQtVersion *> > qtVersionsForArch;
hjk's avatar
hjk committed
876
    foreach (QtSupport::BaseQtVersion *qtVersion, QtSupport::QtVersionManager::versions()) {
Daniel Teske's avatar
Daniel Teske committed
877
878
        if (qtVersion->type() != QLatin1String(Constants::ANDROIDQT))
            continue;
879
        QList<Abi> qtAbis = qtVersion->qtAbis();
880
881
882
        if (qtAbis.empty())
            continue;
        qtVersionsForArch[qtAbis.first().architecture()].append(qtVersion);
Daniel Teske's avatar
Daniel Teske committed
883
884
    }

885
    DeviceManager *dm = DeviceManager::instance();
886
887
888
889
890
891
892
    IDevice::ConstPtr device = dm->find(Core::Id(Constants::ANDROID_DEVICE_ID));
    if (device.isNull()) {
        // no device, means no sdk path
        foreach (Kit *k, existingKits)
            KitManager::deregisterKit(k);
        return;
    }
Daniel Teske's avatar
Daniel Teske committed
893
894
895
896

    // register new kits
    QList<Kit *> newKits;
    foreach (AndroidToolChain *tc, toolchains) {
897
        if (tc->isSecondaryToolChain())
898
            continue;
Daniel Teske's avatar
Daniel Teske committed
899
900
901
902
        QList<QtSupport::BaseQtVersion *> qtVersions = qtVersionsForArch.value(tc->targetAbi().architecture());
        foreach (QtSupport::BaseQtVersion *qt, qtVersions) {
            Kit *newKit = new Kit;
            newKit->setAutoDetected(true);
903
            newKit->setIconPath(Utils::FileName::fromString(QLatin1String(Constants::ANDROID_SETTINGS_CATEGORY_ICON)));
Daniel Teske's avatar
Daniel Teske committed
904
905
906
907
            DeviceTypeKitInformation::setDeviceTypeId(newKit, Core::Id(Constants::ANDROID_DEVICE_TYPE));
            ToolChainKitInformation::setToolChain(newKit, tc);
            QtSupport::QtKitInformation::setQtVersion(newKit, qt);
            DeviceKitInformation::setDevice(newKit, device);
hjk's avatar
hjk committed
908
909
910
911
912
913
914

            Debugger::DebuggerItem debugger;
            debugger.setCommand(tc->suggestedDebugger());
            debugger.setEngineType(Debugger::GdbEngineType);
            debugger.setDisplayName(tr("Android Debugger for %1").arg(tc->displayName()));
            debugger.setAutoDetected(true);
            debugger.setAbi(tc->targetAbi());
915
916
            QVariant id = Debugger::DebuggerItemManager::registerDebugger(debugger);
            Debugger::DebuggerKitInformation::setDebugger(newKit, id);
hjk's avatar
hjk committed
917

Daniel Teske's avatar
Daniel Teske committed
918
            AndroidGdbServerKitInformation::setGdbSever(newKit, tc->suggestedGdbServer());
919
            newKit->makeSticky();
Daniel Teske's avatar
Daniel Teske committed
920
921
922
923
924
925
926
927
928
929
930
            newKits << newKit;
        }
    }

    for (int i = existingKits.count() - 1; i >= 0; --i) {
        Kit *existingKit = existingKits.at(i);
        for (int j = 0; j < newKits.count(); ++j) {
            Kit *newKit = newKits.at(j);
            if (equalKits(existingKit, newKit)) {
                // Kit is already registered, nothing to do
                newKits.removeAt(j);
931
                existingKits.at(i)->makeSticky();
Daniel Teske's avatar
Daniel Teske committed
932
933
934
935
936
937
938
                existingKits.removeAt(i);
                KitManager::deleteKit(newKit);
                j = newKits.count();
            }
        }
    }

939
940
    foreach (Kit *k, existingKits) {
        ProjectExplorer::ToolChain *tc = ToolChainKitInformation::toolChain(k);
941
942
943
        QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(k);
        if (tc && tc->type() == QLatin1String(Constants::ANDROID_TOOLCHAIN_TYPE)
                && qtVersion && qtVersion->type() == QLatin1String(Constants::ANDROIDQT)) {
944
945
946
947
948
949
            k->makeUnSticky();
            k->setAutoDetected(false);
        } else {
            KitManager::deregisterKit(k);
        }
    }
Daniel Teske's avatar
Daniel Teske committed
950

951
952
    foreach (Kit *kit, newKits) {
        AndroidToolChain *tc = static_cast<AndroidToolChain *>(ToolChainKitInformation::toolChain(kit));
953
        AndroidQtVersion *qt = static_cast<AndroidQtVersion *>(QtSupport::QtKitInformation::qtVersion(kit));
954
        kit->setDisplayName(tr("Android for %1 (GCC %2, Qt %3)")
955
                            .arg(qt->targetArch())
956
957
                            .arg(tc->ndkToolChainVersion())
                            .arg(qt->qtVersionString()));
958
        KitManager::registerKit(kit);
959
    }
Daniel Teske's avatar
Daniel Teske committed
960
961
}

962
963
964
965
966
967
968
969
970
971
972
/**
 * Workaround for '????????????' serial numbers
 * @return ("-d") for buggy devices, ("-s", <serial no>) for normal
 */
QStringList AndroidDeviceInfo::adbSelector(const QString &serialNumber)
{
    if (serialNumber.startsWith(QLatin1String("????")))
        return QStringList() << QLatin1String("-d");
    return QStringList() << QLatin1String("-s") << serialNumber;
}

BogDan Vatra's avatar
BogDan Vatra committed
973
974
975
976
977
978
979
980
981
AndroidConfigurations &AndroidConfigurations::instance(QObject *parent)
{
    if (m_instance == 0)
        m_instance = new AndroidConfigurations(parent);
    return *m_instance;
}

void AndroidConfigurations::save()
{
982
    QSettings *settings = Core::ICore::settings();
BogDan Vatra's avatar
BogDan Vatra committed
983
984
985
986
987
988
989
990
991
    settings->beginGroup(SettingsGroup);
    m_config.save(*settings);
    settings->endGroup();
}

AndroidConfigurations::AndroidConfigurations(QObject *parent)
    : QObject(parent)
{
    load();
992
    updateAvailableNdkPlatforms();
993
    updateAvailableSdkPlatforms();
994
995
996

    connect(ProjectExplorer::SessionManager::instance(), SIGNAL(projectRemoved(ProjectExplorer::Project*)),
            this, SLOT(clearDefaultDevices(ProjectExplorer::Project*)));
BogDan Vatra's avatar
BogDan Vatra committed
997
998
}

999
1000
Utils::FileName javaHomeForJavac(const QString &location)
{