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