luma_third_party: Logcat Service Rebuild

The previous OpenSTF service implemented an unstable binary connection
to the adb logcat service. This CL rebuilds the service to spawn a 
logcat child process for a given device and listen for messages from 
stdout.

Change-Id: If32b774bed333b706d6d6ca034b112ccc0988346
diff --git a/crowdstf/lib/units/device/plugins/logcat.js b/crowdstf/lib/units/device/plugins/logcat.js
index c63f208..3645110 100644
--- a/crowdstf/lib/units/device/plugins/logcat.js
+++ b/crowdstf/lib/units/device/plugins/logcat.js
@@ -1,5 +1,6 @@
 var syrup = require('stf-syrup')
 var Promise = require('bluebird')
+var spawn = require('child_process').spawn;
 
 var logger = require('../../../util/logger')
 var wire = require('../../../wire')
@@ -16,70 +17,93 @@
     var plugin = Object.create(null)
     var activeLogcat = null
 
+    var openLogcat = function(serial) {
+      return new Promise(function(resolve, reject) {
+        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');
+          }
+        });
+      });
+    };
+
     plugin.start = function(filters) {
       return group.get()
         .then(function(group) {
           return plugin.stop()
             .then(function() {
               log.info('Starting logcat')
-              return adb.openLogcat(options.serial, {
-                clear: true
-              })
+              return openLogcat(options.serial);
             })
-            .timeout(10000)
             .then(function(logcat) {
-              activeLogcat = logcat
+              activeLogcat = logcat;
 
               function entryListener(entry) {
-                push.send([
-                  group.group
-                , wireutil.envelope(new wire.DeviceLogcatEntryMessage(
-                    options.serial
-                  , entry.date.getTime() / 1000
-                  , entry.pid
-                  , entry.tid
-                  , entry.priority
-                  , entry.tag
-                  , entry.message
-                  ))
-                ])
+                try {
+                  push.send([
+                    group.group,
+                    wireutil.envelope(new wire.DeviceLogcatEntryMessage(
+                      options.serial,
+                      new Date().getTime(),
+                      0,
+                      0,
+                      '',
+                      '',
+                      entry.toString()
+                    ))
+                  ]);
+                } catch (err) {
+                  console.error("Logcat stdout threw", err);
+                }
               }
 
-              logcat.on('entry', entryListener)
-
+              logcat.stdout.on('data', entryListener);
               return plugin.reset(filters)
-            })
-        })
-    }
+            });
+        });
+    };
 
     plugin.stop = Promise.method(function() {
       if (plugin.isRunning()) {
         log.info('Stopping logcat')
-        activeLogcat.end()
+        activeLogcat.kill();
         activeLogcat = null
       }
     })
 
     plugin.reset = Promise.method(function(filters) {
-      if (plugin.isRunning()) {
-        activeLogcat
-          .resetFilters()
-
-        if (filters.length) {
-          activeLogcat.excludeAll()
-          filters.forEach(function(filter) {
-            activeLogcat.include(filter.tag, filter.priority)
-          })
-        }
-      }
-      else {
-        throw new Error('Logcat is not running')
-      }
-    })
+      filters = null;
+    });
 
     plugin.isRunning = function() {
-      return !!activeLogcat
-    }
+      return !!activeLogcat && activeLogcat.kill;
+    };
 
     lifecycle.observe(plugin.stop)
     group.on('leave', plugin.stop)