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

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

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

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

#include <QStringListModel>
#include <QMessageBox>

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

namespace Android {
namespace Internal {

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

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

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

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

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

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

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

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

Daniel Teske's avatar
Daniel Teske committed
159
void AndroidConfig::load(const QSettings &settings)
BogDan Vatra's avatar
BogDan Vatra committed
160
161
{
    // user settings
Daniel Teske's avatar
Daniel Teske committed
162
163
164
165
166
167
168
169
    m_partitionSize = settings.value(PartitionSizeKey, 1024).toInt();
    m_sdkLocation = FileName::fromString(settings.value(SDKLocationKey).toString());
    m_ndkLocation = FileName::fromString(settings.value(NDKLocationKey).toString());
    m_antLocation = FileName::fromString(settings.value(AntLocationKey).toString());
    m_openJDKLocation = FileName::fromString(settings.value(OpenJDKLocationKey).toString());
    m_keystoreLocation = FileName::fromString(settings.value(KeystoreLocationKey).toString());
    m_toolchainHost = settings.value(ToolchainHostKey).toString();
    m_automaticKitCreation = settings.value(AutomaticKitCreationKey, true).toBool();
170
    QString extraDirectory = settings.value(MakeExtraSearchDirectory).toString();
Daniel Teske's avatar
Daniel Teske committed
171
    m_makeExtraSearchDirectories.clear();
172
    if (!extraDirectory.isEmpty())
Daniel Teske's avatar
Daniel Teske committed
173
        m_makeExtraSearchDirectories << extraDirectory;
BogDan Vatra's avatar
BogDan Vatra committed
174
175

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

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

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

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

    // user settings
Daniel Teske's avatar
Daniel Teske committed
212
213
214
215
216
217
218
219
    settings.setValue(SDKLocationKey, m_sdkLocation.toString());
    settings.setValue(NDKLocationKey, m_ndkLocation.toString());
    settings.setValue(AntLocationKey, m_antLocation.toString());
    settings.setValue(OpenJDKLocationKey, m_openJDKLocation.toString());
    settings.setValue(KeystoreLocationKey, m_keystoreLocation.toString());
    settings.setValue(PartitionSizeKey, m_partitionSize);
    settings.setValue(AutomaticKitCreationKey, m_automaticKitCreation);
    settings.setValue(ToolchainHostKey, m_toolchainHost);
220
    settings.setValue(MakeExtraSearchDirectory,
Daniel Teske's avatar
Daniel Teske committed
221
222
                      m_makeExtraSearchDirectories.isEmpty() ? QString()
                                                             : m_makeExtraSearchDirectories.at(0));
BogDan Vatra's avatar
BogDan Vatra committed
223
224
}

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

    // 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;
    }

    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
260
261
}

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

BogDan Vatra's avatar
BogDan Vatra committed
268
    QProcess proc;
269
    proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
Tobias Hunger's avatar
Tobias Hunger committed
270
    proc.start(androidToolPath().toString(), QStringList() << QLatin1String("list") << QLatin1String("target")); // list avaialbe AVDs
271
    if (!proc.waitForFinished(5000)) {
BogDan Vatra's avatar
BogDan Vatra committed
272
        proc.terminate();
273
        return;
BogDan Vatra's avatar
BogDan Vatra committed
274
    }
275
    while (proc.canReadLine()) {
276
        const QString line = QString::fromLocal8Bit(proc.readLine().trimmed());
BogDan Vatra's avatar
BogDan Vatra committed
277
278
279
        int index = line.indexOf(QLatin1String("\"android-"));
        if (index == -1)
            continue;
280
281
282
283
        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
284
    }
Daniel Teske's avatar
Daniel Teske committed
285
    m_availableSdkPlatformsUpToDate = true;
286
287
}

Daniel Teske's avatar
Daniel Teske committed
288
QStringList AndroidConfig::sdkTargets(int minApiLevel) const
289
{
Daniel Teske's avatar
Daniel Teske committed
290
    updateAvailableSdkPlatforms();
291
292
293
294
295
296
297
298
    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
299
300
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

517
518
    proc.waitForFinished();

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

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

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

557
    AndroidDeviceInfo dev;
BogDan Vatra's avatar
BogDan Vatra committed
558
559
560
561
562
563
564
565
566
567
568
569
570
571
    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:")))
572
                dev.cpuAbi = QStringList() << line.mid(line.lastIndexOf(QLatin1Char(' '))).trimmed();
BogDan Vatra's avatar
BogDan Vatra committed
573
        }
574
575
576
        // armeabi-v7a devices can also run armeabi code
        if (dev.cpuAbi == QStringList(QLatin1String("armeabi-v7a")))
            dev.cpuAbi << QLatin1String("armeabi");
