Merge "Revert "Make NDC not block forever and handle restart.""
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index 5319432..308661f 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -34,8 +34,8 @@
 import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.LinkedList;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
 
 /**
  * Generic connector class for interfacing with a native daemon which uses the
@@ -50,15 +50,11 @@
     private OutputStream mOutputStream;
     private LocalLog mLocalLog;
 
-    private final ResponseQueue mResponseQueue;
+    private final BlockingQueue<NativeDaemonEvent> mResponseQueue;
 
     private INativeDaemonConnectorCallbacks mCallbacks;
     private Handler mCallbackHandler;
 
-    private AtomicInteger mSequenceNumber;
-
-    private static final int DEFAULT_TIMEOUT = 1 * 60 * 1000; /* 1 minute */
-
     /** Lock held whenever communicating with native daemon. */
     private final Object mDaemonLock = new Object();
 
@@ -68,8 +64,7 @@
             int responseQueueSize, String logTag, int maxLogSize) {
         mCallbacks = callbacks;
         mSocket = socket;
-        mResponseQueue = new ResponseQueue(responseQueueSize);
-        mSequenceNumber = new AtomicInteger(0);
+        mResponseQueue = new LinkedBlockingQueue<NativeDaemonEvent>(responseQueueSize);
         TAG = logTag != null ? logTag : "NativeDaemonConnector";
         mLocalLog = new LocalLog(maxLogSize);
     }
@@ -84,7 +79,7 @@
             try {
                 listenToSocket();
             } catch (Exception e) {
-                loge("Error in NativeDaemonConnector: " + e);
+                Slog.e(TAG, "Error in NativeDaemonConnector", e);
                 SystemClock.sleep(5000);
             }
         }
@@ -95,10 +90,12 @@
         String event = (String) msg.obj;
         try {
             if (!mCallbacks.onEvent(msg.what, event, event.split(" "))) {
-                log(String.format("Unhandled event '%s'", event));
+                Slog.w(TAG, String.format(
+                        "Unhandled event '%s'", event));
             }
         } catch (Exception e) {
-            loge("Error handling '" + event + "': " + e);
+            Slog.e(TAG, String.format(
+                    "Error handling '%s'", event), e);
         }
         return true;
     }
@@ -114,9 +111,7 @@
             socket.connect(address);
 
             InputStream inputStream = socket.getInputStream();
-            synchronized (mDaemonLock) {
-                mOutputStream = socket.getOutputStream();
-            }
+            mOutputStream = socket.getOutputStream();
 
             mCallbacks.onDaemonConnected();
 
@@ -125,10 +120,7 @@
 
             while (true) {
                 int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
-                if (count < 0) {
-                    loge("got " + count + " reading with start = " + start);
-                    break;
-                }
+                if (count < 0) break;
 
                 // Add our starting point to the count and reset the start.
                 count += start;
@@ -148,10 +140,14 @@
                                 mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
                                         event.getCode(), event.getRawEvent()));
                             } else {
-                                mResponseQueue.add(event.getCmdNumber(), event);
+                                try {
+                                    mResponseQueue.put(event);
+                                } catch (InterruptedException ex) {
+                                    Slog.e(TAG, "Failed to put response onto queue: " + ex);
+                                }
                             }
                         } catch (IllegalArgumentException e) {
-                            log("Problem parsing message: " + rawEvent + " - " + e);
+                            Slog.w(TAG, "Problem parsing message: " + rawEvent, e);
                         }
 
                         start = i + 1;
@@ -173,16 +169,15 @@
                 }
             }
         } catch (IOException ex) {
-            loge("Communications error: " + ex);
+            Slog.e(TAG, "Communications error", ex);
             throw ex;
         } finally {
             synchronized (mDaemonLock) {
                 if (mOutputStream != null) {
                     try {
-                        loge("closing stream for " + mSocket);
                         mOutputStream.close();
                     } catch (IOException e) {
-                        loge("Failed closing output stream: " + e);
+                        Slog.w(TAG, "Failed closing output stream", e);
                     }
                     mOutputStream = null;
                 }
@@ -193,17 +188,17 @@
                     socket.close();
                 }
             } catch (IOException ex) {
-                loge("Failed closing socket: " + ex);
+                Slog.w(TAG, "Failed closing socket", ex);
             }
         }
     }
 
     /**
-     * Make command for daemon, escaping arguments as needed.
+     * Send command to daemon, escaping arguments as needed.
      *
-     * @return the final command.
+     * @return the final command issued.
      */
