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

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

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

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

#include <QStringListModel>
#include <QMessageBox>

#if defined(_WIN32)
#include <iostream>
#include <windows.h>
#define sleep(_n) Sleep(1000 * (_n))
Thiago Macieira's avatar
Thiago Macieira committed
66 67
#else
#include <unistd.h>
BogDan Vatra's avatar
BogDan Vatra committed
68 69
#endif

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

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

105
    bool androidDevicesLessThan(const AndroidDeviceInfo &dev1, const AndroidDeviceInfo &dev2)
BogDan Vatra's avatar
BogDan Vatra committed
106 107 108 109 110
    {
        return dev1.sdk < dev2.sdk;
    }
}

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

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

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

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

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

}

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

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

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

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

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

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

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

QStringList AndroidConfigurations::sdkTargets(int minApiLevel) const
{
    QStringList targets;
    QProcess proc;
Tobias Hunger's avatar
Tobias Hunger committed
245
    proc.start(androidToolPath().toString(), QStringList() << QLatin1String("list") << QLatin1String("target")); // list avaialbe AVDs
BogDan Vatra's avatar
BogDan Vatra committed
246 247 248 249
    if (!proc.waitForFinished(-1)) {
        proc.terminate();
        return targets;
    }
250
    while (proc.canReadLine()) {
251
        const QString line = QString::fromLocal8Bit(proc.readLine().trimmed());
BogDan Vatra's avatar
BogDan Vatra committed
252 253 254 255 256 257 258 259 260 261
        int index = line.indexOf(QLatin1String("\"android-"));
        if (index == -1)
            continue;
        QString apiLevel = line.mid(index + 1, line.length() - index - 2);
        if (apiLevel.mid(apiLevel.lastIndexOf(QLatin1Char('-')) + 1).toInt() >= minApiLevel)
            targets.push_back(apiLevel);
    }
    return targets;
}

