Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8927d24b authored by Guang Zhu's avatar Guang Zhu Committed by Android Git Automerger
Browse files

am 1c4ea6a2: Merge "Revert "Make NDC not block forever and handle restart.""

* commit '1c4ea6a2':
  Revert "Make NDC not block forever and handle restart."
parents 299471ea 1c4ea6a2
Loading
Loading
Loading
Loading
+52 −175
Original line number Diff line number Diff line
@@ -34,8 +34,8 @@ import java.io.InputStream;
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 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
    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 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
            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 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
            try {
                listenToSocket();
            } catch (Exception e) {
                loge("Error in NativeDaemonConnector: " + e);
                Slog.e(TAG, "Error in NativeDaemonConnector", e);
                SystemClock.sleep(5000);
            }
        }
@@ -95,10 +90,12 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
        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 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
            socket.connect(address);

            InputStream inputStream = socket.getInputStream();
            synchronized (mDaemonLock) {
            mOutputStream = socket.getOutputStream();
            }

            mCallbacks.onDaemonConnected();

@@ -125,10 +120,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo

            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 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
                                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 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
                }
            }
        } 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 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
                    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,21 +216,11 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
            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 {
@@ -245,9 +230,8 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
                throw new NativeDaemonConnectorException("problem sending command", e);
            }
        }
        }

        return sequenceNumber;
        return unterminated;
    }

    /**
@@ -308,41 +292,39 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
     */
    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 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo

    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);
                }
            }
        }
    }
}
+6 −20
Original line number Diff line number Diff line
@@ -28,22 +28,16 @@ public class NativeDaemonEvent {
    // 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 @@ public class NativeDaemonEvent {
     *             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);
    }

    /**