blob: 5edc1108a5a210f41d74c46fa4032f6679a3caab [file] [log] [blame]
var syrup = require('stf-syrup')
var Promise = require('bluebird')
var spawn = require('child_process').spawn;
var logger = require('../../../util/logger')
var wire = require('../../../wire')
var wireutil = require('../../../wire/util')
var lifecycle = require('../../../util/lifecycle')
module.exports = syrup.serial()
.dependency(require('../support/adb'))
.dependency(require('../support/router'))
.dependency(require('../support/push'))
.dependency(require('./group'))
.define(function(options, adb, router, push, group) {
var log = logger.createLogger('device:plugins:logcat')
var plugin = Object.create(null)
var activeLogcat = null
var openLogcat = function(serial, appId) {
return new Promise(function(resolve, reject) {
var launchLogcat = function(exitCode) {
if (exitCode === 0) {
log.info('adb logcat opening stream.');
// Flag -c clears backlogged output.
// Flag -s specifies the device serial.
// Option 2>/dev/null ignores process err messages.
var psClear = spawn('adb', [
'-s',
options.serial,
'logcat',
'-c',
'2>/dev/null'
]);
psClear.on('close', function(exitCode) {
if (exitCode === 0) {
// Flag *:D provide info level Debug and higher.
// Other flags include
// (V)erbose (D)ebug (I)nfo (W)arning (E)rror (F)atal.
var logcat = spawn('adb', ['-s',
serial,
'logcat',
'*:D',
'2>/dev/null'
]);
resolve(logcat);
} else {
reject();
throw Error('Unable to access adb logcat clear process');
}
});
} else {
reject();
throw Error('Unable to access adb app launch process');
}
};
if (appId) {
log.info('adb launching app %s.', appId);
var psAppLaunch = spawn('adb', [
'-s',
options.serial,
'shell',
'monkey',
'-p',
appId,
'-c',
'android.intent.category.LAUNCHER',
'1',
'2>/dev/null'
]);
psAppLaunch.on('close', launchLogcat);
} else {
launchLogcat(0);
}
}).catch(function(err) {
log.error('Cannot get next free app to launch.', err.stack);
});
};
plugin.start = function(filters, appId) {
return group.get()
.then(function(group) {
return plugin.stop()
.then(function() {
log.info('Starting logcat')
return openLogcat(options.serial, appId);
})
.then(function(logcat) {
activeLogcat = logcat;
function entryListener(entry) {
try {
push.send([
group.group,
wireutil.envelope(new wire.DeviceLogcatEntryMessage(
options.serial,
new Date().getTime(),
entry.toString()
))
]);
} catch (err) {
console.error("Logcat stdout threw", err);
}
}
logcat.stdout.on('data', entryListener);
return plugin.reset(filters)
});
});
};
plugin.stop = Promise.method(function(appId) {
if (plugin.isRunning()) {
if (appId) {
log.info('adb killing app %s', appId);
var arrArgs = [
'-s',
options.serial,
'shell',
'am',
'force-stop',
appId,
'2>/dev/null'
];
var psKill = spawn('adb', arrArgs);
psKill.on('close', function(exitCode) {
if (exitCode === 0) {
log.info('adb killed app %s', appId);
} else {
log.error('adb error killing app %s', appId);
}
});
}
log.info('Stopping logcat')
activeLogcat.kill();
activeLogcat = null
}
})
plugin.reset = Promise.method(function(filters) {
filters = null;
});
plugin.isRunning = function() {
return !!activeLogcat && activeLogcat.kill;
};
lifecycle.observe(plugin.stop)
group.on('leave', plugin.stop)
router
.on(wire.LogcatStartMessage, function(channel, message) {
var reply = wireutil.reply(options.serial)
plugin.start(message.filters, message.appId)
.then(function() {
push.send([
channel
, reply.okay('success')
])
})
.catch(function(err) {
log.error('Unable to open logcat', err.stack)
push.send([
channel
, reply.fail('fail')
])
})
})
.on(wire.LogcatApplyFiltersMessage, function(channel, message) {
var reply = wireutil.reply(options.serial)
plugin.reset(message.filters)
.then(function() {
push.send([
channel
, reply.okay('success')
])
})
.catch(function(err) {
log.error('Failed to apply logcat filters', err.stack)
push.send([
channel
, reply.fail('fail')
])
})
})
.on(wire.LogcatStopMessage, function(channel, message) {
var reply = wireutil.reply(options.serial)
plugin.stop(message.appId)
.then(function() {
push.send([
channel
, reply.okay('success')
])
})
.catch(function(err) {
log.error('Failed to stop logcat', err.stack)
push.send([
channel
, reply.fail('fail')
])
})
})
return plugin
})