diff --git a/bin/templates/cordova/lib/device.js b/bin/templates/cordova/lib/device.js index e62e3db8..4f9acc8a 100644 --- a/bin/templates/cordova/lib/device.js +++ b/bin/templates/cordova/lib/device.js @@ -89,12 +89,25 @@ module.exports.install = function(target, buildResults) { var pkgName = manifest.getPackageId(); var launchName = pkgName + '/.' + manifest.getActivity().getName(); events.emit('log', 'Using apk: ' + apk_path); - // 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) + + 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() { - return Adb.install(resolvedTarget.target, apk_path, {replace: true}); - }).then(function() { //unlock screen return Adb.shell(resolvedTarget.target, 'input keyevent 82'); }).then(function() { diff --git a/bin/templates/cordova/lib/emulator.js b/bin/templates/cordova/lib/emulator.js index 9e214b19..d0bd6223 100644 --- a/bin/templates/cordova/lib/emulator.js +++ b/bin/templates/cordova/lib/emulator.js @@ -321,7 +321,7 @@ module.exports.install = function(givenTarget, buildResults) { }).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 Adb.uninstall(target.target, pkgName) + return Q.when() .then(function() { var apk_path = build.findBestApkForArchitecture(buildResults, target.arch); @@ -334,28 +334,47 @@ module.exports.install = function(givenTarget, buildResults) { events.emit('log', 'Using apk: ' + apk_path); events.emit('verbose', 'Installing app on emulator...'); - function exec(command, opts) { + // 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)) reject(new CordovaError('Failed to install apk to emulator: ' + stdout)); else resolve(stdout); }); }); } - var retriedInstall = retry.retryPromise( - NUM_INSTALL_RETRIES, - exec, 'adb -s ' + target.target + ' install -r "' + apk_path + '"', execOptions - ); + 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; - return retriedInstall.then(function (output) { - if (output.match(/Failure/)) { - return Q.reject(new CordovaError('Failed to install apk to emulator: ' + output)); - } else { - events.emit('log', 'INSTALL SUCCESS'); - } - }, function (err) { - return Q.reject(new CordovaError('Failed to install apk to emulator: ' + err)); + 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(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