577
        dev.unauthorized = false;
578
        dev.type = AndroidDeviceInfo::Emulator;
BogDan Vatra's avatar
BogDan Vatra committed
579
580
581
582
583
584
585
        devices.push_back(dev);
    }
    qSort(devices.begin(), devices.end(), androidDevicesLessThan);

    return devices;
}

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

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

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

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

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

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

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

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

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

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

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

    QProcess adbProc;
    adbProc.start(adbToolPath().toString(), arguments);
735
    if (!adbProc.waitForFinished(5000)) {
736
737
738
739
740
741
742
743
744
        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
745
QStringList AndroidConfig::getAbis(const QString &device) const
746
747
{
    QStringList result;
748
    for (int i = 1; i < 6; ++i) {
749
750
751
752
753
754
755
756
757
        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);
758
        if (!adbProc.waitForFinished(5000)) {
759
760
761
762
763
764
765
766
767
768
769
            adbProc.kill();
            return result;
        }
        QString abi = QString::fromLocal8Bit(adbProc.readAll().trimmed());
        if (abi.isEmpty())
            break;
        result << abi;
    }
    return result;
}

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

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

Daniel Teske's avatar
Daniel Teske committed
789
FileName AndroidConfig::sdkLocation() const
790
{
Daniel Teske's avatar
Daniel Teske committed
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
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
    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;
}

///////////////////////////////////
// AndroidConfigurations
///////////////////////////////////
void AndroidConfigurations::setConfig(const AndroidConfig &devConfigs)
{
    m_instance->m_config = devConfigs;

    m_instance->save();
    m_instance->updateAutomaticKitList();
    m_instance->updateAndroidDevice();
    emit m_instance->updated();
}

AndroidDeviceInfo AndroidConfigurations::showDeviceDialog(ProjectExplorer::Project *project, int apiLevel, const QString &abi)
{
    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;
    }

    AndroidDeviceDialog dialog(apiLevel, abi, Core::ICore::mainWindow());
    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);
933
934
}

935
static bool equalKits(Kit *a, Kit *b)
Daniel Teske's avatar
Daniel Teske committed
936
937
938
939
940
941
942
943
{
    return ToolChainKitInformation::toolChain(a) == ToolChainKitInformation::toolChain(b)
            && QtSupport::QtKitInformation::qtVersion(a) == QtSupport::QtKitInformation::qtVersion(b);
}

void AndroidConfigurations::updateAutomaticKitList()
{
    QList<AndroidToolChain *> toolchains;
Daniel Teske's avatar
Daniel Teske committed
944
    if (AndroidConfigurations::currentConfig().automaticKitCreation()) {
Daniel Teske's avatar
Daniel Teske committed
945
946
        // having a empty toolchains list will remove all autodetected kits for android
        // exactly what we want in that case
hjk's avatar
hjk committed
947
        foreach (ToolChain *tc, ToolChainManager::toolChains()) {
Daniel Teske's avatar
Daniel Teske committed
948
949
950
951
952
953
954
955
956
957
            if (!tc->isAutoDetected())
                continue;
            if (tc->type() != QLatin1String(Constants::ANDROID_TOOLCHAIN_TYPE))
                continue;
            toolchains << static_cast<AndroidToolChain *>(tc);
        }
    }

    QList<Kit *> existingKits;

958
959
    foreach (Kit *k, KitManager::kits()) {
        if (DeviceKitInformation::deviceId(k) != Core::Id(Constants::ANDROID_DEVICE_ID))
Daniel Teske's avatar
Daniel Teske committed
960
961
962
            continue;
        if (!k->isAutoDetected())
            continue;
Daniel Teske's avatar
Daniel Teske committed
963
964
        if (k->isSdkProvided())
            continue;
Daniel Teske's avatar
Daniel Teske committed
965

966
967
968
969
970
971
972
973
974
975
976
977
        // 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
978
979
980
        existingKits << k;
    }

981
    QMap<Abi::Architecture, QList<QtSupport::BaseQtVersion *> > qtVersionsForArch;
hjk's avatar
hjk committed
982
    foreach (QtSupport::BaseQtVersion *qtVersion, QtSupport::QtVersionManager::versions()) {
Daniel Teske's avatar
Daniel Teske committed
983
984
        if (qtVersion->type() != QLatin1String(Constants::ANDROIDQT))
            continue;
985
        QList<Abi> qtAbis = qtVersion->qtAbis();
986
987
988
        if (qtAbis.empty())
            continue;
        qtVersionsForArch[qtAbis.first().architecture()].append(qtVersion);
Daniel Teske's avatar
Daniel Teske committed
989
990
    }

991
    DeviceManager *dm = DeviceManager::instance();
992
993
994
995
996
997
998
    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
999
1000

    // register new kits