You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by st...@apache.org on 2017/05/01 21:17:24 UTC

[42/63] [abbrv] cordova-lib git commit: CB-11242: updated tests and fixtures

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/lib/builders/GradleBuilder.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/lib/builders/GradleBuilder.js b/cordova-lib/spec-plugman/projects/android/cordova/lib/builders/GradleBuilder.js
new file mode 100644
index 0000000..5b5ce13
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/lib/builders/GradleBuilder.js
@@ -0,0 +1,279 @@
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+*/
+
+var Q = require('q');
+var fs = require('fs');
+var util = require('util');
+var path = require('path');
+var shell = require('shelljs');
+var spawn = require('cordova-common').superspawn.spawn;
+var CordovaError = require('cordova-common').CordovaError;
+var check_reqs = require('../check_reqs');
+
+var GenericBuilder = require('./GenericBuilder');
+
+var MARKER = 'YOUR CHANGES WILL BE ERASED!';
+var SIGNING_PROPERTIES = '-signing.properties';
+var TEMPLATE =
+    '# This file is automatically generated.\n' +
+    '# Do not modify this file -- ' + MARKER + '\n';
+
+function GradleBuilder (projectRoot) {
+    GenericBuilder.call(this, projectRoot);
+
+    this.binDirs = {gradle: this.binDirs.gradle};
+}
+
+util.inherits(GradleBuilder, GenericBuilder);
+
+GradleBuilder.prototype.getArgs = function(cmd, opts) {
+    if (cmd == 'release') {
+        cmd = 'cdvBuildRelease';
+    } else if (cmd == 'debug') {
+        cmd = 'cdvBuildDebug';
+    }
+    var args = [cmd, '-b', path.join(this.root, 'build.gradle')];
+    if (opts.arch) {
+        args.push('-PcdvBuildArch=' + opts.arch);
+    }
+
+    // 10 seconds -> 6 seconds
+    args.push('-Dorg.gradle.daemon=true');
+    // to allow dex in process
+    args.push('-Dorg.gradle.jvmargs=-Xmx2048m');
+    // allow NDK to be used - required by Gradle 1.5 plugin
+    args.push('-Pandroid.useDeprecatedNdk=true');
+    args.push.apply(args, opts.extraArgs);
+    // Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
+    // args.push('-Dorg.gradle.parallel=true');
+    return args;
+};
+
+/*
+ * This returns a promise
+ */
+
+GradleBuilder.prototype.runGradleWrapper = function(gradle_cmd) {
+    var gradlePath = path.join(this.root, 'gradlew');
+    var wrapperGradle = path.join(this.root, 'wrapper.gradle');
+    if(fs.existsSync(gradlePath)) {
+      //Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows
+    } else {
+      return spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], {stdio: 'inherit'});
+    }
+};
+
+
+// Makes the project buildable, minus the gradle wrapper.
+GradleBuilder.prototype.prepBuildFiles = function() {
+    // Update the version of build.gradle in each dependent library.
+    var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle');
+    var propertiesObj = this.readProjectProperties();
+    var subProjects = propertiesObj.libs;
+    var checkAndCopy = function(subProject, root) {
+      var subProjectGradle = path.join(root, subProject, 'build.gradle');
+      // This is the future-proof way of checking if a file exists
+      // This must be synchronous to satisfy a Travis test
+      try {
+          fs.accessSync(subProjectGradle, fs.F_OK);
+      } catch (e) {
+          shell.cp('-f', pluginBuildGradle, subProjectGradle);
+      }
+    };
+    for (var i = 0; i < subProjects.length; ++i) {
+        if (subProjects[i] !== 'CordovaLib') {
+          checkAndCopy(subProjects[i], this.root);
+        }
+    }
+    var name = this.extractRealProjectNameFromManifest();
+    //Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
+    var settingsGradlePaths =  subProjects.map(function(p){
+        var realDir=p.replace(/[/\\]/g, ':');
+        var libName=realDir.replace(name+'-','');
+        var str='include ":'+libName+'"\n';
+        if(realDir.indexOf(name+'-')!==-1)
+            str+='project(":'+libName+'").projectDir = new File("'+p+'")\n';
+        return str;
+    });
+
+    // Write the settings.gradle file.
+    fs.writeFileSync(path.join(this.root, 'settings.gradle'),
+        '// GENERATED FILE - DO NOT EDIT\n' +
+        'include ":"\n' + settingsGradlePaths.join(''));
+    // Update dependencies within build.gradle.
+    var buildGradle = fs.readFileSync(path.join(this.root, 'build.gradle'), 'utf8');
+    var depsList = '';
+    var root = this.root;
+    var insertExclude = function(p) {
+          var gradlePath = path.join(root, p, 'build.gradle');
+          var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8');
+          if(projectGradleFile.indexOf('CordovaLib') != -1) {
+            depsList += '{\n        exclude module:("CordovaLib")\n    }\n';
+          }
+          else {
+            depsList +='\n';
+          }
+    };
+    subProjects.forEach(function(p) {
+        console.log('Subproject Path: ' + p);
+        var libName=p.replace(/[/\\]/g, ':').replace(name+'-','');
+        depsList += '    debugCompile(project(path: "' + libName + '", configuration: "debug"))';
+        insertExclude(p);
+        depsList += '    releaseCompile(project(path: "' + libName + '", configuration: "release"))';
+        insertExclude(p);
+    });
+    // For why we do this mapping: https://issues.apache.org/jira/browse/CB-8390
+    var SYSTEM_LIBRARY_MAPPINGS = [
+        [/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'],
+        [/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+']
+    ];
+    propertiesObj.systemLibs.forEach(function(p) {
+        var mavenRef;
+        // It's already in gradle form if it has two ':'s
+        if (/:.*:/.exec(p)) {
+            mavenRef = p;
+        } else {
+            for (var i = 0; i < SYSTEM_LIBRARY_MAPPINGS.length; ++i) {
+                var pair = SYSTEM_LIBRARY_MAPPINGS[i];
+                if (pair[0].exec(p)) {
+                    mavenRef = p.replace(pair[0], pair[1]);
+                    break;
+                }
+            }
+            if (!mavenRef) {
+                throw new CordovaError('Unsupported system library (does not work with gradle): ' + p);
+            }
+        }
+        depsList += '    compile "' + mavenRef + '"\n';
+    });
+    buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + '    $2');
+    var includeList = '';
+    propertiesObj.gradleIncludes.forEach(function(includePath) {
+        includeList += 'apply from: "' + includePath + '"\n';
+    });
+    buildGradle = buildGradle.replace(/(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + includeList + '$2');
+    fs.writeFileSync(path.join(this.root, 'build.gradle'), buildGradle);
+};
+
+GradleBuilder.prototype.prepEnv = function(opts) {
+    var self = this;
+    return check_reqs.check_gradle()
+      .then(function(gradlePath) {
+        return self.runGradleWrapper(gradlePath);
+      }).then(function() {
+          return self.prepBuildFiles();
+      }).then(function() {
+        // We now copy the gradle out of the framework
+        // This is a dirty patch to get the build working
+        /*
+        var wrapperDir = path.join(self.root, 'CordovaLib');
+        if (process.platform == 'win32') {
+            shell.rm('-f', path.join(self.root, 'gradlew.bat'));
+            shell.cp(path.join(wrapperDir, 'gradlew.bat'), self.root);
+        } else {
+            shell.rm('-f', path.join(self.root, 'gradlew'));
+            shell.cp(path.join(wrapperDir, 'gradlew'), self.root);
+        }
+        shell.rm('-rf', path.join(self.root, 'gradle', 'wrapper'));
+        shell.mkdir('-p', path.join(self.root, 'gradle'));
+        shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(self.root, 'gradle'));
+*/
+        // If the gradle distribution URL is set, make sure it points to version we want.
+        // If it's not set, do nothing, assuming that we're using a future version of gradle that we don't want to mess with.
+        // For some reason, using ^ and $ don't work.  This does the job, though.
+        var distributionUrlRegex = /distributionUrl.*zip/;
+        /*jshint -W069 */
+        var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'https\\://services.gradle.org/distributions/gradle-3.3-all.zip';
+        /*jshint +W069 */
+        var gradleWrapperPropertiesPath = path.join(self.root, 'gradle', 'wrapper', 'gradle-wrapper.properties');
+        shell.chmod('u+w', gradleWrapperPropertiesPath);
+        shell.sed('-i', distributionUrlRegex, 'distributionUrl='+distributionUrl, gradleWrapperPropertiesPath);
+
+        var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
+        var propertiesFilePath = path.join(self.root, propertiesFile);
+        if (opts.packageInfo) {
+            fs.writeFileSync(propertiesFilePath, TEMPLATE + opts.packageInfo.toProperties());
+        } else if (isAutoGenerated(propertiesFilePath)) {
+            shell.rm('-f', propertiesFilePath);
+        }
+    });
+};
+
+/*
+ * Builds the project with gradle.
+ * Returns a promise.
+ */
+GradleBuilder.prototype.build = function(opts) {
+    var wrapper = path.join(this.root, 'gradlew');
+    var args = this.getArgs(opts.buildType == 'debug' ? 'debug' : 'release', opts);
+
+    return spawn(wrapper, args, {stdio: 'pipe'})
+    .progress(function (stdio){
+        if (stdio.stderr) {
+            /*
+             * Workaround for the issue with Java printing some unwanted information to
+             * stderr instead of stdout.
+             * This function suppresses 'Picked up _JAVA_OPTIONS' message from being
+             * printed to stderr. See https://issues.apache.org/jira/browse/CB-9971 for
+             * explanation.
+             */
+            var suppressThisLine = /^Picked up _JAVA_OPTIONS: /i.test(stdio.stderr.toString());
+            if (suppressThisLine) {
+                return;
+            }
+            process.stderr.write(stdio.stderr);
+        } else {
+            process.stdout.write(stdio.stdout);
+        }
+    }).catch(function (error) {
+        if (error.toString().indexOf('failed to find target with hash string') >= 0) {
+            return check_reqs.check_android_target(error).then(function() {
+                // If due to some odd reason - check_android_target succeeds
+                // we should still fail here.
+                return Q.reject(error);
+            });
+        }
+        return Q.reject(error);
+    });
+};
+
+GradleBuilder.prototype.clean = function(opts) {
+    var builder = this;
+    var wrapper = path.join(this.root, 'gradlew');
+    var args = builder.getArgs('clean', opts);
+    return Q().then(function() {
+        return spawn(wrapper, args, {stdio: 'inherit'});
+    })
+    .then(function () {
+        shell.rm('-rf', path.join(builder.root, 'out'));
+
+        ['debug', 'release'].forEach(function(config) {
+            var propertiesFilePath = path.join(builder.root, config + SIGNING_PROPERTIES);
+            if(isAutoGenerated(propertiesFilePath)){
+                shell.rm('-f', propertiesFilePath);
+            }
+        });
+    });
+};
+
+module.exports = GradleBuilder;
+
+function isAutoGenerated(file) {
+    return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
+}

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/lib/builders/builders.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/lib/builders/builders.js b/cordova-lib/spec-plugman/projects/android/cordova/lib/builders/builders.js
new file mode 100644
index 0000000..4921c49
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/lib/builders/builders.js
@@ -0,0 +1,47 @@
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+*/
+
+var CordovaError = require('cordova-common').CordovaError;
+
+var knownBuilders = {
+    ant: 'AntBuilder',
+    gradle: 'GradleBuilder',
+    none: 'GenericBuilder'
+};
+
+/**
+ * Helper method that instantiates and returns a builder for specified build
+ *   type.
+ *
+ * @param   {String}  builderType   Builder name to construct and return. Must
+ *   be one of 'ant', 'gradle' or 'none'
+ *
+ * @return  {Builder}               A builder instance for specified build type.
+ */
+module.exports.getBuilder = function (builderType, projectRoot) {
+    if (!knownBuilders[builderType])
+        throw new CordovaError('Builder ' + builderType + ' is not supported.');
+
+    try {
+        var Builder = require('./' + knownBuilders[builderType]);
+        return new Builder(projectRoot);
+    } catch (err) {
+        throw new CordovaError('Failed to instantiate ' + knownBuilders[builderType] + ' builder: ' + err);
+    }
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/lib/check_reqs.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/lib/check_reqs.js b/cordova-lib/spec-plugman/projects/android/cordova/lib/check_reqs.js
new file mode 100644
index 0000000..1fd397a
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/lib/check_reqs.js
@@ -0,0 +1,432 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+*/
+
+/* jshint sub:true */
+
+var shelljs = require('shelljs'),
+    child_process = require('child_process'),
+    Q     = require('q'),
+    path  = require('path'),
+    fs    = require('fs'),
+    os    = require('os'),
+    REPO_ROOT  = path.join(__dirname, '..', '..', '..', '..'),
+    PROJECT_ROOT = path.join(__dirname, '..', '..');
+var CordovaError = require('cordova-common').CordovaError;
+var superspawn = require('cordova-common').superspawn;
+var android_sdk = require('./android_sdk');
+
+function forgivingWhichSync(cmd) {
+    try {
+        return fs.realpathSync(shelljs.which(cmd));
+    } catch (e) {
+        return '';
+    }
+}
+
+function tryCommand(cmd, errMsg, catchStderr) {
+    var d = Q.defer();
+    child_process.exec(cmd, function(err, stdout, stderr) {
+        if (err) d.reject(new CordovaError(errMsg));
+        // Sometimes it is necessary to return an stderr instead of stdout in case of success, since
+        // some commands prints theirs output to stderr instead of stdout. 'javac' is the example
+        else d.resolve((catchStderr ? stderr : stdout).trim());
+    });
+    return d.promise;
+}
+
+module.exports.isWindows = function() {
+    return (os.platform() == 'win32');
+};
+
+module.exports.isDarwin = function() {
+    return (os.platform() == 'darwin');
+};
+
+// Get valid target from framework/project.properties if run from this repo
+// Otherwise get target from project.properties file within a generated cordova-android project
+module.exports.get_target = function() {
+    function extractFromFile(filePath) {
+        var target = shelljs.grep(/\btarget=/, filePath);
+        if (!target) {
+            throw new Error('Could not find android target within: ' + filePath);
+        }
+        return target.split('=')[1].trim();
+    }
+    var repo_file = path.join(REPO_ROOT, 'framework', 'project.properties');
+    if (fs.existsSync(repo_file)) {
+        return extractFromFile(repo_file);
+    }
+    var project_file = path.join(PROJECT_ROOT, 'project.properties');
+    if (fs.existsSync(project_file)) {
+        // if no target found, we're probably in a project and project.properties is in PROJECT_ROOT.
+        return extractFromFile(project_file);
+    }
+    throw new Error('Could not find android target in either ' + repo_file + ' nor ' + project_file);
+};
+
+// Returns a promise. Called only by build and clean commands.
+module.exports.check_ant = function() {
+    return superspawn.spawn('ant', ['-version'])
+    .then(function(output) {
+        // Parse Ant version from command output
+        return /version ((?:\d+\.)+(?:\d+))/i.exec(output)[1];
+    }).catch(function(err) {
+        throw new CordovaError('Failed to run `ant -version`. Make sure you have `ant` on your $PATH.');
+    });
+};
+
+module.exports.get_gradle_wrapper = function() {
+    var androidStudioPath;
+    var i = 0;
+    var foundStudio = false;
+    var program_dir;
+    if (module.exports.isDarwin()) {
+        program_dir = fs.readdirSync('/Applications');
+        while (i < program_dir.length && !foundStudio) {
+            if (program_dir[i].startsWith('Android Studio')) {
+                //TODO: Check for a specific Android Studio version, make sure it's not Canary
+                androidStudioPath = path.join('/Applications', program_dir[i], 'Contents', 'gradle');
+                foundStudio = true;
+            } else { ++i; }
+        }
+    } else if (module.exports.isWindows()) {
+        var androidPath = path.join(process.env['ProgramFiles'], 'Android') + '/';
+        if (fs.existsSync(androidPath)) {
+            program_dir = fs.readdirSync(androidPath);
+            while (i < program_dir.length && !foundStudio) {
+                if (program_dir[i].startsWith('Android Studio')) {
+                    foundStudio = true;
+                    androidStudioPath = path.join(process.env['ProgramFiles'], 'Android', program_dir[i], 'gradle');
+                } else { ++i; }
+            }
+        }
+    }
+
+    if (androidStudioPath !== null && fs.existsSync(androidStudioPath)) {
+        var dirs = fs.readdirSync(androidStudioPath);
+        if(dirs[0].split('-')[0] == 'gradle') {
+            return path.join(androidStudioPath, dirs[0], 'bin', 'gradle');
+        }
+    } else {
+        //OK, let's try to check for Gradle!
+        return forgivingWhichSync('gradle');
+    }
+};
+
+// Returns a promise. Called only by build and clean commands.
+module.exports.check_gradle = function() {
+    var sdkDir = process.env['ANDROID_HOME'];
+    var d = Q.defer();
+    if (!sdkDir)
+        return Q.reject(new CordovaError('Could not find gradle wrapper within Android SDK. Could not find Android SDK directory.\n' +
+            'Might need to install Android SDK or set up \'ANDROID_HOME\' env variable.'));
+
+    var gradlePath = module.exports.get_gradle_wrapper();
+    if (gradlePath.length !== 0)
+        d.resolve(gradlePath);
+    else
+        d.reject(new CordovaError('Could not find an installed version of Gradle either in Android Studio,\n' +
+                                'or on your system to install the gradle wrapper. Please include gradle \n' +
+                                'in your path, or install Android Studio'));
+    return d.promise;
+};
+
+// Returns a promise.
+module.exports.check_java = function() {
+    var javacPath = forgivingWhichSync('javac');
+    var hasJavaHome = !!process.env['JAVA_HOME'];
+    return Q().then(function() {
+        if (hasJavaHome) {
+            // Windows java installer doesn't add javac to PATH, nor set JAVA_HOME (ugh).
+            if (!javacPath) {
+                process.env['PATH'] += path.delimiter + path.join(process.env['JAVA_HOME'], 'bin');
+            }
+        } else {
+            if (javacPath) {
+                // OS X has a command for finding JAVA_HOME.
+                var find_java = '/usr/libexec/java_home';
+                var default_java_error_msg = 'Failed to find \'JAVA_HOME\' environment variable. Try setting setting it manually.';
+                if (fs.existsSync(find_java)) {
+                    return superspawn.spawn(find_java)
+                    .then(function(stdout) {
+                        process.env['JAVA_HOME'] = stdout.trim();
+                    }).catch(function(err) {
+                        throw new CordovaError(default_java_error_msg);
+                    });
+                } else {
+                    // See if we can derive it from javac's location.
+                    // fs.realpathSync is require on Ubuntu, which symplinks from /usr/bin -> JDK
+                    var maybeJavaHome = path.dirname(path.dirname(javacPath));
+                    if (fs.existsSync(path.join(maybeJavaHome, 'lib', 'tools.jar'))) {
+                        process.env['JAVA_HOME'] = maybeJavaHome;
+                    } else {
+                        throw new CordovaError(default_java_error_msg);
+                    }
+                }
+            } else if (module.exports.isWindows()) {
+                // Try to auto-detect java in the default install paths.
+                var oldSilent = shelljs.config.silent;
+                shelljs.config.silent = true;
+                var firstJdkDir =
+                    shelljs.ls(process.env['ProgramFiles'] + '\\java\\jdk*')[0] ||
+                    shelljs.ls('C:\\Program Files\\java\\jdk*')[0] ||
+                    shelljs.ls('C:\\Program Files (x86)\\java\\jdk*')[0];
+                shelljs.config.silent = oldSilent;
+                if (firstJdkDir) {
+                    // shelljs always uses / in paths.
+                    firstJdkDir = firstJdkDir.replace(/\//g, path.sep);
+                    if (!javacPath) {
+                        process.env['PATH'] += path.delimiter + path.join(firstJdkDir, 'bin');
+                    }
+                    process.env['JAVA_HOME'] = firstJdkDir;
+                }
+            }
+        }
+    }).then(function() {
+        var msg =
+            'Failed to run "javac -version", make sure that you have a JDK installed.\n' +
+            'You can get it from: http://www.oracle.com/technetwork/java/javase/downloads.\n';
+        if (process.env['JAVA_HOME']) {
+            msg += 'Your JAVA_HOME is invalid: ' + process.env['JAVA_HOME'] + '\n';
+        }
+        // We use tryCommand with catchStderr = true, because
+        // javac writes version info to stderr instead of stdout
+        return tryCommand('javac -version', msg, true)
+        .then(function (output) {
+            //Let's check for at least Java 8, and keep it future proof so we can support Java 10
+            var match = /javac ((?:1\.)(?:[8-9]\.)(?:\d+))|((?:1\.)(?:[1-9]\d+\.)(?:\d+))/i.exec(output);
+            return match && match[1];
+        });
+    });
+};
+
+// Returns a promise.
+module.exports.check_android = function() {
+    return Q().then(function() {
+        var androidCmdPath = forgivingWhichSync('android');
+        var adbInPath = forgivingWhichSync('adb');
+        var avdmanagerInPath = forgivingWhichSync('avdmanager');
+        var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
+        function maybeSetAndroidHome(value) {
+            if (!hasAndroidHome && fs.existsSync(value)) {
+                hasAndroidHome = true;
+                process.env['ANDROID_HOME'] = value;
+            }
+        }
+        // First ensure ANDROID_HOME is set
+        // If we have no hints (nothing in PATH), try a few default locations
+        if (!hasAndroidHome && !androidCmdPath && !adbInPath && !avdmanagerInPath) {
+            if (module.exports.isWindows()) {
+                // Android Studio 1.0 installer
+                maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'sdk'));
+                maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'sdk'));
+                // Android Studio pre-1.0 installer
+                maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'android-studio', 'sdk'));
+                maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'android-studio', 'sdk'));
+                // Stand-alone installer
+                maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'android-sdk'));
+                maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'android-sdk'));
+            } else if (module.exports.isDarwin()) {
+                // Android Studio 1.0 installer
+                maybeSetAndroidHome(path.join(process.env['HOME'], 'Library', 'Android', 'sdk'));
+                // Android Studio pre-1.0 installer
+                maybeSetAndroidHome('/Applications/Android Studio.app/sdk');
+                // Stand-alone zip file that user might think to put under /Applications
+                maybeSetAndroidHome('/Applications/android-sdk-macosx');
+                maybeSetAndroidHome('/Applications/android-sdk');
+            }
+            if (process.env['HOME']) {
+                // Stand-alone zip file that user might think to put under their home directory
+                maybeSetAndroidHome(path.join(process.env['HOME'], 'android-sdk-macosx'));
+                maybeSetAndroidHome(path.join(process.env['HOME'], 'android-sdk'));
+            }
+        }
+        if (!hasAndroidHome) {
+            // If we dont have ANDROID_HOME, but we do have some tools on the PATH, try to infer from the tooling PATH.
+            var parentDir, grandParentDir;
+            if (androidCmdPath) {
+                parentDir = path.dirname(androidCmdPath);
+                grandParentDir = path.dirname(parentDir);
+                if (path.basename(parentDir) == 'tools' || fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) {
+                    maybeSetAndroidHome(grandParentDir);
+                } else {
+                    throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
+                        'Detected \'android\' command at ' + parentDir + ' but no \'tools\' directory found near.\n' +
+                        'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'tools directory.');
+                }
+            }
+            if (adbInPath) {
+                parentDir = path.dirname(adbInPath);
+                grandParentDir = path.dirname(parentDir);
+                if (path.basename(parentDir) == 'platform-tools') {
+                    maybeSetAndroidHome(grandParentDir);
+                } else {
+                    throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
+                        'Detected \'adb\' command at ' + parentDir + ' but no \'platform-tools\' directory found near.\n' +
+                        'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'platform-tools directory.');
+                }
+            }
+            if (avdmanagerInPath) {
+                parentDir = path.dirname(avdmanagerInPath);
+                grandParentDir = path.dirname(parentDir);
+                if (path.basename(parentDir) == 'bin' && path.basename(grandParentDir) == 'tools') {
+                    maybeSetAndroidHome(path.dirname(grandParentDir));
+                } else {
+                    throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
+                        'Detected \'avdmanager\' command at ' + parentDir + ' but no \'tools' + path.sep + 'bin\' directory found near.\n' +
+                        'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'tools' + path.sep + 'bin directory.');
+                }
+            }
+        }
+        if (!process.env['ANDROID_HOME']) {
+            throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
+                'Failed to find \'android\' command in your \'PATH\'. Try update your \'PATH\' to include path to valid SDK directory.');
+        }
+        if (!fs.existsSync(process.env['ANDROID_HOME'])) {
+            throw new CordovaError('\'ANDROID_HOME\' environment variable is set to non-existent path: ' + process.env['ANDROID_HOME'] +
+                '\nTry update it manually to point to valid SDK directory.');
+        }
+        // Next let's make sure relevant parts of the SDK tooling is in our PATH
+        if (hasAndroidHome && !androidCmdPath) {
+            process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools');
+        }
+        if (hasAndroidHome && !adbInPath) {
+            process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'platform-tools');
+        }
+        if (hasAndroidHome && !avdmanagerInPath) {
+            process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools', 'bin');
+        }
+        return hasAndroidHome;
+    });
+};
+
+// TODO: is this actually needed?
+module.exports.getAbsoluteAndroidCmd = function () {
+    var cmd = forgivingWhichSync('android');
+    if (cmd.length === 0) {
+        cmd = forgivingWhichSync('sdkmanager');
+    }
+    if (module.exports.isWindows()) {
+        return '"' + cmd + '"';
+    }
+    return cmd.replace(/(\s)/g, '\\$1');
+};
+
+module.exports.check_android_target = function(originalError) {
+    // valid_target can look like:
+    //   android-19
+    //   android-L
+    //   Google Inc.:Google APIs:20
+    //   Google Inc.:Glass Development Kit Preview:20
+    var desired_api_level = module.exports.get_target();
+    return android_sdk.list_targets()
+    .then(function(targets) {
+        if (targets.indexOf(desired_api_level) >= 0) {
+            return targets;
+        }
+        var androidCmd = module.exports.getAbsoluteAndroidCmd();
+        var msg = 'Please install Android target / API level: "' + desired_api_level  + '".\n\n' +
+            'Hint: Open the SDK manager by running: ' + androidCmd + '\n' +
+            'You will require:\n' +
+            '1. "SDK Platform" for API level ' + desired_api_level + '\n' +
+            '2. "Android SDK Platform-tools (latest)\n' +
+            '3. "Android SDK Build-tools" (latest)';
+        if (originalError) {
+            msg = originalError + '\n' + msg;
+        }
+        throw new CordovaError(msg);
+    });
+};
+
+// Returns a promise.
+module.exports.run = function() {
+     return Q.all([this.check_java(), this.check_android()])
+     .then(function(values) {
+         console.log('ANDROID_HOME=' + process.env['ANDROID_HOME']);
+         console.log('JAVA_HOME=' + process.env['JAVA_HOME']);
+
+         if (!values[0]) {
+            throw new CordovaError('Requirements check failed for JDK 1.8 or greater');
+         }
+
+         if (!values[1]) {
+            throw new CordovaError('Requirements check failed for Android SDK');
+         }
+     });
+};
+
+
+/**
+ * Object thar represents one of requirements for current platform.
+ * @param {String} id         The unique identifier for this requirements.
+ * @param {String} name       The name of requirements. Human-readable field.
+ * @param {String} version    The version of requirement installed. In some cases could be an array of strings
+ *                            (for example, check_android_target returns an array of android targets installed)
+ * @param {Boolean} installed Indicates whether the requirement is installed or not
+ */
+var Requirement = function (id, name, version, installed) {
+    this.id = id;
+    this.name = name;
+    this.installed = installed || false;
+    this.metadata = {
+        version: version,
+    };
+};
+
+/**
+ * Methods that runs all checks one by one and returns a result of checks
+ * as an array of Requirement objects. This method intended to be used by cordova-lib check_reqs method
+ *
+ * @return Promise<Requirement[]> Array of requirements. Due to implementation, promise is always fulfilled.
+ */
+module.exports.check_all = function() {
+
+    var requirements = [
+        new Requirement('java', 'Java JDK'),
+        new Requirement('androidSdk', 'Android SDK'),
+        new Requirement('androidTarget', 'Android target'),
+        new Requirement('gradle', 'Gradle')
+    ];
+
+    var checkFns = [
+        this.check_java,
+        this.check_android,
+        this.check_android_target,
+        this.check_gradle
+    ];
+
+    // Then execute requirement checks one-by-one
+    return checkFns.reduce(function (promise, checkFn, idx) {
+        // Update each requirement with results
+        var requirement = requirements[idx];
+        return promise.then(checkFn)
+        .then(function (version) {
+            requirement.installed = true;
+            requirement.metadata.version = version;
+        }, function (err) {
+            requirement.metadata.reason = err instanceof Error ? err.message : err;
+        });
+    }, Q())
+    .then(function () {
+        // When chain is completed, return requirements array to upstream API
+        return requirements;
+    });
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/lib/device.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/lib/device.js b/cordova-lib/spec-plugman/projects/android/cordova/lib/device.js
new file mode 100644
index 0000000..4b171db
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/lib/device.js
@@ -0,0 +1,120 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+*/
+
+var Q     = require('q'),
+    build = require('./build');
+var path = require('path');
+var Adb = require('./Adb');
+var AndroidManifest = require('./AndroidManifest');
+var spawn = require('cordova-common').superspawn.spawn;
+var CordovaError = require('cordova-common').CordovaError;
+var events = require('cordova-common').events;
+
+/**
+ * Returns a promise for the list of the device ID's found
+ * @param lookHarder When true, try restarting adb if no devices are found.
+ */
+module.exports.list = function(lookHarder) {
+    return Adb.devices()
+    .then(function(list) {
+        if (list.length === 0 && lookHarder) {
+            // adb kill-server doesn't seem to do the trick.
+            // Could probably find a x-platform version of killall, but I'm not actually
+            // sure that this scenario even happens on non-OSX machines.
+            return spawn('killall', ['adb'])
+            .then(function() {
+                events.emit('verbose', 'Restarting adb to see if more devices are detected.');
+                return Adb.devices();
+            }, function() {
+                // For non-killall OS's.
+                return list;
+            });
+        }
+        return list;
+    });
+};
+
+module.exports.resolveTarget = function(target) {
+    return this.list(true)
+    .then(function(device_list) {
+        if (!device_list || !device_list.length) {
+            return Q.reject(new CordovaError('Failed to deploy to device, no devices found.'));
+        }
+        // default device
+        target = target || device_list[0];
+
+        if (device_list.indexOf(target) < 0) {
+            return Q.reject('ERROR: Unable to find target \'' + target + '\'.');
+        }
+
+        return build.detectArchitecture(target)
+        .then(function(arch) {
+            return { target: target, arch: arch, isEmulator: false };
+        });
+    });
+};
+
+/*
+ * Installs a previously built application on the device
+ * and launches it.
+ * Returns a promise.
+ */
+module.exports.install = function(target, buildResults) {
+    return Q().then(function() {
+        if (target && typeof target == 'object') {
+            return target;
+        }
+        return module.exports.resolveTarget(target);
+    }).then(function(resolvedTarget) {
+        var apk_path = build.findBestApkForArchitecture(buildResults, resolvedTarget.arch);
+        var manifest = new AndroidManifest(path.join(__dirname, '../../AndroidManifest.xml'));
+        var pkgName = manifest.getPackageId();
+        var launchName = pkgName + '/.' + manifest.getActivity().getName();
+        events.emit('log', 'Using apk: ' + apk_path);
+        events.emit('log', 'Package name: ' + pkgName);
+
+        return Adb.install(resolvedTarget.target, apk_path, {replace: true})
+        .catch(function (error) {
+            // CB-9557 CB-10157 only uninstall and reinstall app if the one that
+            // is already installed on device was signed w/different certificate
+            if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString()))
+                throw error;
+
+            events.emit('warn', 'Uninstalling app from device and reinstalling it again because the ' +
+                'installed app already signed with different key');
+
+            // This promise is always resolved, even if 'adb uninstall' fails to uninstall app
+            // or the app doesn't installed at all, so no error catching needed.
+            return Adb.uninstall(resolvedTarget.target, pkgName)
+            .then(function() {
+                return Adb.install(resolvedTarget.target, apk_path, {replace: true});
+            });
+        })
+        .then(function() {
+            //unlock screen
+            return Adb.shell(resolvedTarget.target, 'input keyevent 82');
+        }).then(function() {
+            return Adb.start(resolvedTarget.target, launchName);
+        }).then(function() {
+            events.emit('log', 'LAUNCH SUCCESS');
+        });
+    });
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/lib/emulator.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/lib/emulator.js b/cordova-lib/spec-plugman/projects/android/cordova/lib/emulator.js
new file mode 100644
index 0000000..2f36b9a
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/lib/emulator.js
@@ -0,0 +1,540 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+*/
+
+/* jshint sub:true */
+
+var retry      = require('./retry');
+var build      = require('./build');
+var path = require('path');
+var Adb = require('./Adb');
+var AndroidManifest = require('./AndroidManifest');
+var events = require('cordova-common').events;
+var superspawn = require('cordova-common').superspawn;
+var CordovaError = require('cordova-common').CordovaError;
+var shelljs = require('shelljs');
+var android_sdk = require('./android_sdk');
+var check_reqs = require('./check_reqs');
+
+var Q             = require('q');
+var os            = require('os');
+var fs            = require('fs');
+var child_process = require('child_process');
+
+// constants
+var ONE_SECOND              = 1000; // in milliseconds
+var ONE_MINUTE              = 60 * ONE_SECOND; // in milliseconds
+var INSTALL_COMMAND_TIMEOUT = 5 * ONE_MINUTE; // in milliseconds
+var NUM_INSTALL_RETRIES     = 3;
+var CHECK_BOOTED_INTERVAL   = 3 * ONE_SECOND; // in milliseconds
+var EXEC_KILL_SIGNAL        = 'SIGKILL';
+
+function forgivingWhichSync(cmd) {
+    try {
+        return fs.realpathSync(shelljs.which(cmd));
+    } catch (e) {
+        return '';
+    }
+}
+
+module.exports.list_images_using_avdmanager = function () {
+    return superspawn.spawn('avdmanager', ['list', 'avd'])
+    .then(function(output) {
+        var response = output.split('\n');
+        var emulator_list = [];
+        for (var i = 1; i < response.length; i++) {
+            // To return more detailed information use img_obj
+            var img_obj = {};
+            if (response[i].match(/Name:\s/)) {
+                img_obj['name'] = response[i].split('Name: ')[1].replace('\r', '');
+                if (response[i + 1].match(/Device:\s/)) {
+                    i++;
+                    img_obj['device'] = response[i].split('Device: ')[1].replace('\r', '');
+                }
+                if (response[i + 1].match(/Path:\s/)) {
+                    i++;
+                    img_obj['path'] = response[i].split('Path: ')[1].replace('\r', '');
+                }
+                if (response[i + 1].match(/Target:\s/)) {
+                    i++;
+                    if (response[i + 1].match(/ABI:\s/)) {
+                        img_obj['abi'] = response[i + 1].split('ABI: ')[1].replace('\r', '');
+                    }
+                    // This next conditional just aims to match the old output of `android list avd`
+                    // We do so so that we don't have to change the logic when parsing for the
+                    // best emulator target to spawn (see below in `best_image`)
+                    // This allows us to transitionally support both `android` and `avdmanager` binaries,
+                    // depending on what SDK version the user has
+                    if (response[i + 1].match(/Based\son:\s/)) {
+                        img_obj['target'] = response[i + 1].split('Based on:')[1];
+                        if (img_obj['target'].match(/Tag\/ABI:\s/)) {
+                            img_obj['target'] = img_obj['target'].split('Tag/ABI:')[0].replace('\r', '').trim();
+                            if (img_obj['target'].indexOf('(') > -1) {
+                                img_obj['target'] = img_obj['target'].substr(0, img_obj['target'].indexOf('(') - 1).trim();
+                            }
+                        }
+                        var version_string = img_obj['target'].replace(/Android\s+/, '');
+
+                        var api_level = android_sdk.version_string_to_api_level[version_string];
+                        if (api_level) {
+                            img_obj['target'] += ' (API level ' + api_level + ')';
+                        }
+                    }
+                }
+                if (response[i + 1].match(/Skin:\s/)) {
+                    i++;
+                    img_obj['skin'] = response[i].split('Skin: ')[1].replace('\r', '');
+                }
+
+                emulator_list.push(img_obj);
+            }
+            /* To just return a list of names use this
+            if (response[i].match(/Name:\s/)) {
+                emulator_list.push(response[i].split('Name: ')[1].replace('\r', '');
+            }*/
+
+        }
+        return emulator_list;
+    });
+};
+
+module.exports.list_images_using_android = function() {
+    return superspawn.spawn('android', ['list', 'avds'])
+    .then(function(output) {
+        var response = output.split('\n');
+        var emulator_list = [];
+        for (var i = 1; i < response.length; i++) {
+            // To return more detailed information use img_obj
+            var img_obj = {};
+            if (response[i].match(/Name:\s/)) {
+                img_obj['name'] = response[i].split('Name: ')[1].replace('\r', '');
+                if (response[i + 1].match(/Device:\s/)) {
+                    i++;
+                    img_obj['device'] = response[i].split('Device: ')[1].replace('\r', '');
+                }
+                if (response[i + 1].match(/Path:\s/)) {
+                    i++;
+                    img_obj['path'] = response[i].split('Path: ')[1].replace('\r', '');
+                }
+                if (response[i + 1].match(/\(API\slevel\s/) || (response[i + 2] && response[i + 2].match(/\(API\slevel\s/))) {
+                    i++;
+                    var secondLine = response[i + 1].match(/\(API\slevel\s/) ? response[i + 1] : '';
+                    img_obj['target'] = (response[i] + secondLine).split('Target: ')[1].replace('\r', '');
+                }
+                if (response[i + 1].match(/ABI:\s/)) {
+                    i++;
+                    img_obj['abi'] = response[i].split('ABI: ')[1].replace('\r', '');
+                }
+                if (response[i + 1].match(/Skin:\s/)) {
+                    i++;
+                    img_obj['skin'] = response[i].split('Skin: ')[1].replace('\r', '');
+                }
+
+                emulator_list.push(img_obj);
+            }
+            /* To just return a list of names use this
+            if (response[i].match(/Name:\s/)) {
+                emulator_list.push(response[i].split('Name: ')[1].replace('\r', '');
+            }*/
+
+        }
+        return emulator_list;
+    });
+};
+
+/**
+ * Returns a Promise for a list of emulator images in the form of objects
+ * {
+       name   : <emulator_name>,
+       device : <device>,
+       path   : <path_to_emulator_image>,
+       target : <api_target>,
+       abi    : <cpu>,
+       skin   : <skin>
+   }
+ */
+module.exports.list_images = function() {
+    if (forgivingWhichSync('android')) {
+        return module.exports.list_images_using_android()
+        .catch(function(err) {
+            // try to use `avdmanager` in case `android` reports it is no longer available.
+            // this likely means the target machine is using a newer version of
+            // the android sdk, and possibly `avdmanager` is available.
+            if (err.code == 1 && err.stdout.indexOf('android command is no longer available')) {
+                return module.exports.list_images_using_avdmanager();
+            } else {
+                throw err;
+            }
+        });
+    } else if (forgivingWhichSync('avdmanager')) {
+        return module.exports.list_images_using_avdmanager();
+    } else {
+        return Q().then(function() {
+            throw new CordovaError('Could not find either `android` or `avdmanager` on your $PATH! Are you sure the Android SDK is installed and available?');
+        });
+    }
+};
+
+/**
+ * Will return the closest avd to the projects target
+ * or undefined if no avds exist.
+ * Returns a promise.
+ */
+module.exports.best_image = function() {
+    return this.list_images()
+    .then(function(images) {
+        // Just return undefined if there is no images
+        if (images.length === 0) return;
+
+        var closest = 9999;
+        var best = images[0];
+        var project_target = check_reqs.get_target().replace('android-', '');
+        for (var i in images) {
+            var target = images[i].target;
+            if(target) {
+                var num = target.split('(API level ')[1].replace(')', '');
+                if (num == project_target) {
+                    return images[i];
+                } else if (project_target - num < closest && project_target > num) {
+                    closest = project_target - num;
+                    best = images[i];
+                }
+            }
+        }
+        return best;
+    });
+};
+
+// Returns a promise.
+module.exports.list_started = function() {
+    return Adb.devices({emulators: true});
+};
+
+// Returns a promise.
+module.exports.list_targets = function() {
+    return superspawn.spawn('android', ['list', 'targets'], {cwd: os.tmpdir()})
+    .then(function(output) {
+        var target_out = output.split('\n');
+        var targets = [];
+        for (var i = target_out.length; i >= 0; i--) {
+            if(target_out[i].match(/id:/)) {
+                targets.push(targets[i].split(' ')[1]);
+            }
+        }
+        return targets;
+    });
+};
+
+/*
+ * Gets unused port for android emulator, between 5554 and 5584
+ * Returns a promise.
+ */
+module.exports.get_available_port = function () {
+    var self = this;
+
+    return self.list_started()
+    .then(function (emulators) {
+        for (var p = 5584; p >= 5554; p-=2) {
+            if (emulators.indexOf('emulator-' + p) === -1) {
+                events.emit('verbose', 'Found available port: ' + p);
+                return p;
+            }
+        }
+        throw new CordovaError('Could not find an available avd port');
+    });
+};
+
+/*
+ * Starts an emulator with the given ID,
+ * and returns the started ID of that emulator.
+ * If no ID is given it will use the first image available,
+ * if no image is available it will error out (maybe create one?).
+ * If no boot timeout is given or the value is negative it will wait forever for
+ * the emulator to boot
+ *
+ * Returns a promise.
+ */
+module.exports.start = function(emulator_ID, boot_timeout) {
+    var self = this;
+
+    return Q().then(function() {
+        if (emulator_ID) return Q(emulator_ID);
+
+        return self.best_image()
+        .then(function(best) {
+            if (best && best.name) {
+                events.emit('warn', 'No emulator specified, defaulting to ' + best.name);
+                return best.name;
+            }
+
+            var androidCmd = check_reqs.getAbsoluteAndroidCmd();
+            return Q.reject(new CordovaError('No emulator images (avds) found.\n' +
+                '1. Download desired System Image by running: ' + androidCmd + ' sdk\n' +
+                '2. Create an AVD by running: ' + androidCmd + ' avd\n' +
+                'HINT: For a faster emulator, use an Intel System Image and install the HAXM device driver\n'));
+        });
+    }).then(function(emulatorId) {
+        return self.get_available_port()
+        .then(function (port) {
+            // Figure out the directory the emulator binary runs in, and set the cwd to that directory.
+            // Workaround for https://code.google.com/p/android/issues/detail?id=235461
+            var emulator_dir = path.dirname(shelljs.which('emulator'));
+            var args = ['-avd', emulatorId, '-port', port];
+            // Don't wait for it to finish, since the emulator will probably keep running for a long time.
+            child_process
+                .spawn('emulator', args, { stdio: 'inherit', detached: true, cwd: emulator_dir })
+                .unref();
+
+            // wait for emulator to start
+            events.emit('log', 'Waiting for emulator to start...');
+            return self.wait_for_emulator(port);
+        });
+    }).then(function(emulatorId) {
+        if (!emulatorId)
+            return Q.reject(new CordovaError('Failed to start emulator'));
+
+        //wait for emulator to boot up
+        process.stdout.write('Waiting for emulator to boot (this may take a while)...');
+        return self.wait_for_boot(emulatorId, boot_timeout)
+        .then(function(success) {
+            if (success) {
+                events.emit('log','BOOT COMPLETE');
+                //unlock screen
+                return Adb.shell(emulatorId, 'input keyevent 82')
+                .then(function() {
+                    //return the new emulator id for the started emulators
+                    return emulatorId;
+                });
+            } else {
+                // We timed out waiting for the boot to happen
+                return null;
+            }
+        });
+    });
+};
+
+/*
+ * Waits for an emulator to boot on a given port.
+ * Returns this emulator's ID in a promise.
+ */
+module.exports.wait_for_emulator = function(port) {
+    var self = this;
+    return Q().then(function() {
+        var emulator_id = 'emulator-' + port;
+        return Adb.shell(emulator_id, 'getprop dev.bootcomplete')
+        .then(function (output) {
+            if (output.indexOf('1') >= 0) {
+                return emulator_id;
+            }
+            return self.wait_for_emulator(port);
+        }, function (error) {
+            if (error && error.message &&
+            (error.message.indexOf('not found') > -1) ||
+            error.message.indexOf('device offline') > -1) {
+                // emulator not yet started, continue waiting
+                return self.wait_for_emulator(port);
+            } else {
+                // something unexpected has happened
+                throw error;
+            }
+        });
+     });
+};
+
+/*
+ * Waits for the core android process of the emulator to start. Returns a
+ * promise that resolves to a boolean indicating success. Not specifying a
+ * time_remaining or passing a negative value will cause it to wait forever
+ */
+module.exports.wait_for_boot = function(emulator_id, time_remaining) {
+    var self = this;
+    return Adb.shell(emulator_id, 'ps')
+    .then(function(output) {
+        if (output.match(/android\.process\.acore/)) {
+            return true;
+        } else if (time_remaining === 0) {
+            return false;
+        } else {
+            process.stdout.write('.');
+
+            // Check at regular intervals
+            return Q.delay(time_remaining < CHECK_BOOTED_INTERVAL ? time_remaining : CHECK_BOOTED_INTERVAL).then(function() {
+                var updated_time = time_remaining >= 0 ? Math.max(time_remaining - CHECK_BOOTED_INTERVAL, 0) : time_remaining;
+                return self.wait_for_boot(emulator_id, updated_time);
+            });
+        }
+    });
+};
+
+/*
+ * Create avd
+ * TODO : Enter the stdin input required to complete the creation of an avd.
+ * Returns a promise.
+ */
+module.exports.create_image = function(name, target) {
+    console.log('Creating new avd named ' + name);
+    if (target) {
+        return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', target])
+        .then(null, function(error) {
+            console.error('ERROR : Failed to create emulator image : ');
+            console.error(' Do you have the latest android targets including ' + target + '?');
+            console.error(error);
+        });
+    } else {
+        console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.');
+        return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]])
+        .then(function() {
+            // TODO: This seems like another error case, even though it always happens.
+            console.error('ERROR : Unable to create an avd emulator, no targets found.');
+            console.error('Ensure you have targets available by running the "android" command');
+            return Q.reject();
+        }, function(error) {
+            console.error('ERROR : Failed to create emulator image : ');
+            console.error(error);
+        });
+    }
+};
+
+module.exports.resolveTarget = function(target) {
+    return this.list_started()
+    .then(function(emulator_list) {
+        if (emulator_list.length < 1) {
+            return Q.reject('No running Android emulators found, please start an emulator before deploying your project.');
+        }
+
+        // default emulator
+        target = target || emulator_list[0];
+        if (emulator_list.indexOf(target) < 0) {
+            return Q.reject('Unable to find target \'' + target + '\'. Failed to deploy to emulator.');
+        }
+
+        return build.detectArchitecture(target)
+        .then(function(arch) {
+            return {target:target, arch:arch, isEmulator:true};
+        });
+    });
+};
+
+/*
+ * Installs a previously built application on the emulator and launches it.
+ * If no target is specified, then it picks one.
+ * If no started emulators are found, error out.
+ * Returns a promise.
+ */
+module.exports.install = function(givenTarget, buildResults) {
+
+    var target;
+    var manifest = new AndroidManifest(path.join(__dirname, '../../AndroidManifest.xml'));
+    var pkgName = manifest.getPackageId();
+
+    // resolve the target emulator
+    return Q().then(function () {
+        if (givenTarget && typeof givenTarget == 'object') {
+            return givenTarget;
+        } else {
+            return module.exports.resolveTarget(givenTarget);
+        }
+
+    // set the resolved target
+    }).then(function (resolvedTarget) {
+        target = resolvedTarget;
+
+    // install the app
+    }).then(function () {
+        // This promise is always resolved, even if 'adb uninstall' fails to uninstall app
+        // or the app doesn't installed at all, so no error catching needed.
+        return Q.when()
+        .then(function() {
+
+            var apk_path = build.findBestApkForArchitecture(buildResults, target.arch);
+            var execOptions = {
+                cwd: os.tmpdir(),
+                timeout:    INSTALL_COMMAND_TIMEOUT, // in milliseconds
+                killSignal: EXEC_KILL_SIGNAL
+            };
+
+            events.emit('log', 'Using apk: ' + apk_path);
+            events.emit('log', 'Package name: ' + pkgName);
+            events.emit('verbose', 'Installing app on emulator...');
+
+            // A special function to call adb install in specific environment w/ specific options.
+            // Introduced as a part of fix for http://issues.apache.org/jira/browse/CB-9119
+            // to workaround sporadic emulator hangs
+            function adbInstallWithOptions(target, apk, opts) {
+                events.emit('verbose', 'Installing apk ' + apk + ' on ' + target + '...');
+
+                var command = 'adb -s ' + target + ' install -r "' + apk + '"';
+                return Q.promise(function (resolve, reject) {
+                    child_process.exec(command, opts, function(err, stdout, stderr) {
+                        if (err) reject(new CordovaError('Error executing "' + command + '": ' + stderr));
+                        // adb does not return an error code even if installation fails. Instead it puts a specific
+                        // message to stdout, so we have to use RegExp matching to detect installation failure.
+                        else if (/Failure/.test(stdout)) {
+                            if (stdout.match(/INSTALL_PARSE_FAILED_NO_CERTIFICATES/)) {
+                                stdout += 'Sign the build using \'-- --keystore\' or \'--buildConfig\'' +
+                                    ' or sign and deploy the unsigned apk manually using Android tools.';
+                            } else if (stdout.match(/INSTALL_FAILED_VERSION_DOWNGRADE/)) {
+                                stdout += 'You\'re trying to install apk with a lower versionCode that is already installed.' +
+                                    '\nEither uninstall an app or increment the versionCode.';
+                            }
+
+                            reject(new CordovaError('Failed to install apk to emulator: ' + stdout));
+                        } else resolve(stdout);
+                    });
+                });
+            }
+
+            function installPromise () {
+                return adbInstallWithOptions(target.target, apk_path, execOptions)
+                .catch(function (error) {
+                    // CB-9557 CB-10157 only uninstall and reinstall app if the one that
+                    // is already installed on device was signed w/different certificate
+                    if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString()))
+                        throw error;
+
+                    events.emit('warn', 'Uninstalling app from device and reinstalling it because the ' +
+                        'currently installed app was signed with different key');
+
+                    // This promise is always resolved, even if 'adb uninstall' fails to uninstall app
+                    // or the app doesn't installed at all, so no error catching needed.
+                    return Adb.uninstall(target.target, pkgName)
+                    .then(function() {
+                        return adbInstallWithOptions(target.target, apk_path, execOptions);
+                    });
+                });
+            }
+
+            return retry.retryPromise(NUM_INSTALL_RETRIES, installPromise)
+            .then(function (output) {
+                events.emit('log', 'INSTALL SUCCESS');
+            });
+        });
+    // unlock screen
+    }).then(function () {
+
+        events.emit('verbose', 'Unlocking screen...');
+        return Adb.shell(target.target, 'input keyevent 82');
+    }).then(function () {
+        Adb.start(target.target, pkgName + '/.' + manifest.getActivity().getName());
+    // report success or failure
+    }).then(function (output) {
+        events.emit('log', 'LAUNCH SUCCESS');
+    });
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/lib/install-device
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/lib/install-device b/cordova-lib/spec-plugman/projects/android/cordova/lib/install-device
new file mode 100755
index 0000000..fc4b784
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/lib/install-device
@@ -0,0 +1,42 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+*/
+
+var device = require('./device'),
+    args   = process.argv;
+
+if(args.length > 2) {
+    var install_target;
+    if (args[2].substring(0, 9) == '--target=') {
+        install_target = args[2].substring(9, args[2].length);
+        device.install(install_target).done(null, function(err) {
+            console.error('ERROR: ' + err);
+            process.exit(2);
+        });
+     } else {
+        console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
+        process.exit(2);
+     }
+} else {
+    device.install().done(null, function(err) {
+        console.error('ERROR: ' + err);
+        process.exit(2);
+    });
+}

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/lib/install-device.bat
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/lib/install-device.bat b/cordova-lib/spec-plugman/projects/android/cordova/lib/install-device.bat
new file mode 100644
index 0000000..ac7214a
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/lib/install-device.bat
@@ -0,0 +1,26 @@
+:: Licensed to the Apache Software Foundation (ASF) under one
+:: or more contributor license agreements.  See the NOTICE file
+:: distributed with this work for additional information
+:: regarding copyright ownership.  The ASF licenses this file
+:: to you under the Apache License, Version 2.0 (the
+:: "License"); you may not use this file except in compliance
+:: with the License.  You may obtain a copy of the License at
+:: 
+:: http://www.apache.org/licenses/LICENSE-2.0
+:: 
+:: Unless required by applicable law or agreed to in writing,
+:: software distributed under the License is distributed on an
+:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+:: KIND, either express or implied.  See the License for the
+:: specific language governing permissions and limitations
+:: under the License.
+
+@ECHO OFF
+SET script_path="%~dp0install-device"
+IF EXIST %script_path% (
+        node "%script_path%" %*
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'install-device' script in 'cordova\lib' folder, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/lib/install-emulator
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/lib/install-emulator b/cordova-lib/spec-plugman/projects/android/cordova/lib/install-emulator
new file mode 100755
index 0000000..aa2a34f
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/lib/install-emulator
@@ -0,0 +1,38 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+*/
+
+var emulator = require('./emulator'),
+    args     = process.argv;
+
+var install_target;
+if(args.length > 2) {
+    if (args[2].substring(0, 9) == '--target=') {
+        install_target = args[2].substring(9, args[2].length);
+     } else {
+        console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
+        process.exit(2);
+     }
+}
+
+emulator.install(install_target).done(null, function(err) {
+    console.error('ERROR: ' + err);
+    process.exit(2);
+});

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/lib/install-emulator.bat
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/lib/install-emulator.bat b/cordova-lib/spec-plugman/projects/android/cordova/lib/install-emulator.bat
new file mode 100644
index 0000000..1ec6779
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/lib/install-emulator.bat
@@ -0,0 +1,26 @@
+:: Licensed to the Apache Software Foundation (ASF) under one
+:: or more contributor license agreements.  See the NOTICE file
+:: distributed with this work for additional information
+:: regarding copyright ownership.  The ASF licenses this file
+:: to you under the Apache License, Version 2.0 (the
+:: "License"); you may not use this file except in compliance
+:: with the License.  You may obtain a copy of the License at
+:: 
+:: http://www.apache.org/licenses/LICENSE-2.0
+:: 
+:: Unless required by applicable law or agreed to in writing,
+:: software distributed under the License is distributed on an
+:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+:: KIND, either express or implied.  See the License for the
+:: specific language governing permissions and limitations
+:: under the License.
+
+@ECHO OFF
+SET script_path="%~dp0install-emulator"
+IF EXIST %script_path% (
+        node "%script_path%" %*
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'install-emulator' script in 'cordova\lib' folder, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/lib/list-devices
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/lib/list-devices b/cordova-lib/spec-plugman/projects/android/cordova/lib/list-devices
new file mode 100755
index 0000000..8e22c7f
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/lib/list-devices
@@ -0,0 +1,34 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+*/
+
+var devices = require('./device');
+
+// Usage support for when args are given
+require('./check_reqs').check_android().then(function() {
+    devices.list().done(function(device_list) {
+        device_list && device_list.forEach(function(dev) {
+            console.log(dev);
+        });
+    }, function(err) {
+        console.error('ERROR: ' + err);
+        process.exit(2);
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/lib/list-devices.bat
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/lib/list-devices.bat b/cordova-lib/spec-plugman/projects/android/cordova/lib/list-devices.bat
new file mode 100644
index 0000000..c0bcdd9
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/lib/list-devices.bat
@@ -0,0 +1,26 @@
+:: Licensed to the Apache Software Foundation (ASF) under one
+:: or more contributor license agreements.  See the NOTICE file
+:: distributed with this work for additional information
+:: regarding copyright ownership.  The ASF licenses this file
+:: to you under the Apache License, Version 2.0 (the
+:: "License"); you may not use this file except in compliance
+:: with the License.  You may obtain a copy of the License at
+:: 
+:: http://www.apache.org/licenses/LICENSE-2.0
+:: 
+:: Unless required by applicable law or agreed to in writing,
+:: software distributed under the License is distributed on an
+:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+:: KIND, either express or implied.  See the License for the
+:: specific language governing permissions and limitations
+:: under the License.
+
+@ECHO OFF
+SET script_path="%~dp0list-devices"
+IF EXIST %script_path% (
+        node "%script_path%" %*
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'list-devices' script in 'cordova\lib' folder, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/lib/list-emulator-images
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/lib/list-emulator-images b/cordova-lib/spec-plugman/projects/android/cordova/lib/list-emulator-images
new file mode 100755
index 0000000..25e5c81
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/lib/list-emulator-images
@@ -0,0 +1,34 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+*/
+
+var emulators = require('./emulator');
+
+// Usage support for when args are given
+require('./check_reqs').check_android().then(function() {
+    emulators.list_images().done(function(emulator_list) {
+        emulator_list && emulator_list.forEach(function(emu) {
+            console.log(emu.name);
+        });
+    }, function(err) {
+        console.error('ERROR: ' + err);
+        process.exit(2);
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/lib/list-emulator-images.bat
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/lib/list-emulator-images.bat b/cordova-lib/spec-plugman/projects/android/cordova/lib/list-emulator-images.bat
new file mode 100644
index 0000000..661cbf9
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/lib/list-emulator-images.bat
@@ -0,0 +1,26 @@
+:: Licensed to the Apache Software Foundation (ASF) under one
+:: or more contributor license agreements.  See the NOTICE file
+:: distributed with this work for additional information
+:: regarding copyright ownership.  The ASF licenses this file
+:: to you under the Apache License, Version 2.0 (the
+:: "License"); you may not use this file except in compliance
+:: with the License.  You may obtain a copy of the License at
+:: 
+:: http://www.apache.org/licenses/LICENSE-2.0
+:: 
+:: Unless required by applicable law or agreed to in writing,
+:: software distributed under the License is distributed on an
+:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+:: KIND, either express or implied.  See the License for the
+:: specific language governing permissions and limitations
+:: under the License.
+
+@ECHO OFF
+SET script_path="%~dp0list-emulator-images"
+IF EXIST %script_path% (
+        node "%script_path%" %*
+) ELSE (
+    ECHO. 
+    ECHO ERROR: Could not find 'list-emulator-images' script in 'cordova\lib' folder, aborting...>&2
+    EXIT /B 1
+)

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/lib/list-started-emulators
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/lib/list-started-emulators b/cordova-lib/spec-plugman/projects/android/cordova/lib/list-started-emulators
new file mode 100755
index 0000000..43ebda2
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/lib/list-started-emulators
@@ -0,0 +1,34 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+*/
+
+var emulators = require('./emulator');
+
+// Usage support for when args are given
+require('./check_reqs').check_android().then(function() {
+    emulators.list_started().done(function(emulator_list) {
+        emulator_list && emulator_list.forEach(function(emu) {
+            console.log(emu);
+        });
+    }, function(err) {
+        console.error('ERROR: ' + err);
+        process.exit(2);
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/lib/list-started-emulators.bat
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/lib/list-started-emulators.bat b/cordova-lib/spec-plugman/projects/android/cordova/lib/list-started-emulators.bat
new file mode 100644
index 0000000..a4e88f7
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/lib/list-started-emulators.bat
@@ -0,0 +1,26 @@
+:: Licensed to the Apache Software Foundation (ASF) under one
+:: or more contributor license agreements.  See the NOTICE file
+:: distributed with this work for additional information
+:: regarding copyright ownership.  The ASF licenses this file
+:: to you under the Apache License, Version 2.0 (the
+:: "License"); you may not use this file except in compliance
+:: with the License.  You may obtain a copy of the License at
+:: 
+:: http://www.apache.org/licenses/LICENSE-2.0
+:: 
+:: Unless required by applicable law or agreed to in writing,
+:: software distributed under the License is distributed on an
+:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+:: KIND, either express or implied.  See the License for the
+:: specific language governing permissions and limitations
+:: under the License.
+
+@ECHO OFF
+SET script_path="%~dp0list-started-emulators"
+IF EXIST %script_path% (
+        node "%script_path%" %*
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'list-started-emulators' script in 'cordova\lib' folder, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/lib/log.js
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/lib/log.js b/cordova-lib/spec-plugman/projects/android/cordova/lib/log.js
new file mode 100644
index 0000000..ebf836d
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/lib/log.js
@@ -0,0 +1,56 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+*/
+
+var path  = require('path'),
+    os  = require('os'),
+    Q     = require('q'),
+    child_process = require('child_process'),
+    ROOT  = path.join(__dirname, '..', '..');
+
+/*
+ * Starts running logcat in the shell.
+ * Returns a promise.
+ */
+module.exports.run = function() {
+    var d = Q.defer();
+    var adb = child_process.spawn('adb', ['logcat'], {cwd: os.tmpdir()});
+
+    adb.stdout.on('data', function(data) {
+        var lines = data ? data.toString().split('\n') : [];
+        var out = lines.filter(function(x) { return x.indexOf('nativeGetEnabledTags') < 0; });
+        console.log(out.join('\n'));
+    });
+
+    adb.stderr.on('data', console.error);
+    adb.on('close', function(code) {
+        if (code > 0) {
+            d.reject('Failed to run logcat command.');
+        } else d.resolve();
+    });
+
+    return d.promise;
+};
+
+module.exports.help = function() {
+    console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'log')));
+    console.log('Gives the logcat output on the command line.');
+    process.exit(0);
+};

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/lib/plugin-build.gradle
----------------------------------------------------------------------
diff --git a/cordova-lib/spec-plugman/projects/android/cordova/lib/plugin-build.gradle b/cordova-lib/spec-plugman/projects/android/cordova/lib/plugin-build.gradle
new file mode 100644
index 0000000..d1c6336
--- /dev/null
+++ b/cordova-lib/spec-plugman/projects/android/cordova/lib/plugin-build.gradle
@@ -0,0 +1,70 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+   or more contributor license agreements.  See the NOTICE file
+   distributed with this work for additional information
+   regarding copyright ownership.  The ASF licenses this file
+   to you under the Apache License, Version 2.0 (the
+   "License"); you may not use this file except in compliance
+   with the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing,
+   software distributed under the License is distributed on an
+   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+   KIND, either express or implied.  See the License for the
+   specific language governing permissions and limitations
+   under the License.
+*/
+
+// GENERATED FILE! DO NOT EDIT!
+
+buildscript {
+    repositories {
+        mavenCentral()
+        jcenter()
+    }
+
+    // Switch the Android Gradle plugin version requirement depending on the
+    // installed version of Gradle. This dependency is documented at
+    // http://tools.android.com/tech-docs/new-build-system/version-compatibility
+    // and https://issues.apache.org/jira/browse/CB-8143
+    dependencies {
+        classpath 'com.android.tools.build:gradle:1.0.0+'
+    }
+}
+
+apply plugin: 'com.android.library'
+
+dependencies {
+    compile fileTree(dir: 'libs', include: '*.jar')
+    debugCompile project(path: ":CordovaLib", configuration: "debug")
+    releaseCompile project(path: ":CordovaLib", configuration: "release")
+}
+
+android {
+    compileSdkVersion cdvCompileSdkVersion
+    buildToolsVersion cdvBuildToolsVersion
+    publishNonDefault true
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_6
+        targetCompatibility JavaVersion.VERSION_1_6
+    }
+
+    sourceSets {
+        main {
+            manifest.srcFile 'AndroidManifest.xml'
+            java.srcDirs = ['src']
+            resources.srcDirs = ['src']
+            aidl.srcDirs = ['src']
+            renderscript.srcDirs = ['src']
+            res.srcDirs = ['res']
+            assets.srcDirs = ['assets']
+            jniLibs.srcDirs = ['libs']
+        }
+    }
+}
+
+if (file('build-extras.gradle').exists()) {
+    apply from: 'build-extras.gradle'
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org