-    private StringBuilder makeCommand(String cmd, Object... args)
+    private String sendCommandLocked(String cmd, Object... args)
             throws NativeDaemonConnectorException {
         // TODO: eventually enforce that cmd doesn't contain arguments
         if (cmd.indexOf('\0') >= 0) {
@@ -221,33 +216,22 @@
             appendEscaped(builder, argString);
         }
 
-        return builder;
-    }
-
-    private int sendCommand(StringBuilder builder)
-            throws NativeDaemonConnectorException {
-
-        int sequenceNumber = mSequenceNumber.incrementAndGet();
-
-        builder.insert(0, Integer.toString(sequenceNumber) + " ");
-
-        if (LOGD) log("SND -> {" + builder.toString() + "}");
+        final String unterminated = builder.toString();
+        log("SND -> {" + unterminated + "}");
 
         builder.append('\0');
 
-        synchronized (mDaemonLock) {
-            if (mOutputStream == null) {
-                throw new NativeDaemonConnectorException("missing output stream");
-            } else {
-                try {
-                    mOutputStream.write(builder.toString().getBytes(Charsets.UTF_8));
-                } catch (IOException e) {
-                    throw new NativeDaemonConnectorException("problem sending command", e);
-                }
+        if (mOutputStream == null) {
+            throw new NativeDaemonConnectorException("missing output stream");
+        } else {
+            try {
+                mOutputStream.write(builder.toString().getBytes(Charsets.UTF_8));
+            } catch (IOException e) {
+                throw new NativeDaemonConnectorException("problem sending command", e);
             }
         }
 
-        return sequenceNumber;
+        return unterminated;
     }
 
     /**
@@ -308,41 +292,39 @@
      */
     public NativeDaemonEvent[] executeForList(String cmd, Object... args)
             throws NativeDaemonConnectorException {
-            return execute(DEFAULT_TIMEOUT, cmd, args);
+        synchronized (mDaemonLock) {
+            return executeLocked(cmd, args);
+        }
     }
 
-    /**
-     * Issue the given command to the native daemon and return any
-     * {@linke NativeDaemonEvent@isClassContinue()} responses, including the
-     * final terminal response.
-     *
-     * @throws NativeDaemonConnectorException when problem communicating with
-     *             native daemon, or if the response matches
-     *             {@link NativeDaemonEvent#isClassClientError()} or
-     *             {@link NativeDaemonEvent#isClassServerError()}.
-     */
-    public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args)
+    private NativeDaemonEvent[] executeLocked(String cmd, Object... args)
             throws NativeDaemonConnectorException {
         final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();
-        final StringBuilder sentCommand = makeCommand(cmd, args);
-        final int cmdNumber = sendCommand(sentCommand);
+
+        while (mResponseQueue.size() > 0) {
+            try {
+                log("ignoring {" + mResponseQueue.take() + "}");
+            } catch (Exception e) {}
+        }
+
+        final String sentCommand = sendCommandLocked(cmd, args);
 
         NativeDaemonEvent event = null;
-        cmd = sentCommand.toString();
         do {
-            event = mResponseQueue.remove(cmdNumber, timeout, cmd);
-            if (event == null) {
-                loge("timed-out waiting for response to " + cmdNumber + " " + cmd);
-                throw new NativeDaemonFailureException(cmd, event);
+            try {
+                event = mResponseQueue.take();
+            } catch (InterruptedException e) {
+                Slog.w(TAG, "interrupted waiting for event line");
+                continue;
             }
             events.add(event);
         } while (event.isClassContinue());
 
         if (event.isClassClientError()) {
-            throw new NativeDaemonArgumentException(cmd, event);
+            throw new NativeDaemonArgumentException(sentCommand, event);
         }
         if (event.isClassServerError()) {
-            throw new NativeDaemonFailureException(cmd, event);
+            throw new NativeDaemonFailureException(sentCommand, event);
         }
 
         return events.toArray(new NativeDaemonEvent[events.size()]);
@@ -466,115 +448,10 @@
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         mLocalLog.dump(fd, pw, args);
-        pw.println();
-        mResponseQueue.dump(fd, pw, args);
     }
 
     private void log(String logstring) {
         if (LOGD) Slog.d(TAG, logstring);
         mLocalLog.log(logstring);
     }
-
-    private void loge(String logstring) {
-        Slog.e(TAG, logstring);
-        mLocalLog.log(logstring);
-    }
-
-    private static class ResponseQueue {
-
-        private static class Response {
-            public int cmdNum;
-            public LinkedList<NativeDaemonEvent> responses = new LinkedList<NativeDaemonEvent>();
-            public String request;
-            public Response(int c, String r) {cmdNum = c; request = r;}
-        }
-
-        private final LinkedList<Response> mResponses;
-        private int mMaxCount;
-
-        ResponseQueue(int maxCount) {
-            mResponses = new LinkedList<Response>();
-            mMaxCount = maxCount;
-        }
-
-        public void add(int cmdNum, NativeDaemonEvent response) {
-            Response found = null;
-            synchronized (mResponses) {
-                for (Response r : mResponses) {
-                    if (r.cmdNum == cmdNum) {
-                        found = r;
-                        break;
-                    }
-                }
-                if (found == null) {
-                    // didn't find it - make sure our queue isn't too big before adding
-                    // another..
-                    while (mResponses.size() >= mMaxCount) {
-                        Slog.e("NativeDaemonConnector.ResponseQueue",
-                                "more buffered than allowed: " + mResponses.size() +
-                                " >= " + mMaxCount);
-                        mResponses.remove();
-                    }
-                    found = new Response(cmdNum, null);
-                    mResponses.add(found);
-                }
-                found.responses.add(response);
-            }
-            synchronized (found) {
-                found.notify();
-            }
-        }
-
-        public NativeDaemonEvent remove(int cmdNum, int timeoutMs, String origCmd) {
-            long endTime = SystemClock.uptimeMillis() + timeoutMs;
-            long nowTime;
-            Response found = null;
-            while (true) {
-                synchronized (mResponses) {
-                    for (Response response : mResponses) {
-                        if (response.cmdNum == cmdNum) {
-                            found = response;
-                            // how many response fragments are left
-                            switch (response.responses.size()) {
-                            case 0:  // haven't got any - must wait
-                                break;
-                            case 1:  // last one - remove this from the master list
-                                mResponses.remove(response); // fall through
-                            default: // take one and move on
-                                response.request = origCmd;
-                                return response.responses.remove();
-                            }
-                        }
-                    }
-                    nowTime = SystemClock.uptimeMillis();
-                    if (endTime <= nowTime) {
-                        Slog.e("NativeDaemonConnector.ResponseQueue",
-                                "Timeout waiting for response");
-                        return null;
-                    }
-                    /* pre-allocate so we have something unique to wait on */
-                    if (found == null) {
-                        found = new Response(cmdNum, origCmd);
-                        mResponses.add(found);
-                    }
-                }
-                try {
-                    synchronized (found) {
-                        found.wait(endTime - nowTime);
-                    }
-                } catch (InterruptedException e) {
-                    // loop around to check if we're done or if it's time to stop waiting
-                }
-            }
-        }
-
-        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            pw.println("Pending requests:");
-            synchronized (mResponses) {
-                for (Response response : mResponses) {
-                    pw.println("  Cmd " + response.cmdNum + " - " + response.request);
-                }
-            }
-        }
-    }
 }
