androidconfigurations.cpp 54.6 KB
Newer Older
BogDan Vatra's avatar
BogDan Vatra committed
1
2
/**************************************************************************
**
Eike Ziller's avatar
Eike Ziller committed
3
4
** Copyright (C) 2015 BogDan Vatra <bog_dan_ro@yahoo.com>
** Contact: http://www.qt.io/licensing
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
** 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
Eike Ziller's avatar
Eike Ziller committed
12
13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
BogDan Vatra's avatar
BogDan Vatra committed
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18
19
20
21
22
23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
**
Eike Ziller's avatar
Eike Ziller committed
25
26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
BogDan Vatra's avatar
BogDan Vatra committed
27
28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
BogDan Vatra's avatar
BogDan Vatra committed
30
31
32

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

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

#include <QDateTime>
#include <QSettings>
#include <QStringList>
#include <QProcess>
#include <QFileInfo>
#include <QDirIterator>
#include <QMetaObject>
66
#include <QApplication>
67
#include <QtConcurrentRun>
BogDan Vatra's avatar
BogDan Vatra committed
68
69
70

#include <QStringListModel>
#include <QMessageBox>
71
72
#include <QTcpSocket>
#include <QHostAddress>
BogDan Vatra's avatar
BogDan Vatra committed
73

hjk's avatar
hjk committed
74
75
#include <functional>

hjk's avatar
hjk committed
76
using namespace ProjectExplorer;
BogDan Vatra's avatar
BogDan Vatra committed
77
78
79
using namespace Utils;

namespace Android {
80
using namespace Internal;
BogDan Vatra's avatar
BogDan Vatra committed
81
82
83
84
85
86
87
88

namespace {
    const QLatin1String SettingsGroup("AndroidConfigurations");
    const QLatin1String SDKLocationKey("SDKLocation");
    const QLatin1String NDKLocationKey("NDKLocation");
    const QLatin1String AntLocationKey("AntLocation");
    const QLatin1String OpenJDKLocationKey("OpenJDKLocation");
    const QLatin1String KeystoreLocationKey("KeystoreLocation");
Daniel Teske's avatar
Daniel Teske committed
89
    const QLatin1String AutomaticKitCreationKey("AutomatiKitCreation");
BogDan Vatra's avatar
BogDan Vatra committed
90
    const QLatin1String UseGradleKey("UseGradle");
91
    const QLatin1String MakeExtraSearchDirectory("MakeExtraSearchDirectory");
BogDan Vatra's avatar
BogDan Vatra committed
92
    const QLatin1String PartitionSizeKey("PartitionSize");
93
    const QLatin1String ToolchainHostKey("ToolchainHost");
Daniel Teske's avatar
Daniel Teske committed
94

BogDan Vatra's avatar
BogDan Vatra committed
95
96
    const QLatin1String ArmToolchainPrefix("arm-linux-androideabi");
    const QLatin1String X86ToolchainPrefix("x86");
BogDan Vatra's avatar
BogDan Vatra committed
97
    const QLatin1String MipsToolchainPrefix("mipsel-linux-android");
Daniel Teske's avatar
Daniel Teske committed
98
99
100
    const QLatin1String AArch64ToolchainPrefix("aarch64-linux-android");
    const QLatin1String X86_64ToolchainPrefix("x86_64");

BogDan Vatra's avatar
BogDan Vatra committed
101
    const QLatin1String ArmToolsPrefix("arm-linux-androideabi");
BogDan Vatra's avatar
BogDan Vatra committed
102
    const QLatin1String X86ToolsPrefix("i686-linux-android");
BogDan Vatra's avatar
BogDan Vatra committed
103
    const QLatin1String MipsToolsPrefix("mipsel-linux-android");
Daniel Teske's avatar
Daniel Teske committed
104
105
106
107
108
109
110
111
112
    const QLatin1String AArch64ToolsPrefix("aarch64-linux-android");
    const QLatin1String X86_64ToolsPrefix("x86_64-linux-android");

    const QLatin1String ArmToolsDisplayName("arm");
    const QLatin1String X86ToolsDisplayName("i686");
    const QLatin1String MipsToolsDisplayName("mipsel");
    const QLatin1String AArch64ToolsDisplayName("aarch64");
    const QLatin1String X86_64ToolsDisplayName("x86_64");

BogDan Vatra's avatar
BogDan Vatra committed
113
114
115
116
    const QLatin1String Unknown("unknown");
    const QLatin1String keytoolName("keytool");
    const QLatin1String changeTimeStamp("ChangeTimeStamp");

117
    static QString sdkSettingsFileName()
BogDan Vatra's avatar
BogDan Vatra committed
118
    {
119
120
        return QFileInfo(Core::ICore::settings(QSettings::SystemScope)->fileName()).absolutePath()
                + QLatin1String("/qtcreator/android.xml");
BogDan Vatra's avatar
BogDan Vatra committed
121
122
    }

123
    bool androidDevicesLessThan(const AndroidDeviceInfo &dev1, const AndroidDeviceInfo &dev2)
BogDan Vatra's avatar
BogDan Vatra committed
124
    {
125
        if (dev1.serialNumber.contains(QLatin1String("????")) != dev2.serialNumber.contains(QLatin1String("????")))
Daniel Teske's avatar
Compile  
Daniel Teske committed
126
            return !dev1.serialNumber.contains(QLatin1String("????"));
127
128
        if (dev1.type != dev2.type)
            return dev1.type == AndroidDeviceInfo::Hardware;
129
130
        if (dev1.sdk != dev2.sdk)
            return dev1.sdk < dev2.sdk;
131
132
        if (dev1.avdname != dev2.avdname)
            return dev1.avdname < dev2.avdname;
133

134
        return dev1.serialNumber < dev2.serialNumber;
BogDan Vatra's avatar
BogDan Vatra committed
135
    }
136
137
138
139
140
141
142
143
144
145
146
147
148

    static QStringList cleanAndroidABIs(const QStringList &abis)
    {
        QStringList res;
        foreach (const QString &abi, abis) {
            int index = abi.lastIndexOf(QLatin1Char('/'));
            if (index == -1)
                res << abi;
            else
                res << abi.mid(index + 1);
        }
        return res;
    }
149
150
151
152

    static bool is32BitUserSpace()
    {
        // Do the exact same check as android's emulator is doing:
153
        if (HostOsInfo::isLinuxHost()) {
154
            if (QSysInfo::WordSize == 32 ) {
155
                Environment env = Environment::systemEnvironment();
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
                QString executable = env.searchInPath(QLatin1String("file")).toString();
                QString shell = env.value(QLatin1String("SHELL"));
                if (executable.isEmpty() || shell.isEmpty())
                    return true; // we can't detect, but creator is 32bit so assume 32bit

                QProcess proc;
                proc.setProcessChannelMode(QProcess::MergedChannels);
                proc.start(executable, QStringList() << shell);
                if (!proc.waitForFinished(2000)) {
                    proc.kill();
                    return true;
                }
                if (proc.readAll().contains("x86-64"))
                    return false;
                return true;
            }
        }
        return false;
    }

Daniel Teske's avatar
Daniel Teske committed
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
    // Some preview sdks use a non integer version
    int apiLevelFromAndroidList(const QString &string)
    {
        bool ok;
        int result = string.toInt(&ok);
        if (ok)
            return result;
        Utils::FileName sdkLocation = AndroidConfigurations::currentConfig().sdkLocation();
        sdkLocation.appendPath(QLatin1String("/platforms/android-") + string + QLatin1String("/source.properties"));
        result = QSettings(sdkLocation.toString(), QSettings::IniFormat).value(QLatin1String("AndroidVersion.ApiLevel")).toInt(&ok);
        if (ok)
            return result;
        if (string == QLatin1String("L"))
            return 21;
        if (string == QLatin1String("MNC"))
            return 22;
        return 23; // At least
    }
BogDan Vatra's avatar
BogDan Vatra committed
194
195
}

Daniel Teske's avatar
Daniel Teske committed
196
197
198
199
//////////////////////////////////
// AndroidConfig
//////////////////////////////////

Daniel Teske's avatar
Daniel Teske committed
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
Abi AndroidConfig::abiForToolChainPrefix(const QString &toolchainPrefix)
{
    Abi::Architecture arch = Abi::UnknownArchitecture;
    unsigned char wordWidth = 32;
    if (toolchainPrefix == ArmToolchainPrefix) {
        arch = Abi::ArmArchitecture;
    } else if (toolchainPrefix == X86ToolchainPrefix) {
        arch = Abi::X86Architecture;
    } else if (toolchainPrefix == MipsToolchainPrefix) {
        arch = Abi::MipsArchitecture;
    } else if (toolchainPrefix == AArch64ToolchainPrefix) {
        arch = Abi::ArmArchitecture;
        wordWidth = 64;
    } else if (toolchainPrefix == X86_64ToolchainPrefix) {
        arch = Abi::X86Architecture;
        wordWidth = 64;
    }

    Abi abi = ProjectExplorer::Abi(arch,
                                   ProjectExplorer::Abi::LinuxOS,
                                   ProjectExplorer::Abi::AndroidLinuxFlavor, ProjectExplorer::Abi::ElfFormat,
                                   wordWidth);
    return abi;
223
224
}

Daniel Teske's avatar
Daniel Teske committed
225
QLatin1String AndroidConfig::toolchainPrefix(const Abi &abi)
BogDan Vatra's avatar
BogDan Vatra committed
226
{
Daniel Teske's avatar
Daniel Teske committed
227
    switch (abi.architecture()) {
hjk's avatar
hjk committed
228
    case Abi::ArmArchitecture:
Daniel Teske's avatar
Daniel Teske committed
229
230
        if (abi.wordWidth() == 64)
            return AArch64ToolchainPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
231
        return ArmToolchainPrefix;
hjk's avatar
hjk committed
232
    case Abi::X86Architecture:
Daniel Teske's avatar
Daniel Teske committed
233
234
        if (abi.wordWidth() == 64)
            return X86_64ToolchainPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
235
        return X86ToolchainPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
236
237
    case Abi::MipsArchitecture:
        return MipsToolchainPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
238
239
240
241
242
    default:
        return Unknown;
    }
}

Daniel Teske's avatar
Daniel Teske committed
243
QLatin1String AndroidConfig::toolsPrefix(const Abi &abi)
BogDan Vatra's avatar
BogDan Vatra committed
244
{
Daniel Teske's avatar
Daniel Teske committed
245
    switch (abi.architecture()) {
hjk's avatar
hjk committed
246
    case Abi::ArmArchitecture:
Daniel Teske's avatar
Daniel Teske committed
247
248
        if (abi.wordWidth() == 64)
            return AArch64ToolsPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
249
        return ArmToolsPrefix;
hjk's avatar
hjk committed
250
    case Abi::X86Architecture:
Daniel Teske's avatar
Daniel Teske committed
251
252
        if (abi.wordWidth() == 64)
            return X86_64ToolsPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
253
        return X86ToolsPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
254
255
    case Abi::MipsArchitecture:
        return MipsToolsPrefix;
BogDan Vatra's avatar
BogDan Vatra committed
256
257
258
259
260
    default:
        return Unknown;
    }
}

Daniel Teske's avatar
Daniel Teske committed
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
QLatin1String AndroidConfig::displayName(const Abi &abi)
{
    switch (abi.architecture()) {
    case Abi::ArmArchitecture:
        if (abi.wordWidth() == 64)
            return AArch64ToolsDisplayName;
        return ArmToolsDisplayName;
    case Abi::X86Architecture:
        if (abi.wordWidth() == 64)
            return X86_64ToolsDisplayName;
        return X86ToolsDisplayName;
    case Abi::MipsArchitecture:
        return MipsToolsDisplayName;
    default:
        return Unknown;
    }
}

Daniel Teske's avatar
Daniel Teske committed
279
void AndroidConfig::load(const QSettings &settings)
BogDan Vatra's avatar
BogDan Vatra committed
280
281
{
    // user settings
Daniel Teske's avatar
Daniel Teske committed
282
283
284
285
    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());
286
    m_useGradle = settings.value(UseGradleKey, false).toBool();
Daniel Teske's avatar
Daniel Teske committed
287
288
289
290
    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();
291
    QString extraDirectory = settings.value(MakeExtraSearchDirectory).toString();
Daniel Teske's avatar
Daniel Teske committed
292
    m_makeExtraSearchDirectories.clear();
293
    if (!extraDirectory.isEmpty())
Daniel Teske's avatar
Daniel Teske committed
294
        m_makeExtraSearchDirectories << extraDirectory;
BogDan Vatra's avatar
BogDan Vatra committed
295
296

    PersistentSettingsReader reader;
297
298
    if (reader.load(FileName::fromString(sdkSettingsFileName()))
            && settings.value(changeTimeStamp).toInt() != QFileInfo(sdkSettingsFileName()).lastModified().toMSecsSinceEpoch() / 1000) {
BogDan Vatra's avatar
BogDan Vatra committed
299
        // persisten settings
300
301
302
303
304
305
306
        m_sdkLocation = FileName::fromString(reader.restoreValue(SDKLocationKey, m_sdkLocation.toString()).toString());
        m_ndkLocation = FileName::fromString(reader.restoreValue(NDKLocationKey, m_ndkLocation.toString()).toString());
        m_antLocation = FileName::fromString(reader.restoreValue(AntLocationKey, m_antLocation.toString()).toString());
        m_openJDKLocation = FileName::fromString(reader.restoreValue(OpenJDKLocationKey, m_openJDKLocation.toString()).toString());
        m_keystoreLocation = FileName::fromString(reader.restoreValue(KeystoreLocationKey, m_keystoreLocation.toString()).toString());
        m_toolchainHost = reader.restoreValue(ToolchainHostKey, m_toolchainHost).toString();
        m_automaticKitCreation = reader.restoreValue(AutomaticKitCreationKey, m_automaticKitCreation).toBool();
307
        QString extraDirectory = reader.restoreValue(MakeExtraSearchDirectory).toString();
Daniel Teske's avatar
Daniel Teske committed
308
        m_makeExtraSearchDirectories.clear();
309
        if (!extraDirectory.isEmpty())
Daniel Teske's avatar
Daniel Teske committed
310
            m_makeExtraSearchDirectories << extraDirectory;
BogDan Vatra's avatar
BogDan Vatra committed
311
312
        // persistent settings
    }
Daniel Teske's avatar
Daniel Teske committed
313
314
    m_availableSdkPlatformsUpToDate = false;
    m_NdkInformationUpToDate = false;
BogDan Vatra's avatar
BogDan Vatra committed
315
316
317
}

AndroidConfig::AndroidConfig()
318
    : m_useGradle(false),
BogDan Vatra's avatar
BogDan Vatra committed
319
      m_availableSdkPlatformsUpToDate(false),
Daniel Teske's avatar
Daniel Teske committed
320
      m_NdkInformationUpToDate(false)
BogDan Vatra's avatar
BogDan Vatra committed
321
{
Daniel Teske's avatar
Daniel Teske committed
322

BogDan Vatra's avatar
BogDan Vatra committed
323
324
325
326
}

void AndroidConfig::save(QSettings &settings) const
{
327
    QFileInfo fileInfo(sdkSettingsFileName());
BogDan Vatra's avatar
BogDan Vatra committed
328
329
330
331
    if (fileInfo.exists())
        settings.setValue(changeTimeStamp, fileInfo.lastModified().toMSecsSinceEpoch() / 1000);

    // user settings
Daniel Teske's avatar
Daniel Teske committed
332
333
334
    settings.setValue(SDKLocationKey, m_sdkLocation.toString());
    settings.setValue(NDKLocationKey, m_ndkLocation.toString());
    settings.setValue(AntLocationKey, m_antLocation.toString());
BogDan Vatra's avatar
BogDan Vatra committed
335
    settings.setValue(UseGradleKey, m_useGradle);
Daniel Teske's avatar
Daniel Teske committed
336
337
338
339
340
    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);
341
    settings.setValue(MakeExtraSearchDirectory,
Daniel Teske's avatar
Daniel Teske committed
342
343
                      m_makeExtraSearchDirectories.isEmpty() ? QString()
                                                             : m_makeExtraSearchDirectories.at(0));
BogDan Vatra's avatar
BogDan Vatra committed
344
345
}

Daniel Teske's avatar
Daniel Teske committed
346
void AndroidConfig::updateNdkInformation() const
BogDan Vatra's avatar
BogDan Vatra committed
347
{
Daniel Teske's avatar
Daniel Teske committed
348
349
    if (m_NdkInformationUpToDate)
        return;
350
    m_availableNdkPlatforms.clear();
Daniel Teske's avatar
Daniel Teske committed
351
    FileName path = ndkLocation();
BogDan Vatra's avatar
BogDan Vatra committed
352
    QDirIterator it(path.appendPath(QLatin1String("platforms")).toString(), QStringList() << QLatin1String("android-*"), QDir::Dirs);
BogDan Vatra's avatar
BogDan Vatra committed
353
354
    while (it.hasNext()) {
        const QString &fileName = it.next();
355
        m_availableNdkPlatforms.push_back(fileName.mid(fileName.lastIndexOf(QLatin1Char('-')) + 1).toInt());
BogDan Vatra's avatar
BogDan Vatra committed
356
    }
357
    Utils::sort(m_availableNdkPlatforms, std::greater<int>());
Daniel Teske's avatar
Daniel Teske committed
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373

    // detect toolchain host
    QStringList hostPatterns;
    switch (HostOsInfo::hostOs()) {
    case OsTypeLinux:
        hostPatterns << QLatin1String("linux*");
        break;
    case OsTypeWindows:
        hostPatterns << QLatin1String("windows*");
        break;
    case OsTypeMac:
        hostPatterns << QLatin1String("darwin*");
        break;
    default: /* unknown host */ return;
    }