hjk's avatar
hjk committed
262
FileName AndroidConfigurations::adbToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
263
{
hjk's avatar
hjk committed
264
    FileName path = m_config.sdkLocation;
265
    return path.appendPath(QLatin1String("platform-tools/adb" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
266 267
}

hjk's avatar
hjk committed
268
FileName AndroidConfigurations::androidToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
269
{
270 271 272 273
    if (HostOsInfo::isWindowsHost()) {
        // I want to switch from using android.bat to using an executable. All it really does is call
        // Java and I've made some progress on it. So if android.exe exists, return that instead.
        FileName path = m_config.sdkLocation;
274
        path.appendPath(QLatin1String("tools/android" QTC_HOST_EXE_SUFFIX));
275 276 277
        if (path.toFileInfo().exists())
            return path;
        path = m_config.sdkLocation;
278
        return path.appendPath(QLatin1String("tools/android" ANDROID_BAT_SUFFIX));
279 280 281 282
    } else {
        FileName path = m_config.sdkLocation;
        return path.appendPath(QLatin1String("tools/android"));
    }
BogDan Vatra's avatar
BogDan Vatra committed
283 284
}

hjk's avatar
hjk committed
285
FileName AndroidConfigurations::antToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
286
{
Tobias Hunger's avatar
Tobias Hunger committed
287
    if (!m_config.antLocation.isEmpty())
BogDan Vatra's avatar
BogDan Vatra committed
288 289
        return m_config.antLocation;
    else
hjk's avatar
hjk committed
290
        return FileName::fromString(QLatin1String("ant"));
BogDan Vatra's avatar
BogDan Vatra committed
291 292
}

hjk's avatar
hjk committed
293
FileName AndroidConfigurations::emulatorToolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
294
{
hjk's avatar
hjk committed
295
    FileName path = m_config.sdkLocation;
296
    return path.appendPath(QLatin1String("tools/emulator" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
297 298
}

299
FileName AndroidConfigurations::toolPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
300
{
hjk's avatar
hjk committed
301
    FileName path = m_config.ndkLocation;
Tobias Hunger's avatar
Tobias Hunger committed
302
    return path.appendPath(QString::fromLatin1("toolchains/%1-%2/prebuilt/%3/bin/%4")
BogDan Vatra's avatar
BogDan Vatra committed
303
            .arg(toolchainPrefix(architecture))
304
            .arg(ndkToolChainVersion)
305
            .arg(m_config.toolchainHost)
Tobias Hunger's avatar
Tobias Hunger committed
306
            .arg(toolsPrefix(architecture)));
BogDan Vatra's avatar
BogDan Vatra committed
307 308
}

309
FileName AndroidConfigurations::stripPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
310
{
311
    return toolPath(architecture, ndkToolChainVersion).append(QLatin1String("-strip" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
312 313
}

314
FileName AndroidConfigurations::readelfPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
315
{
316
    return toolPath(architecture, ndkToolChainVersion).append(QLatin1String("-readelf" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
317 318
}

319 320 321 322 323
FileName AndroidConfigurations::gccPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
{
    return toolPath(architecture, ndkToolChainVersion).append(QLatin1String("-gcc" QTC_HOST_EXE_SUFFIX));
}

324
FileName AndroidConfigurations::gdbPath(Abi::Architecture architecture, const QString &ndkToolChainVersion) const
BogDan Vatra's avatar
BogDan Vatra committed
325
{
326
    return toolPath(architecture, ndkToolChainVersion).append(QLatin1String("-gdb" QTC_HOST_EXE_SUFFIX));
BogDan Vatra's avatar
BogDan Vatra committed
327 328
}

hjk's avatar
hjk committed
329
FileName AndroidConfigurations::openJDKPath() const
BogDan Vatra's avatar
BogDan Vatra committed
330 331 332 333
{
    return m_config.openJDKLocation;
}

334 335 336 337
void AndroidConfigurations::detectToolchainHost()
{
    QStringList hostPatterns;
    switch (HostOsInfo::hostOs()) {
338
    case OsTypeLinux:
339 340
        hostPatterns << QLatin1String("linux*");
        break;
341
    case OsTypeWindows:
342 343
        hostPatterns << QLatin1String("windows*");
        break;
344
    case OsTypeMac:
345 346 347 348 349 350 351 352 353 354 355 356 357
        hostPatterns << QLatin1String("darwin*");
        break;
    default: /* unknown host */ return;
    }

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

hjk's avatar
hjk committed
358
FileName AndroidConfigurations::openJDKBinPath() const
BogDan Vatra's avatar
BogDan Vatra committed
359
{
hjk's avatar
hjk committed
360
    FileName path = m_config.openJDKLocation;
Tobias Hunger's avatar
Tobias Hunger committed
361 362 363
    if (!path.isEmpty())
        return path.appendPath(QLatin1String("bin"));
    return path;
BogDan Vatra's avatar
BogDan Vatra committed
364 365
}

hjk's avatar
hjk committed
366
FileName AndroidConfigurations::keytoolPath() const
BogDan Vatra's avatar
BogDan Vatra committed
367
{
Tobias Hunger's avatar
Tobias Hunger committed
368
    return openJDKBinPath().appendPath(keytoolName);
BogDan Vatra's avatar
BogDan Vatra committed
369 370
}

hjk's avatar
hjk committed
371
FileName AndroidConfigurations::jarsignerPath() const
BogDan Vatra's avatar
BogDan Vatra committed
372
{
Tobias Hunger's avatar
Tobias Hunger committed
373
    return openJDKBinPath().appendPath(jarsignerName);
BogDan Vatra's avatar
BogDan Vatra committed
374 375
}

hjk's avatar
hjk committed
376
FileName AndroidConfigurations::zipalignPath() const
377 378
{
    Utils::FileName path = m_config.sdkLocation;
379
    return path.appendPath(QLatin1String("tools/zipalign" QTC_HOST_EXE_SUFFIX));
380 381
}

382
QString AndroidConfigurations::getDeployDeviceSerialNumber(int *apiLevel, const QString &abi, QString *error) const
BogDan Vatra's avatar
BogDan Vatra committed
383
{
384
    QVector<AndroidDeviceInfo> devices = connectedDevices(error);
BogDan Vatra's avatar
BogDan Vatra committed
385

386
    foreach (AndroidDeviceInfo device, devices) {
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
        if (!device.cpuAbi.contains(abi)) {
            if (error) {
                *error += tr("Skipping %1: ABI is incompatible, device supports ABIs: %2.")
                    .arg(getProductModel(device.serialNumber))
                    .arg(device.cpuAbi.join(QLatin1String(" ")));
                *error += QLatin1Char('\n');
            }
        } else if (device.sdk < *apiLevel) {
            if (error) {
                *error += tr("Skipping %1: API Level of device is: %2.")
                        .arg(getProductModel(device.serialNumber))
                        .arg(device.sdk);
                *error += QLatin1Char('\n');
            }
        } else {
            if (error)
                error->clear(); // no errors if we found a device
BogDan Vatra's avatar
BogDan Vatra committed
404 405 406
            *apiLevel = device.sdk;
            return device.serialNumber;
        }
Tobias Hunger's avatar
Tobias Hunger committed
407
    }
408
    return QString();
BogDan Vatra's avatar
BogDan Vatra committed
409 410
}

411
QVector<AndroidDeviceInfo> AndroidConfigurations::connectedDevices(QString *error) const
BogDan Vatra's avatar
BogDan Vatra committed
412
{
413
    QVector<AndroidDeviceInfo> devices;
BogDan Vatra's avatar
BogDan Vatra committed
414
    QProcess adbProc;
Tobias Hunger's avatar
Tobias Hunger committed
415
    adbProc.start(adbToolPath().toString(), QStringList() << QLatin1String("devices"));
BogDan Vatra's avatar
BogDan Vatra committed
416
    if (!adbProc.waitForFinished(-1)) {
417
        adbProc.kill();
418 419
        if (error)
            *error = tr("Could not run: %1").arg(adbToolPath().toString() + QLatin1String(" devices"));
BogDan Vatra's avatar
BogDan Vatra committed
420 421 422 423
        return devices;
    }
    QList<QByteArray> adbDevs = adbProc.readAll().trimmed().split('\n');
    adbDevs.removeFirst();
424
    AndroidDeviceInfo dev;
425 426 427 428 429

    // workaround for '????????????' serial numbers:
    // can use "adb -d" when only one usb device attached
    int usbDevicesNum = 0;
    QStringList serialNumbers;
BogDan Vatra's avatar
BogDan Vatra committed
430
    foreach (const QByteArray &device, adbDevs) {
431 432 433 434 435 436 437 438 439 440 441
        const QString serialNo = QString::fromLatin1(device.left(device.indexOf('\t')).trimmed());;
        if (!serialNo.startsWith(QLatin1String("emulator")))
            ++usbDevicesNum;
        serialNumbers << serialNo;
    }

    foreach (const QString &serialNo, serialNumbers) {
        if (serialNo.contains(QLatin1String("????")) && usbDevicesNum > 1)
            continue;

        dev.serialNumber = serialNo;
BogDan Vatra's avatar
BogDan Vatra committed
442
        dev.sdk = getSDKVersion(dev.serialNumber);
443
        dev.cpuAbi = getAbis(dev.serialNumber);
BogDan Vatra's avatar
BogDan Vatra committed
444 445 446
        devices.push_back(dev);
    }
    qSort(devices.begin(), devices.end(), androidDevicesLessThan);
447 448
    if (devices.isEmpty() && error)
        *error = tr("No devices found in output of: %1").arg(adbToolPath().toString() + QLatin1String(" devices"));
BogDan Vatra's avatar
BogDan Vatra committed
449 450 451
    return devices;
}

452
QString AndroidConfigurations::createAVD(int minApiLevel, QString targetArch) const
BogDan Vatra's avatar
BogDan Vatra committed
453 454 455 456
{
    QDialog d;
    Ui::AddNewAVDDialog avdDialog;
    avdDialog.setupUi(&d);
457
    avdDialog.targetComboBox->addItems(sdkTargets(minApiLevel));
458 459 460 461 462 463 464 465 466 467

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

468
    if (!avdDialog.targetComboBox->count()) {
Friedemann Kleint's avatar
Friedemann Kleint committed
469
        QMessageBox::critical(0, tr("Error Creating AVD"),
470
                              tr("Cannot create a new AVD. No sufficiently recent Android SDK available.\n"
Friedemann Kleint's avatar
Friedemann Kleint committed
471
                                 "Please install an SDK of at least API version %1.").
Friedemann Kleint's avatar
Friedemann Kleint committed
472
                              arg(minApiLevel));
473
        return QString();
BogDan Vatra's avatar
BogDan Vatra committed
474 475 476 477 478 479
    }

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

484
QString AndroidConfigurations::createAVD(const QString &target, const QString &name, const QString &abi, int sdcardSize ) const
BogDan Vatra's avatar
BogDan Vatra committed
485 486
{
    QProcess proc;
Tobias Hunger's avatar
Tobias Hunger committed
487
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
488 489 490
               QStringList() << QLatin1String("create") << QLatin1String("avd")
               << QLatin1String("-a") << QLatin1String("-t") << target
               << QLatin1String("-n") << name
491
               << QLatin1String("-b") << abi
BogDan Vatra's avatar
BogDan Vatra committed
492 493
               << QLatin1String("-c") << QString::fromLatin1("%1M").arg(sdcardSize));
    if (!proc.waitForStarted())
494
        return QString();
BogDan Vatra's avatar
BogDan Vatra committed
495 496 497
    proc.write(QByteArray("no\n"));
    if (!proc.waitForFinished(-1)) {
        proc.terminate();
498
        return QString();
BogDan Vatra's avatar
BogDan Vatra committed
499
    }
500 501 502
    if (proc.exitCode()) // error!
        return QString();
    return name;
BogDan Vatra's avatar
BogDan Vatra committed
503 504 505 506 507
}

bool AndroidConfigurations::removeAVD(const QString &name) const
{
    QProcess proc;
Tobias Hunger's avatar
Tobias Hunger committed
508
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
509 510 511 512 513 514 515 516 517
               QStringList() << QLatin1String("delete") << QLatin1String("avd")
               << QLatin1String("-n") << name);
    if (!proc.waitForFinished(-1)) {
        proc.terminate();
        return false;
    }
    return !proc.exitCode();
}

518
QVector<AndroidDeviceInfo> AndroidConfigurations::androidVirtualDevices() const
BogDan Vatra's avatar
BogDan Vatra committed
519
{
520
    QVector<AndroidDeviceInfo> devices;
BogDan Vatra's avatar
BogDan Vatra committed
521
    QProcess proc;
Tobias Hunger's avatar
Tobias Hunger committed
522
    proc.start(androidToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
523 524 525 526 527 528 529
               QStringList() << QLatin1String("list") << QLatin1String("avd")); // list available AVDs
    if (!proc.waitForFinished(-1)) {
        proc.terminate();
        return devices;
    }
    QList<QByteArray> avds = proc.readAll().trimmed().split('\n');
    avds.removeFirst();
530
    AndroidDeviceInfo dev;
BogDan Vatra's avatar
BogDan Vatra committed
531 532 533 534 535 536 537 538 539 540 541 542 543 544
    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:")))
545
                dev.cpuAbi = QStringList() << line.mid(line.lastIndexOf(QLatin1Char(' '))).trimmed();
BogDan Vatra's avatar
BogDan Vatra committed
546
        }
547 548 549
        // armeabi-v7a devices can also run armeabi code
        if (dev.cpuAbi == QStringList(QLatin1String("armeabi-v7a")))
            dev.cpuAbi << QLatin1String("armeabi");
BogDan Vatra's avatar
BogDan Vatra committed
550 551 552 553 554 555 556
        devices.push_back(dev);
    }
    qSort(devices.begin(), devices.end(), androidDevicesLessThan);

    return devices;
}

557
QString AndroidConfigurations::findAvd(int *apiLevel, const QString &cpuAbi)
BogDan Vatra's avatar
BogDan Vatra committed
558
{
559 560 561 562 563 564
    QVector<AndroidDeviceInfo> devices = androidVirtualDevices();
    foreach (const AndroidDeviceInfo &device, devices) {
        // take first emulator how supports this package
        if (device.sdk >= *apiLevel && device.cpuAbi.contains(cpuAbi)) {
            *apiLevel = device.sdk;
            return device.serialNumber;
BogDan Vatra's avatar
BogDan Vatra committed
565 566
        }
    }
567 568 569 570 571 572 573 574 575
    return QString();
}

QString AndroidConfigurations::startAVD(const QString &name, int apiLevel, QString cpuAbi) const
{
    if (startAVDAsync(name))
        return waitForAvd(apiLevel, cpuAbi);
    return QString();
}
BogDan Vatra's avatar
BogDan Vatra committed
576

577 578 579 580 581
bool AndroidConfigurations::startAVDAsync(const QString &avdName) const
{
    QProcess *avdProcess = new QProcess();
    connect(this, SIGNAL(destroyed()), avdProcess, SLOT(deleteLater()));
    connect(avdProcess, SIGNAL(finished(int)), avdProcess, SLOT(deleteLater()));
BogDan Vatra's avatar
BogDan Vatra committed
582 583

    // start the emulator
Orgad Shaneh's avatar
Orgad Shaneh committed
584
    avdProcess->start(emulatorToolPath().toString(),
BogDan Vatra's avatar
BogDan Vatra committed
585 586
                        QStringList() << QLatin1String("-partition-size") << QString::number(config().partitionSize)
                        << QLatin1String("-avd") << avdName);
Orgad Shaneh's avatar
Orgad Shaneh committed
587 588
    if (!avdProcess->waitForStarted(-1)) {
        delete avdProcess;
589
        return false;
BogDan Vatra's avatar
BogDan Vatra committed
590
    }
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
    return true;
}

QString AndroidConfigurations::waitForAvd(int apiLevel, const QString &cpuAbi) const
{
    // we cannot use adb -e wait-for-device, since that doesn't work if a emulator is already running

    // 15 rounds of 8s sleeping, a minute for the avd to start
    QString serialNumber;
    for (int i = 0; i < 15; ++i) {
        QVector<AndroidDeviceInfo> devices = connectedDevices();
        foreach (AndroidDeviceInfo device, devices) {
            if (!device.serialNumber.startsWith(QLatin1String("emulator")))
                continue;
            if (!device.cpuAbi.contains(cpuAbi))
                continue;
            if (!device.sdk == apiLevel)
                continue;
            serialNumber = device.serialNumber;
            // found a serial number, now wait until it's done booting...
            for (int i = 0; i < 15; ++i) {
                if (hasFinishedBooting(serialNumber))
                    return serialNumber;
                else
                    sleep(8);
            }
Orgad Shaneh's avatar
Orgad Shaneh committed
617 618
            return QString();
        }
619
        sleep(8);
BogDan Vatra's avatar
BogDan Vatra committed
620 621 622 623 624 625
    }
    return QString();
}

int AndroidConfigurations::getSDKVersion(const QString &device) const
{
626 627 628 629
    // 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
630 631

    QProcess adbProc;
632
    adbProc.start(adbToolPath().toString(), arguments);
BogDan Vatra's avatar
BogDan Vatra committed
633
    if (!adbProc.waitForFinished(-1)) {
634
        adbProc.kill();
BogDan Vatra's avatar
BogDan Vatra committed
635 636 637 638 639
        return -1;
    }
    return adbProc.readAll().trimmed().toInt();
}

640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
//!
//! \brief AndroidConfigurations::getProductModel
//! \param device serial number
//! \return the produce model of the device or if that cannot be read the serial number
//!
QString AndroidConfigurations::getProductModel(const QString &device) const
{
    // workaround for '????????????' serial numbers
    QStringList arguments = AndroidDeviceInfo::adbSelector(device);
    arguments << QLatin1String("shell") << QLatin1String("getprop")
              << QLatin1String("ro.product.model");

    QProcess adbProc;
    adbProc.start(adbToolPath().toString(), arguments);
    if (!adbProc.waitForFinished(-1)) {
        adbProc.kill();
        return device;
    }
    QString model = QString::fromLocal8Bit(adbProc.readAll().trimmed());
    if (model.isEmpty())
        return device;
    return model;
}

664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
bool AndroidConfigurations::hasFinishedBooting(const QString &device) const
{
    QStringList arguments = AndroidDeviceInfo::adbSelector(device);
    arguments << QLatin1String("shell") << QLatin1String("getprop")
              << QLatin1String("init.svc.bootanim");

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

682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708
QStringList AndroidConfigurations::getAbis(const QString &device) const
{
    QStringList result;
    int i = 1;
    while (true) {
        QStringList arguments = AndroidDeviceInfo::adbSelector(device);
        arguments << QLatin1String("shell") << QLatin1String("getprop");
        if (i == 1)
            arguments << QLatin1String("ro.product.cpu.abi");
        else
            arguments << QString::fromLatin1("ro.product.cpu.abi%1").arg(i);

        QProcess adbProc;
        adbProc.start(adbToolPath().toString(), arguments);
        if (!adbProc.waitForFinished(-1)) {
            adbProc.kill();
            return result;
        }
        QString abi = QString::fromLocal8Bit(adbProc.readAll().trimmed());
        if (abi.isEmpty())
            break;
        result << abi;
        ++i;
    }
    return result;
}

BogDan Vatra's avatar
BogDan Vatra committed
709 710 711 712 713 714 715 716 717 718
QString AndroidConfigurations::bestMatch(const QString &targetAPI) const
{
    int target = targetAPI.mid(targetAPI.lastIndexOf(QLatin1Char('-')) + 1).toInt();
    foreach (int apiLevel, m_availablePlatforms) {
        if (apiLevel <= target)
            return QString::fromLatin1("android-%1").arg(apiLevel);
    }
    return QLatin1String("android-8");
}

719 720 721 722 723
QStringList AndroidConfigurations::makeExtraSearchDirectories() const
{
    return m_config.makeExtraSearchDirectories;
}

Daniel Teske's avatar
Daniel Teske committed
724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751
bool equalKits(Kit *a, Kit *b)
{
    return ToolChainKitInformation::toolChain(a) == ToolChainKitInformation::toolChain(b)
            && QtSupport::QtKitInformation::qtVersion(a) == QtSupport::QtKitInformation::qtVersion(b);
}

void AndroidConfigurations::updateAutomaticKitList()
{
    QList<AndroidToolChain *> toolchains;
    if (AndroidConfigurations::instance().config().automaticKitCreation) {
        // having a empty toolchains list will remove all autodetected kits for android
        // exactly what we want in that case
        foreach (ProjectExplorer::ToolChain *tc, ProjectExplorer::ToolChainManager::instance()->toolChains()) {
            if (!tc->isAutoDetected())
                continue;
            if (tc->type() != QLatin1String(Constants::ANDROID_TOOLCHAIN_TYPE))
                continue;
            toolchains << static_cast<AndroidToolChain *>(tc);
        }
    }

    QList<Kit *> existingKits;

    foreach (ProjectExplorer::Kit *k, ProjectExplorer::KitManager::instance()->kits()) {
        if (ProjectExplorer::DeviceKitInformation::deviceId(k) != Core::Id(Constants::ANDROID_DEVICE_ID))
            continue;
        if (!k->isAutoDetected())
            continue;
Daniel Teske's avatar
Daniel Teske committed
752 753
        if (k->isSdkProvided())
            continue;
Daniel Teske's avatar
Daniel Teske committed
754 755 756 757 758 759 760 761

        existingKits << k;
    }

    QMap<ProjectExplorer::Abi::Architecture, QList<QtSupport::BaseQtVersion *> > qtVersionsForArch;
    foreach (QtSupport::BaseQtVersion *qtVersion, QtSupport::QtVersionManager::instance()->versions()) {
        if (qtVersion->type() != QLatin1String(Constants::ANDROIDQT))
            continue;
762 763 764 765
        QList<ProjectExplorer::Abi> qtAbis = qtVersion->qtAbis();
        if (qtAbis.empty())
            continue;
        qtVersionsForArch[qtAbis.first().architecture()].append(qtVersion);
Daniel Teske's avatar
Daniel Teske committed
766 767 768 769 770 771 772 773 774 775 776 777
    }

    ProjectExplorer::DeviceManager *dm = ProjectExplorer::DeviceManager::instance();
    IDevice::ConstPtr device = dm->find(Core::Id(Constants::ANDROID_DEVICE_ID)); // should always exist

    // register new kits
    QList<Kit *> newKits;
    foreach (AndroidToolChain *tc, toolchains) {
        QList<QtSupport::BaseQtVersion *> qtVersions = qtVersionsForArch.value(tc->targetAbi().architecture());
        foreach (QtSupport::BaseQtVersion *qt, qtVersions) {
            Kit *newKit = new Kit;
            newKit->setAutoDetected(true);
Daniel Teske's avatar
Daniel Teske committed
778
            newKit->setIconPath(QLatin1String(Constants::ANDROID_SETTINGS_CATEGORY_ICON));
Daniel Teske's avatar
Daniel Teske committed
779 780 781 782 783 784 785 786 787
            DeviceTypeKitInformation::setDeviceTypeId(newKit, Core::Id(Constants::ANDROID_DEVICE_TYPE));
            ToolChainKitInformation::setToolChain(newKit, tc);
            QtSupport::QtKitInformation::setQtVersion(newKit, qt);
            DeviceKitInformation::setDevice(newKit, device);
            Debugger::DebuggerKitInformation::DebuggerItem item;
            item.engineType = Debugger::GdbEngineType;
            item.binary = tc->suggestedDebugger();
            Debugger::DebuggerKitInformation::setDebuggerItem(newKit, item);
            AndroidGdbServerKitInformation::setGdbSever(newKit, tc->suggestedGdbServer());
788
            newKit->makeSticky();
Daniel Teske's avatar
Daniel Teske committed
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809
            newKits << newKit;
        }
    }

    for (int i = existingKits.count() - 1; i >= 0; --i) {
        Kit *existingKit = existingKits.at(i);
        for (int j = 0; j < newKits.count(); ++j) {
            Kit *newKit = newKits.at(j);
            if (equalKits(existingKit, newKit)) {
                // Kit is already registered, nothing to do
                newKits.removeAt(j);
                existingKits.removeAt(i);
                KitManager::deleteKit(newKit);
                j = newKits.count();
            }
        }
    }

    foreach (Kit *k, existingKits)
        KitManager::instance()->deregisterKit(k);

810 811
    foreach (Kit *kit, newKits) {
        AndroidToolChain *tc = static_cast<AndroidToolChain *>(ToolChainKitInformation::toolChain(kit));
812
        AndroidQtVersion *qt = static_cast<AndroidQtVersion *>(QtSupport::QtKitInformation::qtVersion(kit));
813
        kit->setDisplayName(tr("Android for %1 (GCC %2, Qt %3)")
814
                            .arg(qt->targetArch())
815 816
                            .arg(tc->ndkToolChainVersion())
                            .arg(qt->qtVersionString()));
Daniel Teske's avatar
Daniel Teske committed
817
        KitManager::instance()->registerKit(kit);
818
    }
Daniel Teske's avatar
Daniel Teske committed
819 820
}

821 822 823 824 825 826 827 828 829 830 831
/**
 * Workaround for '????????????' serial numbers
 * @return ("-d") for buggy devices, ("-s", <serial no>) for normal
 */
QStringList AndroidDeviceInfo::adbSelector(const QString &serialNumber)
{
    if (serialNumber.startsWith(QLatin1String("????")))
        return QStringList() << QLatin1String("-d");
    return QStringList() << QLatin1String("-s") << serialNumber;
}

BogDan Vatra's avatar
BogDan Vatra committed
832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855
AndroidConfigurations &AndroidConfigurations::instance(QObject *parent)
{
    if (m_instance == 0)
        m_instance = new AndroidConfigurations(parent);
    return *m_instance;
}

void AndroidConfigurations::save()
{
    QSettings *settings = Core::ICore::instance()->settings();
    settings->beginGroup(SettingsGroup);
    m_config.save(*settings);
    settings->endGroup();
}

AndroidConfigurations::AndroidConfigurations(QObject *parent)
    : QObject(parent)
{
    load();
    updateAvailablePlatforms();
}

void AndroidConfigurations::load()
{
856
    bool saveSettings = false;
BogDan Vatra's avatar
BogDan Vatra committed
857 858 859
    QSettings *settings = Core::ICore::instance()->settings();
    settings->beginGroup(SettingsGroup);
    m_config = AndroidConfig(*settings);
Daniel Teske's avatar
Daniel Teske committed
860 861 862 863 864

    if (m_config.antLocation.isEmpty()) {
        Utils::Environment env = Utils::Environment::systemEnvironment();
        QString location = env.searchInPath(QLatin1String("ant"));
        QFileInfo fi(location);
865
        if (fi.exists() && fi.isExecutable() && !fi.isDir()) {
Daniel Teske's avatar
Daniel Teske committed
866
            m_config.antLocation = Utils::FileName::fromString(location);
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
            saveSettings = true;
        }
    }

    if (m_config.openJDKLocation.isEmpty()) {
        Utils::Environment env = Utils::Environment::systemEnvironment();
        QString location = env.searchInPath(QLatin1String("javac"));
        QFileInfo fi(location);
        if (fi.exists() && fi.isExecutable() && !fi.isDir()) {
            QDir parentDirectory = fi.canonicalPath();
            parentDirectory.cdUp(); // one up from bin
            m_config.openJDKLocation = Utils::FileName::fromString(parentDirectory.absolutePath());
            saveSettings = true;
        } else if (Utils::HostOsInfo::isWindowsHost()) {
            QSettings settings(QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Javasoft\\Java Development Kit"), QSettings::NativeFormat);
            QStringList allVersions = settings.childGroups();
            QString javaHome;
            int major = -1;
            int minor = -1;
            foreach (const QString &version, allVersions) {
                QStringList parts = version.split(QLatin1String("."));
                if (parts.size() != 2) // not interested in 1.7.0_u21
                    continue;
                bool okMajor, okMinor;
                int tmpMajor = parts.at(0).toInt(&okMajor);
                int tmpMinor = parts.at(1).toInt(&okMinor);
                if (!okMajor || !okMinor)
                    continue;
                if (tmpMajor > major
                        || (tmpMajor == major
                            && tmpMinor > minor)) {
                    settings.beginGroup(version);
                    QString tmpJavaHome = settings.value(QLatin1String("JavaHome")).toString();
                    settings.endGroup();
                    if (!QFileInfo(tmpJavaHome).exists())
                        continue;

                    major = tmpMajor;
                    minor = tmpMinor;
                    javaHome = tmpJavaHome;
                }
            }
            if (!javaHome.isEmpty()) {
                m_config.openJDKLocation = Utils::FileName::fromString(javaHome);
                saveSettings = true;
            }
        }
Daniel Teske's avatar
Daniel Teske committed
914 915
    }

BogDan Vatra's avatar
BogDan Vatra committed
916
    settings->endGroup();
917 918 919

    if (saveSettings)
        save();
920 921 922 923
}

void AndroidConfigurations::updateAndroidDevice()
{
924
    ProjectExplorer::DeviceManager * const devMgr = ProjectExplorer::DeviceManager::instance();
925
    if (adbToolPath().toFileInfo().exists())
926 927 928
        devMgr->addDevice(ProjectExplorer::IDevice::Ptr(new Internal::AndroidDevice));
    else if (devMgr->find(Constants::ANDROID_DEVICE_ID))
        devMgr->removeDevice(Core::Id(Constants::ANDROID_DEVICE_ID));
BogDan Vatra's avatar
BogDan Vatra committed
929 930 931 932 933 934
}

AndroidConfigurations *AndroidConfigurations::m_instance = 0;

} // namespace Internal
} // namespace Android