diff --git a/services/java/com/android/server/NativeDaemonEvent.java b/services/java/com/android/server/NativeDaemonEvent.java
index 50ba6d1..62084c0 100644
--- a/services/java/com/android/server/NativeDaemonEvent.java
+++ b/services/java/com/android/server/NativeDaemonEvent.java
@@ -28,22 +28,16 @@
     // TODO: keep class ranges in sync with ResponseCode.h
     // TODO: swap client and server error ranges to roughly mirror HTTP spec
 
-    private final int mCmdNumber;
     private final int mCode;
     private final String mMessage;
     private final String mRawEvent;
 
-    private NativeDaemonEvent(int cmdNumber, int code, String message, String rawEvent) {
-        mCmdNumber = cmdNumber;
+    private NativeDaemonEvent(int code, String message, String rawEvent) {
         mCode = code;
         mMessage = message;
         mRawEvent = rawEvent;
     }
 
-    public int getCmdNumber() {
-        return mCmdNumber;
-    }
-
     public int getCode() {
         return mCode;
     }
@@ -116,28 +110,20 @@
      *             from native side.
      */
     public static NativeDaemonEvent parseRawEvent(String rawEvent) {
-        final String[] parsed = rawEvent.split(" ");
-        if (parsed.length < 3) {
+        final int splitIndex = rawEvent.indexOf(' ');
+        if (splitIndex == -1) {
             throw new IllegalArgumentException("unable to find ' ' separator");
         }
 
-        final int cmdNumber;
-        try {
-            cmdNumber = Integer.parseInt(parsed[0]);
-        } catch (NumberFormatException e) {
-            throw new IllegalArgumentException("problem parsing cmdNumber", e);
-        }
-
         final int code;
         try {
-            code = Integer.parseInt(parsed[1]);
+            code = Integer.parseInt(rawEvent.substring(0, splitIndex));
         } catch (NumberFormatException e) {
             throw new IllegalArgumentException("problem parsing code", e);
         }
 
-        final String message = rawEvent.substring(parsed[0].length() + parsed[1].length() + 2);
-
-        return new NativeDaemonEvent(cmdNumber, code, message, rawEvent);
+        final String message = rawEvent.substring(splitIndex + 1);
+        return new NativeDaemonEvent(code, message, rawEvent);
     }
 
     /**