Daniel Teske's avatar
Daniel Teske committed
374
    path = ndkLocation();
Daniel Teske's avatar
Daniel Teske committed
375
376
377
378
379
380
381
    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
382
383
}

384
385
386
387
388
389
390
391
392
bool AndroidConfig::sortSdkPlatformByApiLevel(const SdkPlatform &a, const SdkPlatform &b)
{
    if (a.apiLevel != b.apiLevel)
        return a.apiLevel > b.apiLevel;
    if (a.name != b.name)
        return a.name < b.name;
    return false;
}

Daniel Teske's avatar
Daniel Teske committed
393
void AndroidConfig::updateAvailableSdkPlatforms() const
BogDan Vatra's avatar
BogDan Vatra committed
394
{
Daniel Teske's avatar
Daniel Teske committed
395
396
    if (m_availableSdkPlatformsUpToDate)
        return;
397
398
    m_availableSdkPlatforms.clear();

BogDan Vatra's avatar
BogDan Vatra committed
399
    QProcess proc;
400
    proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
Tobias Hunger's avatar
Tobias Hunger committed
401
    proc.start(androidToolPath().toString(), QStringList() << QLatin1String("list") << QLatin1String("target")); // list avaialbe AVDs
402
    if (!proc.waitForFinished(10000)) {
BogDan Vatra's avatar
BogDan Vatra committed
403
        proc.terminate();
404
        return;
BogDan Vatra's avatar
BogDan Vatra committed
405
    }
406
407

    SdkPlatform platform;
408
    while (proc.canReadLine()) {
409
        const QString line = QString::fromLocal8Bit(proc.readLine().trimmed());
410
411
412
413
414
        if (line.startsWith(QLatin1String("id:")) && line.contains(QLatin1String("android-"))) {
            int index = line.indexOf(QLatin1String("\"android-"));
            if (index == -1)
                continue;
            QString androidTarget = line.mid(index + 1, line.length() - index - 2);
Daniel Teske's avatar
Daniel Teske committed
415
416
            const QString tmp = androidTarget.mid(androidTarget.lastIndexOf(QLatin1Char('-')) + 1);
            platform.apiLevel = apiLevelFromAndroidList(tmp);
417
418
        } else if (line.startsWith(QLatin1String("Name:"))) {
            platform.name = line.mid(6);
419
420
        } else if (line.startsWith(QLatin1String("Tag/ABIs :"))) {
            platform.abis = cleanAndroidABIs(line.mid(10).trimmed().split(QLatin1String(", ")));
421
        } else if (line.startsWith(QLatin1String("ABIs"))) {
422
            platform.abis = cleanAndroidABIs(line.mid(6).trimmed().split(QLatin1String(", ")));
423
424
425
426
427
428
429
430
        } else if (line.startsWith(QLatin1String("---")) || line.startsWith(QLatin1String("==="))) {
            if (platform.apiLevel == -1)
                continue;
            auto it = qLowerBound(m_availableSdkPlatforms.begin(), m_availableSdkPlatforms.end(),
                                  platform, sortSdkPlatformByApiLevel);
            m_availableSdkPlatforms.insert(it, platform);
            platform = SdkPlatform();
        }
BogDan Vatra's avatar
BogDan Vatra committed
431
    }
432
433
434
435
436
437
438

    if (platform.apiLevel != -1) {
        auto it = qLowerBound(m_availableSdkPlatforms.begin(), m_availableSdkPlatforms.end(),
                              platform, sortSdkPlatformByApiLevel);
        m_availableSdkPlatforms.insert(it, platform);
    }

Daniel Teske's avatar
Daniel Teske committed
439
    m_availableSdkPlatformsUpToDate = true;
440
441
}

