167bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat/*
267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat * Copyright (C) 2007 The Android Open Source Project
367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat *
467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat * Licensed under the Apache License, Version 2.0 (the "License");
567bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat * you may not use this file except in compliance with the License.
667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat * You may obtain a copy of the License at
767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat *
867bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat *      http://www.apache.org/licenses/LICENSE-2.0
967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat *
1067bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat * Unless required by applicable law or agreed to in writing, software
1167bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat * distributed under the License is distributed on an "AS IS" BASIS,
1267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat * See the License for the specific language governing permissions and
1467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat * limitations under the License.
1567bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat */
1667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
1767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehatpackage com.android.server;
1867bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
1967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehatimport android.net.LocalSocket;
20fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkeyimport android.net.LocalSocketAddress;
21e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yehimport android.os.Handler;
22e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yehimport android.os.HandlerThread;
23e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yehimport android.os.Message;
2467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehatimport android.os.SystemClock;
25470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwaltimport android.util.LocalLog;
268a9b22056b13477f59df934928c00c58b5871c95Joe Onoratoimport android.util.Slog;
2767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
2831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkeyimport com.google.android.collect.Lists;
2931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
30470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwaltimport java.nio.charset.Charsets;
31470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwaltimport java.io.FileDescriptor;
3267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehatimport java.io.IOException;
3367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehatimport java.io.InputStream;
3467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehatimport java.io.OutputStream;
35470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwaltimport java.io.PrintWriter;
3667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehatimport java.util.ArrayList;
37470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwaltimport java.util.concurrent.atomic.AtomicInteger;
38ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwaltimport java.util.concurrent.ArrayBlockingQueue;
39ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwaltimport java.util.concurrent.BlockingQueue;
40ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwaltimport java.util.concurrent.TimeUnit;
41470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwaltimport java.util.LinkedList;
4267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
4367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat/**
4431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * Generic connector class for interfacing with a native daemon which uses the
4531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * {@code libsysutils} FrameworkListener protocol.
4667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat */
47fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkeyfinal class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
4831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    private static final boolean LOGD = false;
4967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
5031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    private final String TAG;
5167bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
5231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    private String mSocket;
5331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    private OutputStream mOutputStream;
54470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt    private LocalLog mLocalLog;
55961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root
56470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt    private final ResponseQueue mResponseQueue;
5767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
5831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    private INativeDaemonConnectorCallbacks mCallbacks;
5931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    private Handler mCallbackHandler;
6067bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
61470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt    private AtomicInteger mSequenceNumber;
62470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
63470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt    private static final int DEFAULT_TIMEOUT = 1 * 60 * 1000; /* 1 minute */
645a0c320a820a0ccaafaa87ad858a29bf5d88a8b9Robert Greenwalt    private static final long WARN_EXECUTE_DELAY_MS = 500; /* .5 sec */
65470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
6631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    /** Lock held whenever communicating with native daemon. */
6731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    private final Object mDaemonLock = new Object();
6867bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
6931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    private final int BUFFER_SIZE = 4096;
7067bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
7131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
72470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt            int responseQueueSize, String logTag, int maxLogSize) {
7367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat        mCallbacks = callbacks;
7467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat        mSocket = socket;
75470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        mResponseQueue = new ResponseQueue(responseQueueSize);
76470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        mSequenceNumber = new AtomicInteger(0);
7731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        TAG = logTag != null ? logTag : "NativeDaemonConnector";
78470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt        mLocalLog = new LocalLog(maxLogSize);
7967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat    }
8067bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
81e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh    @Override
8267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat    public void run() {
83e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh        HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler");
84e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh        thread.start();
85e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh        mCallbackHandler = new Handler(thread.getLooper(), this);
8667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
8767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat        while (true) {
8867bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat            try {
8967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat                listenToSocket();
9067bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat            } catch (Exception e) {
91470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                loge("Error in NativeDaemonConnector: " + e);
924c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat                SystemClock.sleep(5000);
9367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat            }
9467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat        }
9567bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat    }
9667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
97e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh    @Override
98e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh    public boolean handleMessage(Message msg) {
99e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh        String event = (String) msg.obj;
100e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh        try {
1012d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) {
102470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                log(String.format("Unhandled event '%s'", event));
103e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh            }
104e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh        } catch (Exception e) {
105470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            loge("Error handling '" + event + "': " + e);
106e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh        }
107e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh        return true;
108e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh    }
109e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh
1104c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat    private void listenToSocket() throws IOException {
111961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root        LocalSocket socket = null;
11267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
11367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat        try {
11467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat            socket = new LocalSocket();
11567bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat            LocalSocketAddress address = new LocalSocketAddress(mSocket,
11667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat                    LocalSocketAddress.Namespace.RESERVED);
11767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
11867bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat            socket.connect(address);
11967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
12067bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat            InputStream inputStream = socket.getInputStream();
121470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            synchronized (mDaemonLock) {
122470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                mOutputStream = socket.getOutputStream();
123470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            }
12467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
125030bc88b4c09a41b0d7dbe249aec55f33d6b8b8aanga            mCallbacks.onDaemonConnected();
126030bc88b4c09a41b0d7dbe249aec55f33d6b8b8aanga
127961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root            byte[] buffer = new byte[BUFFER_SIZE];
128961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root            int start = 0;
12967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
13067bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat            while (true) {
131961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root                int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
132470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                if (count < 0) {
133470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                    loge("got " + count + " reading with start = " + start);
134470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                    break;
135470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                }
13667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
13712da9d7472ae87b841575d5358e19f143d12f900Kenny Root                // Add our starting point to the count and reset the start.
13812da9d7472ae87b841575d5358e19f143d12f900Kenny Root                count += start;
13912da9d7472ae87b841575d5358e19f143d12f900Kenny Root                start = 0;
14012da9d7472ae87b841575d5358e19f143d12f900Kenny Root
14167bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat                for (int i = 0; i < count; i++) {
14267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat                    if (buffer[i] == 0) {
143ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey                        final String rawEvent = new String(
144ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey                                buffer, start, i - start, Charsets.UTF_8);
145470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt                        log("RCV <- {" + rawEvent + "}");
14667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
14767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat                        try {
14831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey                            final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(
14931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey                                    rawEvent);
15031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey                            if (event.isClassUnsolicited()) {
151ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey                                // TODO: migrate to sending NativeDaemonEvent instances
15231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey                                mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
15331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey                                        event.getCode(), event.getRawEvent()));
1541cd94ef9570a1534e32b27d5b174dc690c9be6b9Irfan Sheriff                            } else {
155470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                                mResponseQueue.add(event.getCmdNumber(), event);
15667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat                            }
15731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey                        } catch (IllegalArgumentException e) {
158470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                            log("Problem parsing message: " + rawEvent + " - " + e);
15967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat                        }
16031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
16167bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat                        start = i + 1;
16267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat                    }
16367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat                }
164f0be1d89bf1cf5592ea1786d837f4f2329bdf66dRobert Greenwalt                if (start == 0) {
165f0be1d89bf1cf5592ea1786d837f4f2329bdf66dRobert Greenwalt                    final String rawEvent = new String(buffer, start, count, Charsets.UTF_8);
166f0be1d89bf1cf5592ea1786d837f4f2329bdf66dRobert Greenwalt                    log("RCV incomplete <- {" + rawEvent + "}");
167f0be1d89bf1cf5592ea1786d837f4f2329bdf66dRobert Greenwalt                }
16812da9d7472ae87b841575d5358e19f143d12f900Kenny Root
16912da9d7472ae87b841575d5358e19f143d12f900Kenny Root                // We should end at the amount we read. If not, compact then
17012da9d7472ae87b841575d5358e19f143d12f900Kenny Root                // buffer and read again.
171961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root                if (start != count) {
172961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root                    final int remaining = BUFFER_SIZE - start;
173961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root                    System.arraycopy(buffer, start, buffer, 0, remaining);
174961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root                    start = remaining;
175961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root                } else {
176961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root                    start = 0;
177961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root                }
17867bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat            }
17967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat        } catch (IOException ex) {
180470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            loge("Communications error: " + ex);
1814c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat            throw ex;
1824c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat        } finally {
183fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey            synchronized (mDaemonLock) {
1844c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat                if (mOutputStream != null) {
1854c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat                    try {
186470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                        loge("closing stream for " + mSocket);
1874c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat                        mOutputStream.close();
1884c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat                    } catch (IOException e) {
189470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                        loge("Failed closing output stream: " + e);
1904c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat                    }
1914c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat                    mOutputStream = null;
19267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat                }
19367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat            }
19467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
1954c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat            try {
1964c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat                if (socket != null) {
1974c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat                    socket.close();
1984c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat                }
1994c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat            } catch (IOException ex) {
200470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                loge("Failed closing socket: " + ex);
20167bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat            }
20267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat        }
20367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat    }
20467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
20567bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat    /**
206470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt     * Make command for daemon, escaping arguments as needed.
20767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat     */
208d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt    private void makeCommand(StringBuilder builder, String cmd, Object... args)
209fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey            throws NativeDaemonConnectorException {
21031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        // TODO: eventually enforce that cmd doesn't contain arguments
21131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        if (cmd.indexOf('\0') >= 0) {
21231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            throw new IllegalArgumentException("unexpected command: " + cmd);
213b0aec07f7462ff7563835c3107f4b46a28eae7a4Jeff Sharkey        }
21431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
215d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt        builder.append(cmd);
21631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        for (Object arg : args) {
21731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            final String argString = String.valueOf(arg);
21831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            if (argString.indexOf('\0') >= 0) {
21931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey                throw new IllegalArgumentException("unexpected argument: " + arg);
22031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            }
22131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
22231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            builder.append(' ');
22331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            appendEscaped(builder, argString);
224b0aec07f7462ff7563835c3107f4b46a28eae7a4Jeff Sharkey        }
22567bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat    }
22667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
227deba6935c6595c724416cde3368a92c32d8f8683San Mehat    /**
228ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * Issue the given command to the native daemon and return a single expected
229ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * response.
230ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     *
231ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * @throws NativeDaemonConnectorException when problem communicating with
232ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     *             native daemon, or if the response matches
233ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     *             {@link NativeDaemonEvent#isClassClientError()} or
234ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     *             {@link NativeDaemonEvent#isClassServerError()}.
235deba6935c6595c724416cde3368a92c32d8f8683San Mehat     */
236ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    public NativeDaemonEvent execute(Command cmd) throws NativeDaemonConnectorException {
237ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        return execute(cmd.mCmd, cmd.mArguments.toArray());
238ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    }
239ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey
240ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    /**
241ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * Issue the given command to the native daemon and return a single expected
242ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * response.
243ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     *
244ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * @throws NativeDaemonConnectorException when problem communicating with
245ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     *             native daemon, or if the response matches
246ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     *             {@link NativeDaemonEvent#isClassClientError()} or
247ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     *             {@link NativeDaemonEvent#isClassServerError()}.
248ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     */
249ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    public NativeDaemonEvent execute(String cmd, Object... args)
250ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey            throws NativeDaemonConnectorException {
251ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        final NativeDaemonEvent[] events = executeForList(cmd, args);
252ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        if (events.length != 1) {
253ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey            throw new NativeDaemonConnectorException(
254ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey                    "Expected exactly one response, but received " + events.length);
255ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        }
256ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        return events[0];
257ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    }
258ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey
259ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    /**
260ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * Issue the given command to the native daemon and return any
261ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * {@link NativeDaemonEvent#isClassContinue()} responses, including the
262ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * final terminal response.
263ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     *
264ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * @throws NativeDaemonConnectorException when problem communicating with
265ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     *             native daemon, or if the response matches
266ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     *             {@link NativeDaemonEvent#isClassClientError()} or
267ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     *             {@link NativeDaemonEvent#isClassServerError()}.
268ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     */
269ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    public NativeDaemonEvent[] executeForList(Command cmd) throws NativeDaemonConnectorException {
270ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        return executeForList(cmd.mCmd, cmd.mArguments.toArray());
271ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    }
272ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey
273ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    /**
274ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * Issue the given command to the native daemon and return any
275ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * {@link NativeDaemonEvent#isClassContinue()} responses, including the
276ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * final terminal response.
277ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     *
278ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * @throws NativeDaemonConnectorException when problem communicating with
279ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     *             native daemon, or if the response matches
280ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     *             {@link NativeDaemonEvent#isClassClientError()} or
281ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     *             {@link NativeDaemonEvent#isClassServerError()}.
282ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     */
283ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    public NativeDaemonEvent[] executeForList(String cmd, Object... args)
28431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            throws NativeDaemonConnectorException {
285470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            return execute(DEFAULT_TIMEOUT, cmd, args);
286fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey    }
287fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey
288470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt    /**
289470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt     * Issue the given command to the native daemon and return any
290470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt     * {@linke NativeDaemonEvent@isClassContinue()} responses, including the
291470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt     * final terminal response.  Note that the timeout does not count time in
292470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt     * deep sleep.
293470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt     *
294470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt     * @throws NativeDaemonConnectorException when problem communicating with
295470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt     *             native daemon, or if the response matches
296470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt     *             {@link NativeDaemonEvent#isClassClientError()} or
297470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt     *             {@link NativeDaemonEvent#isClassServerError()}.
298470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt     */
299470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt    public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args)
30031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            throws NativeDaemonConnectorException {
30131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();
302d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt
303d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt        final int sequenceNumber = mSequenceNumber.incrementAndGet();
304d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt        final StringBuilder cmdBuilder =
305d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt                new StringBuilder(Integer.toString(sequenceNumber)).append(' ');
3065a0c320a820a0ccaafaa87ad858a29bf5d88a8b9Robert Greenwalt        final long startTime = SystemClock.elapsedRealtime();
307d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt
308d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt        makeCommand(cmdBuilder, cmd, args);
309d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt
310d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt        final String logCmd = cmdBuilder.toString(); /* includes cmdNum, cmd, args */
311d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt        log("SND -> {" + logCmd + "}");
312d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt
313d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt        cmdBuilder.append('\0');
314d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt        final String sentCmd = cmdBuilder.toString(); /* logCmd + \0 */
315d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt
316d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt        synchronized (mDaemonLock) {
317d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt            if (mOutputStream == null) {
318d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt                throw new NativeDaemonConnectorException("missing output stream");
319d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt            } else {
320d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt                try {
321d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt                    mOutputStream.write(sentCmd.getBytes(Charsets.UTF_8));
322d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt                } catch (IOException e) {
323d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt                    throw new NativeDaemonConnectorException("problem sending command", e);
324d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt                }
325d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt            }
326d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt        }
32767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
32831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        NativeDaemonEvent event = null;
32931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        do {
330d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt            event = mResponseQueue.remove(sequenceNumber, timeout, sentCmd);
331470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            if (event == null) {
332d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt                loge("timed-out waiting for response to " + logCmd);
333d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt                throw new NativeDaemonFailureException(logCmd, event);
33467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat            }
335b5aff3fde346225fab0f2751d4fb51b92bb73b32Robert Greenwalt            log("RMV <- {" + event + "}");
33631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            events.add(event);
33731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        } while (event.isClassContinue());
33831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
3395a0c320a820a0ccaafaa87ad858a29bf5d88a8b9Robert Greenwalt        final long endTime = SystemClock.elapsedRealtime();
3405a0c320a820a0ccaafaa87ad858a29bf5d88a8b9Robert Greenwalt        if (endTime - startTime > WARN_EXECUTE_DELAY_MS) {
3415a0c320a820a0ccaafaa87ad858a29bf5d88a8b9Robert Greenwalt            loge("NDC Command {" + logCmd + "} took too long (" + (endTime - startTime) + "ms)");
3425a0c320a820a0ccaafaa87ad858a29bf5d88a8b9Robert Greenwalt        }
3435a0c320a820a0ccaafaa87ad858a29bf5d88a8b9Robert Greenwalt
34431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        if (event.isClassClientError()) {
345d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt            throw new NativeDaemonArgumentException(logCmd, event);
34631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        }
34731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        if (event.isClassServerError()) {
348d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt            throw new NativeDaemonFailureException(logCmd, event);
34967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat        }
35067bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat
35131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        return events.toArray(new NativeDaemonEvent[events.size()]);
35231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
35331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
35431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    /**
35531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * Issue a command to the native daemon and return the raw responses.
35631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     *
35731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * @deprecated callers should move to {@link #execute(String, Object...)}
35831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     *             which returns parsed {@link NativeDaemonEvent}.
35931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     */
36031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    @Deprecated
36131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public ArrayList<String> doCommand(String cmd) throws NativeDaemonConnectorException {
36231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        final ArrayList<String> rawEvents = Lists.newArrayList();
363ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        final NativeDaemonEvent[] events = executeForList(cmd);
36431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        for (NativeDaemonEvent event : events) {
36531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            rawEvents.add(event.getRawEvent());
36667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat        }
36731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        return rawEvents;
36867bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat    }
369deba6935c6595c724416cde3368a92c32d8f8683San Mehat
370fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey    /**
37131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * Issues a list command and returns the cooked list of all
37231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * {@link NativeDaemonEvent#getMessage()} which match requested code.
373deba6935c6595c724416cde3368a92c32d8f8683San Mehat     */
374ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    @Deprecated
37531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public String[] doListCommand(String cmd, int expectedCode)
3764c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat            throws NativeDaemonConnectorException {
37731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        final ArrayList<String> list = Lists.newArrayList();
37831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
379ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        final NativeDaemonEvent[] events = executeForList(cmd);
38031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        for (int i = 0; i < events.length - 1; i++) {
38131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            final NativeDaemonEvent event = events[i];
38231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            final int code = event.getCode();
38331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            if (code == expectedCode) {
38431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey                list.add(event.getMessage());
38531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            } else {
38631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey                throw new NativeDaemonConnectorException(
38731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey                        "unexpected list response " + code + " instead of " + expectedCode);
38831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            }
38931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        }
390deba6935c6595c724416cde3368a92c32d8f8683San Mehat
39131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        final NativeDaemonEvent finalEvent = events[events.length - 1];
39231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        if (!finalEvent.isClassOk()) {
39331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            throw new NativeDaemonConnectorException("unexpected final event: " + finalEvent);
39431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        }
395deba6935c6595c724416cde3368a92c32d8f8683San Mehat
39631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        return list.toArray(new String[list.size()]);
39731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
39831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
39931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    /**
40031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * Append the given argument to {@link StringBuilder}, escaping as needed,
40131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * and surrounding with quotes when it contains spaces.
40231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     */
40331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    // @VisibleForTesting
40431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    static void appendEscaped(StringBuilder builder, String arg) {
40531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        final boolean hasSpaces = arg.indexOf(' ') >= 0;
40631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        if (hasSpaces) {
40731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            builder.append('"');
40831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        }
40931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
41031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        final int length = arg.length();
41131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        for (int i = 0; i < length; i++) {
41231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            final char c = arg.charAt(i);
41331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
41431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            if (c == '"') {
41531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey                builder.append("\\\"");
41631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            } else if (c == '\\') {
41731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey                builder.append("\\\\");
41831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            } else {
41931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey                builder.append(c);
420deba6935c6595c724416cde3368a92c32d8f8683San Mehat            }
421deba6935c6595c724416cde3368a92c32d8f8683San Mehat        }
42231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
42331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        if (hasSpaces) {
42431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            builder.append('"');
42531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        }
42631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
42731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
42831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    private static class NativeDaemonArgumentException extends NativeDaemonConnectorException {
42931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        public NativeDaemonArgumentException(String command, NativeDaemonEvent event) {
43031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            super(command, event);
43131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        }
43231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
43331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        @Override
43431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        public IllegalArgumentException rethrowAsParcelableException() {
43531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            throw new IllegalArgumentException(getMessage(), this);
43631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        }
43731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
43831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
43931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    private static class NativeDaemonFailureException extends NativeDaemonConnectorException {
44031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        public NativeDaemonFailureException(String command, NativeDaemonEvent event) {
44131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            super(command, event);
44231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        }
443deba6935c6595c724416cde3368a92c32d8f8683San Mehat    }
444fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey
445ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    /**
446ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * Command builder that handles argument list building.
447ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     */
448ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    public static class Command {
449ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        private String mCmd;
450ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        private ArrayList<Object> mArguments = Lists.newArrayList();
451ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey
452ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        public Command(String cmd, Object... args) {
453ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey            mCmd = cmd;
454ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey            for (Object arg : args) {
455ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey                appendArg(arg);
456ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey            }
457ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        }
458ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey
459ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        public Command appendArg(Object arg) {
460ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey            mArguments.add(arg);
461ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey            return this;
462ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        }
463ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    }
464ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey
465fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey    /** {@inheritDoc} */
466fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey    public void monitor() {
467fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey        synchronized (mDaemonLock) { }
468fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey    }
469470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt
470470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
471470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt        mLocalLog.dump(fd, pw, args);
472470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        pw.println();
473470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        mResponseQueue.dump(fd, pw, args);
474470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt    }
475470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt
476470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt    private void log(String logstring) {
477470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt        if (LOGD) Slog.d(TAG, logstring);
478470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt        mLocalLog.log(logstring);
479470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt    }
480470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
481470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt    private void loge(String logstring) {
482470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        Slog.e(TAG, logstring);
483470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        mLocalLog.log(logstring);
484470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt    }
485470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
486470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt    private static class ResponseQueue {
487470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
488ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt        private static class PendingCmd {
489470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            public int cmdNum;
490ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            public BlockingQueue<NativeDaemonEvent> responses =
491ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                    new ArrayBlockingQueue<NativeDaemonEvent>(10);
492470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            public String request;
493ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt
494ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            // The availableResponseCount member is used to track when we can remove this
495ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            // instance from the ResponseQueue.
496ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            // This is used under the protection of a sync of the mPendingCmds object.
497ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            // A positive value means we've had more writers retreive this object while
498ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            // a negative value means we've had more readers.  When we've had an equal number
499ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            // (it goes to zero) we can remove this object from the mPendingCmds list.
500ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            // Note that we may have more responses for this command (and more readers
501ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            // coming), but that would result in a new PendingCmd instance being created
502ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            // and added with the same cmdNum.
503ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            // Also note that when this goes to zero it just means a parity of readers and
504ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            // writers have retrieved this object - not that they are done using it.  The
505ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            // responses queue may well have more responses yet to be read or may get more
506ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            // responses added to it.  But all those readers/writers have retreived and
507ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            // hold references to this instance already so it can be removed from
508ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            // mPendingCmds queue.
509ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            public int availableResponseCount;
510ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            public PendingCmd(int c, String r) {cmdNum = c; request = r;}
511470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        }
512470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
513ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt        private final LinkedList<PendingCmd> mPendingCmds;
514470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        private int mMaxCount;
515470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
516470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        ResponseQueue(int maxCount) {
517ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            mPendingCmds = new LinkedList<PendingCmd>();
518470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            mMaxCount = maxCount;
519470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        }
520470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
521470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        public void add(int cmdNum, NativeDaemonEvent response) {
522ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            PendingCmd found = null;
523ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            synchronized (mPendingCmds) {
524ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                for (PendingCmd pendingCmd : mPendingCmds) {
525ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                    if (pendingCmd.cmdNum == cmdNum) {
526ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                        found = pendingCmd;
527470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                        break;
528470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                    }
529470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                }
530470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                if (found == null) {
531470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                    // didn't find it - make sure our queue isn't too big before adding
532ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                    while (mPendingCmds.size() >= mMaxCount) {
533470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                        Slog.e("NativeDaemonConnector.ResponseQueue",
534ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                                "more buffered than allowed: " + mPendingCmds.size() +
535470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                                " >= " + mMaxCount);
536470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                        // let any waiter timeout waiting for this
537ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                        PendingCmd pendingCmd = mPendingCmds.remove();
538470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                        Slog.e("NativeDaemonConnector.ResponseQueue",
539ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                                "Removing request: " + pendingCmd.request + " (" +
540ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                                pendingCmd.cmdNum + ")");
541470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                    }
542ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                    found = new PendingCmd(cmdNum, null);
543ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                    mPendingCmds.add(found);
544470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                }
545ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                found.availableResponseCount++;
546ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                // if a matching remove call has already retrieved this we can remove this
547ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                // instance from our list
548ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                if (found.availableResponseCount == 0) mPendingCmds.remove(found);
549470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            }
550ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            try {
551ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                found.responses.put(response);
552ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            } catch (InterruptedException e) { }
553470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        }
554470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
555470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        // note that the timeout does not count time in deep sleep.  If you don't want
556470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        // the device to sleep, hold a wakelock
557470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        public NativeDaemonEvent remove(int cmdNum, int timeoutMs, String origCmd) {
558ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            PendingCmd found = null;
559ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            synchronized (mPendingCmds) {
560ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                for (PendingCmd pendingCmd : mPendingCmds) {
561ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                    if (pendingCmd.cmdNum == cmdNum) {
562ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                        found = pendingCmd;
563ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                        break;
564470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                    }
565470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                }
566ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                if (found == null) {
567ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                    found = new PendingCmd(cmdNum, origCmd);
568ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                    mPendingCmds.add(found);
569470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                }
570ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                found.availableResponseCount--;
571ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                // if a matching add call has already retrieved this we can remove this
572ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                // instance from our list
573ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                if (found.availableResponseCount == 0) mPendingCmds.remove(found);
574ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            }
575ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            NativeDaemonEvent result = null;
576ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            try {
577ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                result = found.responses.poll(timeoutMs, TimeUnit.MILLISECONDS);
578ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            } catch (InterruptedException e) {}
579ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            if (result == null) {
580ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                Slog.e("NativeDaemonConnector.ResponseQueue", "Timeout waiting for response");
581470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            }
582ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            return result;
583470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        }
584470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
585470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
586470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            pw.println("Pending requests:");
587ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt            synchronized (mPendingCmds) {
588ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                for (PendingCmd pendingCmd : mPendingCmds) {
589ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt                    pw.println("  Cmd " + pendingCmd.cmdNum + " - " + pendingCmd.request);
590470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                }
591470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            }
592470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        }
593470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt    }
59467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat}
595