442
443
QStringList AndroidConfig::apiLevelNamesFor(const QList<SdkPlatform> &platforms)
{
444
    return Utils::transform(platforms, AndroidConfig::apiLevelNameFor);
445
446
}

447
448
449
450
451
QString AndroidConfig::apiLevelNameFor(const SdkPlatform &platform)
{
    return QLatin1String("android-") + QString::number(platform.apiLevel);
}

452
QList<SdkPlatform> AndroidConfig::sdkTargets(int minApiLevel) const
453
{
Daniel Teske's avatar
Daniel Teske committed
454
    updateAvailableSdkPlatforms();
455
    QList<SdkPlatform> result;
456
    for (int i = 0; i < m_availableSdkPlatforms.size(); ++i) {
457
458
        if (m_availableSdkPlatforms.at(i).apiLevel >= minApiLevel)
            result << m_availableSdkPlatforms.at(i);
459
460
461
462
        else
            break;
    }
    return result;
BogDan Vatra's avatar
BogDan Vatra committed
463
464
}

Daniel Teske's avatar
Daniel Teske committed
465
FileName AndroidConfig::adbToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
466
{
467
    FileName path = m_sdkLocation;
468
    return path.appendPath(QLatin1String("platform-tools/adb" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
469
470
}

471
Environment AndroidConfig::androidToolEnvironment() const
472
{
473
    Environment env = Environment::systemEnvironment();
474
    if (!m_openJDKLocation.isEmpty()) {
Daniel Teske's avatar
Daniel Teske committed
475
        env.set(QLatin1String("JAVA_HOME"), m_openJDKLocation.toUserOutput());
476
477
478
479
        Utils::FileName binPath = m_openJDKLocation;
        binPath.appendPath(QLatin1String("bin"));
        env.prependOrSetPath(binPath.toUserOutput());
    }
480
481
482
    return env;
}

Daniel Teske's avatar
Daniel Teske committed
483
FileName AndroidConfig::androidToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
484
{
485
486
487
    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
488
        FileName path = m_sdkLocation;
489
        path.appendPath(QLatin1String("tools/android" QTC_HOST_EXE_SUFFIX));
hjk's avatar
hjk committed
490
        if (path.exists())
491
            return path;
Daniel Teske's avatar
Daniel Teske committed
492
        path = m_sdkLocation;
493
        return path.appendPath(QLatin1String("tools/android" ANDROID_BAT_SUFFIX));
494
    } else {
Daniel Teske's avatar
Daniel Teske committed
495
        FileName path = m_sdkLocation;
496
497
        return path.appendPath(QLatin1String("tools/android"));
    }
BogDan Vatra's avatar
BogDan Vatra committed
498
499
}

Daniel Teske's avatar
Daniel Teske committed
500
FileName AndroidConfig::antToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
501
{
Daniel Teske's avatar
Daniel Teske committed
502
503
    if (!m_antLocation.isEmpty())
        return m_antLocation;
BogDan Vatra's avatar
BogDan Vatra committed
504
    else
505
        return FileName::fromLatin1("ant");
BogDan Vatra's avatar
BogDan Vatra committed
506
507
}

Daniel Teske's avatar
Daniel Teske committed
508
FileName AndroidConfig::emulatorToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
509
{
Daniel Teske's avatar
Daniel Teske committed
510
    FileName path = m_sdkLocation;
511
    return path.appendPath(QLatin1String("tools/emulator" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
512
513
}

Daniel Teske's avatar
Daniel Teske committed
514
FileName AndroidConfig::toolPath(const Abi &abi, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
515
{
Daniel Teske's avatar
Daniel Teske committed
516
    FileName path = m_ndkLocation;
Tobias Hunger's avatar
Tobias Hunger committed
517
    return path.appendPath(QString::fromLatin1("toolchains/%1-%2/prebuilt/%3/bin/%4")
Daniel Teske's avatar
Daniel Teske committed
518
            .arg(toolchainPrefix(abi))
519
            .arg(ndkToolChainVersion)
Daniel Teske's avatar
Daniel Teske committed
520
            .arg(toolchainHost())
Daniel Teske's avatar
Daniel Teske committed
521
            .arg(toolsPrefix(abi)));
BogDan Vatra's avatar
BogDan Vatra committed
522
523
}

Daniel Teske's avatar
Daniel Teske committed
524
FileName AndroidConfig::gccPath(const Abi &abi, const QString &ndkToolChainVersion) const
525
{
Daniel Teske's avatar
Daniel Teske committed
526
    return toolPath(abi, ndkToolChainVersion).appendString(QLatin1String("-gcc" QTC_HOST_EXE_SUFFIX));
527
528
}

Daniel Teske's avatar
Daniel Teske committed
529
FileName AndroidConfig::gdbPath(const Abi &abi, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
530
{
Daniel Teske's avatar
Daniel Teske committed
531
    return toolPath(abi, ndkToolChainVersion).appendString(QLatin1String("-gdb" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
532
533
}

Daniel Teske's avatar
Daniel Teske committed
534
FileName AndroidConfig::openJDKBinPath() const
BogDan Vatra's avatar
BogDan Vatra committed
535
{
Daniel Teske's avatar
Daniel Teske committed
536
    FileName path = m_openJDKLocation;
Tobias Hunger's avatar
Tobias Hunger committed
537
538
539
    if (!path.isEmpty())
        return path.appendPath(QLatin1String("bin"));
    return path;
BogDan Vatra's avatar
BogDan Vatra committed
540
541
}

Daniel Teske's avatar
Daniel Teske committed
542
FileName AndroidConfig::keytoolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
543
{
Tobias Hunger's avatar
Tobias Hunger committed
544
    return openJDKBinPath().appendPath(keytoolName);
BogDan Vatra's avatar
BogDan Vatra committed
545
546
}

Daniel Teske's avatar
Daniel Teske committed
547
QVector<AndroidDeviceInfo> AndroidConfig::connectedDevices(QString *error) const
548
549
550
551
552
{
    return connectedDevices(adbToolPath().toString(), error);
}

QVector<AndroidDeviceInfo> AndroidConfig::connectedDevices(const QString &adbToolPath, QString *error)
BogDan Vatra's avatar
BogDan Vatra committed
553
{
554
    QVector<AndroidDeviceInfo> devices;
BogDan Vatra's avatar
BogDan Vatra committed
555
    QProcess adbProc;
556
    adbProc.start(adbToolPath, QStringList() << QLatin1String("devices"));
557
    if (!adbProc.waitForFinished(10000)) {
558
        adbProc.kill();
559
        if (error)
Daniel Teske's avatar
Daniel Teske committed
560
561
            *error = QApplication::translate("AndroidConfiguration",
                                             "Could not run: %1")
562
                .arg(adbToolPath + QLatin1String(" devices"));
BogDan Vatra's avatar
BogDan Vatra committed
563
564
565
        return devices;
    }
    QList<QByteArray> adbDevs = adbProc.readAll().trimmed().split('\n');
BogDan Vatra's avatar
BogDan Vatra committed
566
567
568
569
570
571
    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
572
573
574

    // workaround for '????????????' serial numbers:
    // can use "adb -d" when only one usb device attached
BogDan Vatra's avatar
BogDan Vatra committed
575
    foreach (const QByteArray &device, adbDevs) {
576
577
        const QString serialNo = QString::fromLatin1(device.left(device.indexOf('\t')).trimmed());
        const QString deviceType = QString::fromLatin1(device.mid(device.indexOf('\t'))).trimmed();
578
        if (isBootToQt(adbToolPath, serialNo))
579
            continue;
580
        AndroidDeviceInfo dev;
581
        dev.serialNumber = serialNo;
582
        dev.type = serialNo.startsWith(QLatin1String("emulator")) ? AndroidDeviceInfo::Emulator : AndroidDeviceInfo::Hardware;
583
584
        dev.sdk = getSDKVersion(adbToolPath, dev.serialNumber);
        dev.cpuAbi = getAbis(adbToolPath, dev.serialNumber);
Daniel Teske's avatar
Daniel Teske committed
585
586
587
588
589
590
        if (deviceType == QLatin1String("unauthorized"))
            dev.state = AndroidDeviceInfo::UnAuthorizedState;
        else if (deviceType == QLatin1String("offline"))
            dev.state = AndroidDeviceInfo::OfflineState;
        else
            dev.state = AndroidDeviceInfo::OkState;
591
592
593
594

        if (dev.type == AndroidDeviceInfo::Emulator)
            dev.avdname = getAvdName(dev.serialNumber);

BogDan Vatra's avatar
BogDan Vatra committed
595
596
        devices.push_back(dev);
    }
597

598
    Utils::sort(devices, androidDevicesLessThan);
599
    if (devices.isEmpty() && error)
Daniel Teske's avatar
Daniel Teske committed
600
601
        *error = QApplication::translate("AndroidConfiguration",
                                         "No devices found in output of: %1")
602
            .arg(adbToolPath + QLatin1String(" devices"));
BogDan Vatra's avatar
BogDan Vatra committed
603
604
605
    return devices;
}

606
AndroidConfig::CreateAvdInfo AndroidConfig::gatherCreateAVDInfo(QWidget *parent, int minApiLevel, QString targetArch) const
BogDan Vatra's avatar
BogDan Vatra committed
607
{
608
    CreateAvdInfo result;
609
610
    AvdDialog d(minApiLevel, targetArch, this, parent);
    if (d.exec() != QDialog::Accepted || !d.isValid())
611
        return result;
612

613
614
615
616
617
618
    result.target = d.target();
    result.name = d.name();
    result.abi = d.abi();
    result.sdcardSize = d.sdcardSize();
    return result;
}
619

620
621
622
QFuture<AndroidConfig::CreateAvdInfo> AndroidConfig::createAVD(CreateAvdInfo info) const
{
    return QtConcurrent::run(&AndroidConfig::createAVDImpl, info, androidToolPath(), androidToolEnvironment());
BogDan Vatra's avatar
BogDan Vatra committed
623
624
}

625
AndroidConfig::CreateAvdInfo AndroidConfig::createAVDImpl(CreateAvdInfo info, FileName androidToolPath, Environment env)
BogDan Vatra's avatar
BogDan Vatra committed
626
627
{
    QProcess proc;
628
629
630
631
632
    proc.setProcessEnvironment(env.toProcessEnvironment());
    QStringList arguments;
    arguments << QLatin1String("create") << QLatin1String("avd")
              << QLatin1String("-t") << info.target
              << QLatin1String("-n") << info.name
633
634
635
              << QLatin1String("-b") << info.abi;
    if (info.sdcardSize > 0)
        arguments << QLatin1String("-c") << QString::fromLatin1("%1M").arg(info.sdcardSize);
636
637
638
    proc.start(androidToolPath.toString(), arguments);
    if (!proc.waitForStarted()) {
        info.error = QApplication::translate("AndroidConfig", "Could not start process \"%1 %2\"")
hjk's avatar
hjk committed
639
                .arg(androidToolPath.toString(), arguments.join(QLatin1Char(' ')));
640
641
        return info;
    }
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662

    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
663
    }
664
665
666

    proc.waitForFinished();

667
668
669
670
    QString errorOutput = QString::fromLocal8Bit(proc.readAllStandardError());
    // The exit code is always 0, so we need to check stderr
    // For now assume that any output at all indicates a error
    if (!errorOutput.isEmpty()) {
671
        info.error = errorOutput;
672
673
    }

674
    return info;
BogDan Vatra's avatar
BogDan Vatra committed
675
676
}

Daniel Teske's avatar
Daniel Teske committed
677
bool AndroidConfig::removeAVD(const QString &name) const
BogDan Vatra's avatar
BogDan Vatra committed
678
679
{
    QProcess proc;
680
    proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
Tobias Hunger's avatar
Tobias Hunger committed
681
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
682
683
               QStringList() << QLatin1String("delete") << QLatin1String("avd")
               << QLatin1String("-n") << name);
684
    if (!proc.waitForFinished(5000)) {
BogDan Vatra's avatar
BogDan Vatra committed
685
686
687
688
689
690
        proc.terminate();
        return false;
    }
    return !proc.exitCode();
}

691
692
QFuture<QVector<AndroidDeviceInfo>> AndroidConfig::androidVirtualDevicesFuture()
{
693
    return QtConcurrent::run(&AndroidConfig::androidVirtualDevices, androidToolPath().toString(), androidToolEnvironment());
694
695
}

696
QVector<AndroidDeviceInfo> AndroidConfig::androidVirtualDevices(const QString &androidTool, const Environment &environment)
BogDan Vatra's avatar
BogDan Vatra committed
697
{
698
    QVector<AndroidDeviceInfo> devices;
BogDan Vatra's avatar
BogDan Vatra committed
699
    QProcess proc;
700
    proc.setProcessEnvironment(environment.toProcessEnvironment());
701
    proc.start(androidTool,
BogDan Vatra's avatar
BogDan Vatra committed
702
               QStringList() << QLatin1String("list") << QLatin1String("avd")); // list available AVDs
703
    if (!proc.waitForFinished(20000)) {
BogDan Vatra's avatar
BogDan Vatra committed
704
705
706
707
        proc.terminate();
        return devices;
    }
    QList<QByteArray> avds = proc.readAll().trimmed().split('\n');
BogDan Vatra's avatar
BogDan Vatra committed
708
709
710
711
712
713
714
    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

Daniel Teske's avatar
Daniel Teske committed
715
716
    bool nextLineIsTargetLine = false;

717
    AndroidDeviceInfo dev;
BogDan Vatra's avatar
BogDan Vatra committed
718
    for (int i = 0; i < avds.size(); i++) {
719
        QString line = QLatin1String(avds.at(i));
BogDan Vatra's avatar
BogDan Vatra committed
720
721
722
        if (!line.contains(QLatin1String("Name:")))
            continue;

723
724
725
        int index = line.indexOf(QLatin1Char(':')) + 2;
        if (index >= line.size())
            break;
726
        dev.avdname = line.mid(index).trimmed();
727
728
        dev.sdk = -1;
        dev.cpuAbi.clear();
BogDan Vatra's avatar
BogDan Vatra committed
729
730
731
732
733
        ++i;
        for (; i < avds.size(); ++i) {
            line = QLatin1String(avds[i]);
            if (line.contains(QLatin1String("---------")))
                break;
Daniel Teske's avatar
Daniel Teske committed
734
735
736
737
738
739
740
741
742

            if (line.contains(QLatin1String("Target:")) || nextLineIsTargetLine) {
                if (line.contains(QLatin1String("Google APIs"))) {
                    nextLineIsTargetLine = true;
                    continue;
                }

                nextLineIsTargetLine = false;

743
744
745
746
                int lastIndex = line.lastIndexOf(QLatin1Char(' '));
                if (lastIndex == -1) // skip line
                    break;
                QString tmp = line.mid(lastIndex).remove(QLatin1Char(')')).trimmed();
Daniel Teske's avatar
Daniel Teske committed
747
                dev.sdk = apiLevelFromAndroidList(tmp);
748
            }
749
750
751
752
753
754
755
756
757
758
759
            if (line.contains(QLatin1String("Tag/ABI:"))) {
                int lastIndex = line.lastIndexOf(QLatin1Char('/')) + 1;
                if (lastIndex >= line.size())
                    break;
                dev.cpuAbi = QStringList() << line.mid(lastIndex);
            } else if (line.contains(QLatin1String("ABI:"))) {
                int lastIndex = line.lastIndexOf(QLatin1Char(' ')) + 1;
                if (lastIndex >= line.size())
                    break;
                dev.cpuAbi = QStringList() << line.mid(lastIndex).trimmed();
            }
BogDan Vatra's avatar
BogDan Vatra committed
760
        }
761
762
763
        // armeabi-v7a devices can also run armeabi code
        if (dev.cpuAbi == QStringList(QLatin1String("armeabi-v7a")))
            dev.cpuAbi << QLatin1String("armeabi");
Daniel Teske's avatar
Daniel Teske committed
764
        dev.state = AndroidDeviceInfo::OkState;
765
        dev.type = AndroidDeviceInfo::Emulator;
766
767
        if (dev.cpuAbi.isEmpty() || dev.sdk == -1)
            continue;
BogDan Vatra's avatar
BogDan Vatra committed
768
769
        devices.push_back(dev);
    }
770
    Utils::sort(devices, androidDevicesLessThan);
BogDan Vatra's avatar
BogDan Vatra committed
771
772
773
774

    return devices;
}

775
QString AndroidConfig::startAVD(const QString &name) const
776
{
777
778
    if (!findAvd(name).isEmpty() || startAVDAsync(name))
        return waitForAvd(name);
779
780
    return QString();
}
BogDan Vatra's avatar
BogDan Vatra committed
781

Daniel Teske's avatar
Daniel Teske committed
782
bool AndroidConfig::startAVDAsync(const QString &avdName) const
783
784
{
    QProcess *avdProcess = new QProcess();
Daniel Teske's avatar
Daniel Teske committed
785
    avdProcess->connect(avdProcess, SIGNAL(finished(int)), avdProcess, SLOT(deleteLater()));
BogDan Vatra's avatar
BogDan Vatra committed
786
787

    // start the emulator
788
789
790
791
792
793
794
    QStringList arguments;
    if (AndroidConfigurations::force32bitEmulator())
        arguments << QLatin1String("-force-32bit");

    arguments << QLatin1String("-partition-size") << QString::number(partitionSize())
              << QLatin1String("-avd") << avdName;
    avdProcess->start(emulatorToolPath().toString(), arguments);
Orgad Shaneh's avatar
Orgad Shaneh committed
795
796
    if (!avdProcess->waitForStarted(-1)) {
        delete avdProcess;
797
        return false;
BogDan Vatra's avatar
BogDan Vatra committed
798
    }
799
800
801
    return true;
}

802
QString AndroidConfig::findAvd(const QString &avdName) const
803
804
805
{
    QVector<AndroidDeviceInfo> devices = connectedDevices();
    foreach (AndroidDeviceInfo device, devices) {
806
        if (device.type != AndroidDeviceInfo::Emulator)
807
            continue;
808
809
        if (device.avdname == avdName)
            return device.serialNumber;
810
    }
811
    return QString();
812
813
}

Daniel Teske's avatar
Daniel Teske committed
814
bool AndroidConfig::isConnected(const QString &serialNumber) const
815
{
Daniel Teske's avatar
Daniel Teske committed
816
817
818
819
820
821
822
    QVector<AndroidDeviceInfo> devices = connectedDevices();
    foreach (AndroidDeviceInfo device, devices) {
        if (device.serialNumber == serialNumber)
            return true;
    }
    return false;
}
823

Daniel Teske's avatar
Daniel Teske committed
824
bool AndroidConfig::waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const
Daniel Teske's avatar
Daniel Teske committed
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
{
    // 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;
}

841
QString AndroidConfig::waitForAvd(const QString &avdName, const QFutureInterface<bool> &fi) const
Daniel Teske's avatar
Daniel Teske committed
842
843
844
{
    // 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
845
    QString serialNumber;
Daniel Teske's avatar
Daniel Teske committed
846
847
    for (int i = 0; i < 60; ++i) {
        if (fi.isCanceled())
Orgad Shaneh's avatar
Orgad Shaneh committed
848
            return QString();
849
        serialNumber = findAvd(avdName);
Daniel Teske's avatar
Daniel Teske committed
850
851
852
        if (!serialNumber.isEmpty())
            return waitForBooted(serialNumber, fi) ?  serialNumber : QString();
        Utils::sleep(2000);
BogDan Vatra's avatar
BogDan Vatra committed
853
854
855
856
    }
    return QString();
}

Daniel Teske's avatar
Daniel Teske committed
857
bool AndroidConfig::isBootToQt(const QString &device) const
858
859
860
861
862
{
    return isBootToQt(adbToolPath().toString(), device);
}

bool AndroidConfig::isBootToQt(const QString &adbToolPath, const QString &device)
863
864
865
866
867
868
869
{
    // 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;
870
    adbProc.start(adbToolPath, arguments);
871
    if (!adbProc.waitForFinished(10000)) {
872
        adbProc.kill();
873
        return false;
874
875
876
877
    }
    return adbProc.readAll().contains("Boot2Qt");
}

878
879

QString AndroidConfig::getDeviceProperty(const QString &adbToolPath, const QString &device, const QString &property)
BogDan Vatra's avatar
BogDan Vatra committed
880
{
881
882
883
    // workaround for '????????????' serial numbers
    QStringList arguments = AndroidDeviceInfo::adbSelector(device);
    arguments << QLatin1String("shell") << QLatin1String("getprop")
884
              << property;
BogDan Vatra's avatar
BogDan Vatra committed
885
886

    QProcess adbProc;
887
    adbProc.start(adbToolPath, arguments);