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; 217421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colittiimport android.os.Build; 22e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yehimport android.os.Handler; 232ffa11e4b71c545e34533ef827bdc1a07fbe8246Dianne Hackbornimport android.os.Looper; 24e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yehimport android.os.Message; 2577b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackbornimport android.os.PowerManager; 2667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehatimport android.os.SystemClock; 27470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwaltimport android.util.LocalLog; 288a9b22056b13477f59df934928c00c58b5871c95Joe Onoratoimport android.util.Slog; 2967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 308b2c3a14603d163d7564e6f60286995079687690Jeff Sharkeyimport com.android.internal.annotations.VisibleForTesting; 3131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkeyimport com.google.android.collect.Lists; 3231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 33470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwaltimport java.io.FileDescriptor; 3467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehatimport java.io.IOException; 3567bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehatimport java.io.InputStream; 3667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehatimport java.io.OutputStream; 37470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwaltimport java.io.PrintWriter; 38d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughesimport java.nio.charset.StandardCharsets; 3967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehatimport java.util.ArrayList; 40470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwaltimport java.util.concurrent.atomic.AtomicInteger; 41ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwaltimport java.util.concurrent.ArrayBlockingQueue; 42ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwaltimport java.util.concurrent.BlockingQueue; 43ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwaltimport java.util.concurrent.TimeUnit; 44470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwaltimport java.util.LinkedList; 4567bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 4667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat/** 4731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * Generic connector class for interfacing with a native daemon which uses the 4831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * {@code libsysutils} FrameworkListener protocol. 4967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat */ 50fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkeyfinal class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor { 5131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private static final boolean LOGD = false; 5267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 537f44ff8d6b4d115420e7bd5f08f12825738afd54Robert Greenwalt private final static boolean VDBG = false; 547f44ff8d6b4d115420e7bd5f08f12825738afd54Robert Greenwalt 5531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private final String TAG; 5667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 5731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private String mSocket; 5831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private OutputStream mOutputStream; 59470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt private LocalLog mLocalLog; 60961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root 61470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt private final ResponseQueue mResponseQueue; 6267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 6377b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn private final PowerManager.WakeLock mWakeLock; 6477b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn 652ffa11e4b71c545e34533ef827bdc1a07fbe8246Dianne Hackborn private final Looper mLooper; 662ffa11e4b71c545e34533ef827bdc1a07fbe8246Dianne Hackborn 6731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private INativeDaemonConnectorCallbacks mCallbacks; 6831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private Handler mCallbackHandler; 6967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 70470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt private AtomicInteger mSequenceNumber; 71470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 72470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt private static final int DEFAULT_TIMEOUT = 1 * 60 * 1000; /* 1 minute */ 735a0c320a820a0ccaafaa87ad858a29bf5d88a8b9Robert Greenwalt private static final long WARN_EXECUTE_DELAY_MS = 500; /* .5 sec */ 74470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 7531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey /** Lock held whenever communicating with native daemon. */ 7631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private final Object mDaemonLock = new Object(); 7767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 7831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private final int BUFFER_SIZE = 4096; 7967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 8031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket, 8177b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl) { 822ffa11e4b71c545e34533ef827bdc1a07fbe8246Dianne Hackborn this(callbacks, socket, responseQueueSize, logTag, maxLogSize, wl, 832ffa11e4b71c545e34533ef827bdc1a07fbe8246Dianne Hackborn FgThread.get().getLooper()); 842ffa11e4b71c545e34533ef827bdc1a07fbe8246Dianne Hackborn } 852ffa11e4b71c545e34533ef827bdc1a07fbe8246Dianne Hackborn 862ffa11e4b71c545e34533ef827bdc1a07fbe8246Dianne Hackborn NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket, 872ffa11e4b71c545e34533ef827bdc1a07fbe8246Dianne Hackborn int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl, 882ffa11e4b71c545e34533ef827bdc1a07fbe8246Dianne Hackborn Looper looper) { 8967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat mCallbacks = callbacks; 9067bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat mSocket = socket; 91470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt mResponseQueue = new ResponseQueue(responseQueueSize); 9277b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn mWakeLock = wl; 9377b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn if (mWakeLock != null) { 9477b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn mWakeLock.setReferenceCounted(true); 9577b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn } 962ffa11e4b71c545e34533ef827bdc1a07fbe8246Dianne Hackborn mLooper = looper; 97470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt mSequenceNumber = new AtomicInteger(0); 9831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey TAG = logTag != null ? logTag : "NativeDaemonConnector"; 99470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt mLocalLog = new LocalLog(maxLogSize); 10067bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 10167bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 102e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh @Override 10367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat public void run() { 1042ffa11e4b71c545e34533ef827bdc1a07fbe8246Dianne Hackborn mCallbackHandler = new Handler(mLooper, this); 10567bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 10667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat while (true) { 10767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat try { 10867bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat listenToSocket(); 10967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } catch (Exception e) { 110470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt loge("Error in NativeDaemonConnector: " + e); 1114c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat SystemClock.sleep(5000); 11267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 11367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 11467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 11567bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 116e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh @Override 117e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh public boolean handleMessage(Message msg) { 118e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh String event = (String) msg.obj; 119e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh try { 1202d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) { 121470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt log(String.format("Unhandled event '%s'", event)); 122e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh } 123e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh } catch (Exception e) { 124470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt loge("Error handling '" + event + "': " + e); 12577b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn } finally { 1264590e52f3d0558e01322fe4dd55bb612afdfb079Dianne Hackborn if (mCallbacks.onCheckHoldWakeLock(msg.what) && mWakeLock != null) { 12777b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn mWakeLock.release(); 12877b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn } 129e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh } 130e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh return true; 131e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh } 132e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh 1337421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti private LocalSocketAddress determineSocketAddress() { 1347421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti // If we're testing, set up a socket in a namespace that's accessible to test code. 1357421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti // In order to ensure that unprivileged apps aren't able to impersonate native daemons on 1367421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti // production devices, even if said native daemons ill-advisedly pick a socket name that 1377421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti // starts with __test__, only allow this on debug builds. 1387421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti if (mSocket.startsWith("__test__") && Build.IS_DEBUGGABLE) { 1397421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti return new LocalSocketAddress(mSocket); 1407421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti } else { 1417421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti return new LocalSocketAddress(mSocket, LocalSocketAddress.Namespace.RESERVED); 1427421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti } 1437421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti } 1447421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti 1454c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat private void listenToSocket() throws IOException { 146961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root LocalSocket socket = null; 14767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 14867bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat try { 14967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat socket = new LocalSocket(); 1507421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti LocalSocketAddress address = determineSocketAddress(); 15167bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 15267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat socket.connect(address); 15367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 15467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat InputStream inputStream = socket.getInputStream(); 155470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt synchronized (mDaemonLock) { 156470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt mOutputStream = socket.getOutputStream(); 157470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 15867bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 159030bc88b4c09a41b0d7dbe249aec55f33d6b8b8aanga mCallbacks.onDaemonConnected(); 160030bc88b4c09a41b0d7dbe249aec55f33d6b8b8aanga 161961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root byte[] buffer = new byte[BUFFER_SIZE]; 162961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root int start = 0; 16367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 16467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat while (true) { 165961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root int count = inputStream.read(buffer, start, BUFFER_SIZE - start); 166470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt if (count < 0) { 167470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt loge("got " + count + " reading with start = " + start); 168470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt break; 169470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 17067bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 17112da9d7472ae87b841575d5358e19f143d12f900Kenny Root // Add our starting point to the count and reset the start. 17212da9d7472ae87b841575d5358e19f143d12f900Kenny Root count += start; 17312da9d7472ae87b841575d5358e19f143d12f900Kenny Root start = 0; 17412da9d7472ae87b841575d5358e19f143d12f900Kenny Root 17567bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat for (int i = 0; i < count; i++) { 17667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat if (buffer[i] == 0) { 177c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence // Note - do not log this raw message since it may contain 178c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence // sensitive data 179ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey final String rawEvent = new String( 180d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes buffer, start, i - start, StandardCharsets.UTF_8); 18167bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 18277b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn boolean releaseWl = false; 18367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat try { 18431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent( 18531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey rawEvent); 186c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence 187c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence log("RCV <- {" + event + "}"); 188c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence 18931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey if (event.isClassUnsolicited()) { 190ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey // TODO: migrate to sending NativeDaemonEvent instances 1914590e52f3d0558e01322fe4dd55bb612afdfb079Dianne Hackborn if (mCallbacks.onCheckHoldWakeLock(event.getCode()) 1924590e52f3d0558e01322fe4dd55bb612afdfb079Dianne Hackborn && mWakeLock != null) { 19377b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn mWakeLock.acquire(); 19477b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn releaseWl = true; 19577b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn } 19677b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn if (mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage( 19777b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn event.getCode(), event.getRawEvent()))) { 19877b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn releaseWl = false; 19977b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn } 2001cd94ef9570a1534e32b27d5b174dc690c9be6b9Irfan Sheriff } else { 201470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt mResponseQueue.add(event.getCmdNumber(), event); 20267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 20331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } catch (IllegalArgumentException e) { 204c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence log("Problem parsing message " + e); 20577b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn } finally { 20677b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn if (releaseWl) { 20777b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn mWakeLock.acquire(); 20877b987f1a1bb6028a871de01065b94c4cfff0b5cDianne Hackborn } 20967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 21031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 21167bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat start = i + 1; 21267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 21367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 214c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence 215f0be1d89bf1cf5592ea1786d837f4f2329bdf66dRobert Greenwalt if (start == 0) { 216c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence log("RCV incomplete"); 217f0be1d89bf1cf5592ea1786d837f4f2329bdf66dRobert Greenwalt } 21812da9d7472ae87b841575d5358e19f143d12f900Kenny Root 21912da9d7472ae87b841575d5358e19f143d12f900Kenny Root // We should end at the amount we read. If not, compact then 22012da9d7472ae87b841575d5358e19f143d12f900Kenny Root // buffer and read again. 221961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root if (start != count) { 222961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root final int remaining = BUFFER_SIZE - start; 223961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root System.arraycopy(buffer, start, buffer, 0, remaining); 224961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root start = remaining; 225961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root } else { 226961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root start = 0; 227961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root } 22867bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 22967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } catch (IOException ex) { 230470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt loge("Communications error: " + ex); 2314c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat throw ex; 2324c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat } finally { 233fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey synchronized (mDaemonLock) { 2344c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat if (mOutputStream != null) { 2354c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat try { 236470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt loge("closing stream for " + mSocket); 2374c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat mOutputStream.close(); 2384c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat } catch (IOException e) { 239470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt loge("Failed closing output stream: " + e); 2404c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat } 2414c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat mOutputStream = null; 24267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 24367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 24467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 2454c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat try { 2464c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat if (socket != null) { 2474c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat socket.close(); 2484c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat } 2494c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat } catch (IOException ex) { 250470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt loge("Failed closing socket: " + ex); 25167bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 25267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 25367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 25467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 25567bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat /** 25656cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey * Wrapper around argument that indicates it's sensitive and shouldn't be 25756cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey * logged. 25856cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey */ 25956cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey public static class SensitiveArg { 26056cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey private final Object mArg; 26156cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey 26256cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey public SensitiveArg(Object arg) { 26356cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey mArg = arg; 26456cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey } 26556cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey 26656cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey @Override 26756cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey public String toString() { 26856cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey return String.valueOf(mArg); 26956cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey } 27056cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey } 27156cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey 27256cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey /** 273470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt * Make command for daemon, escaping arguments as needed. 27467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat */ 27556cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey @VisibleForTesting 27656cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey static void makeCommand(StringBuilder rawBuilder, StringBuilder logBuilder, int sequenceNumber, 27756cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey String cmd, Object... args) { 27831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey if (cmd.indexOf('\0') >= 0) { 2797b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey throw new IllegalArgumentException("Unexpected command: " + cmd); 2807b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey } 2817b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey if (cmd.indexOf(' ') >= 0) { 2827b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey throw new IllegalArgumentException("Arguments must be separate from command"); 283b0aec07f7462ff7563835c3107f4b46a28eae7a4Jeff Sharkey } 28431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 28556cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey rawBuilder.append(sequenceNumber).append(' ').append(cmd); 28656cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey logBuilder.append(sequenceNumber).append(' ').append(cmd); 28731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey for (Object arg : args) { 28831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey final String argString = String.valueOf(arg); 28931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey if (argString.indexOf('\0') >= 0) { 2907b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey throw new IllegalArgumentException("Unexpected argument: " + arg); 29131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 29231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 29356cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey rawBuilder.append(' '); 29456cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey logBuilder.append(' '); 29556cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey 29656cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey appendEscaped(rawBuilder, argString); 29756cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey if (arg instanceof SensitiveArg) { 29856cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey logBuilder.append("[scrubbed]"); 29956cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey } else { 30056cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey appendEscaped(logBuilder, argString); 30156cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey } 302b0aec07f7462ff7563835c3107f4b46a28eae7a4Jeff Sharkey } 30356cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey 30456cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey rawBuilder.append('\0'); 30567bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 30667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 307deba6935c6595c724416cde3368a92c32d8f8683San Mehat /** 308ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * Issue the given command to the native daemon and return a single expected 309ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * response. 310ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * 311ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * @throws NativeDaemonConnectorException when problem communicating with 312ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * native daemon, or if the response matches 313ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassClientError()} or 314ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassServerError()}. 315deba6935c6595c724416cde3368a92c32d8f8683San Mehat */ 316ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey public NativeDaemonEvent execute(Command cmd) throws NativeDaemonConnectorException { 317ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey return execute(cmd.mCmd, cmd.mArguments.toArray()); 318ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey } 319ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey 320ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey /** 321ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * Issue the given command to the native daemon and return a single expected 3227b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * response. Any arguments must be separated from base command so they can 3237b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * be properly escaped. 324ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * 325ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * @throws NativeDaemonConnectorException when problem communicating with 326ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * native daemon, or if the response matches 327ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassClientError()} or 328ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassServerError()}. 329ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey */ 330ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey public NativeDaemonEvent execute(String cmd, Object... args) 331ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey throws NativeDaemonConnectorException { 332ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey final NativeDaemonEvent[] events = executeForList(cmd, args); 333ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey if (events.length != 1) { 334ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey throw new NativeDaemonConnectorException( 335ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey "Expected exactly one response, but received " + events.length); 336ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey } 337ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey return events[0]; 338ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey } 339ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey 340ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey /** 341ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * Issue the given command to the native daemon and return any 342ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassContinue()} responses, including the 343ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * final terminal response. 344ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * 345ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * @throws NativeDaemonConnectorException when problem communicating with 346ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * native daemon, or if the response matches 347ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassClientError()} or 348ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassServerError()}. 349ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey */ 350ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey public NativeDaemonEvent[] executeForList(Command cmd) throws NativeDaemonConnectorException { 351ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey return executeForList(cmd.mCmd, cmd.mArguments.toArray()); 352ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey } 353ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey 354ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey /** 355ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * Issue the given command to the native daemon and return any 356ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassContinue()} responses, including the 3577b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * final terminal response. Any arguments must be separated from base 3587b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * command so they can be properly escaped. 359ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * 360ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * @throws NativeDaemonConnectorException when problem communicating with 361ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * native daemon, or if the response matches 362ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassClientError()} or 363ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassServerError()}. 364ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey */ 365ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey public NativeDaemonEvent[] executeForList(String cmd, Object... args) 36631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey throws NativeDaemonConnectorException { 367470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt return execute(DEFAULT_TIMEOUT, cmd, args); 368fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey } 369fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey 370470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt /** 3717b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * Issue the given command to the native daemon and return any {@linke 3727b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * NativeDaemonEvent@isClassContinue()} responses, including the final 3737b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * terminal response. Note that the timeout does not count time in deep 3747b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * sleep. Any arguments must be separated from base command so they can be 3757b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * properly escaped. 376470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt * 377470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt * @throws NativeDaemonConnectorException when problem communicating with 378470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt * native daemon, or if the response matches 379470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt * {@link NativeDaemonEvent#isClassClientError()} or 380470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt * {@link NativeDaemonEvent#isClassServerError()}. 381470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt */ 382470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args) 38331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey throws NativeDaemonConnectorException { 38456cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey final long startTime = SystemClock.elapsedRealtime(); 38556cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey 38631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey final ArrayList<NativeDaemonEvent> events = Lists.newArrayList(); 387d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt 38856cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey final StringBuilder rawBuilder = new StringBuilder(); 38956cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey final StringBuilder logBuilder = new StringBuilder(); 390d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt final int sequenceNumber = mSequenceNumber.incrementAndGet(); 391d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt 39256cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey makeCommand(rawBuilder, logBuilder, sequenceNumber, cmd, args); 393d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt 39456cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey final String rawCmd = rawBuilder.toString(); 39556cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey final String logCmd = logBuilder.toString(); 396d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt 39756cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey log("SND -> {" + logCmd + "}"); 398d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt 399d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt synchronized (mDaemonLock) { 400d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt if (mOutputStream == null) { 401d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt throw new NativeDaemonConnectorException("missing output stream"); 402d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt } else { 403d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt try { 404b8292830f79fc76ffb9a1be5cd316212ac494d03Elliott Hughes mOutputStream.write(rawCmd.getBytes(StandardCharsets.UTF_8)); 405d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt } catch (IOException e) { 406d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt throw new NativeDaemonConnectorException("problem sending command", e); 407d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt } 408d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt } 409d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt } 41067bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 41131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey NativeDaemonEvent event = null; 41231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey do { 41356cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey event = mResponseQueue.remove(sequenceNumber, timeout, logCmd); 414470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt if (event == null) { 415d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt loge("timed-out waiting for response to " + logCmd); 416d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt throw new NativeDaemonFailureException(logCmd, event); 41767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 4187f44ff8d6b4d115420e7bd5f08f12825738afd54Robert Greenwalt if (VDBG) log("RMV <- {" + event + "}"); 41931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey events.add(event); 42031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } while (event.isClassContinue()); 42131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 4225a0c320a820a0ccaafaa87ad858a29bf5d88a8b9Robert Greenwalt final long endTime = SystemClock.elapsedRealtime(); 4235a0c320a820a0ccaafaa87ad858a29bf5d88a8b9Robert Greenwalt if (endTime - startTime > WARN_EXECUTE_DELAY_MS) { 4245a0c320a820a0ccaafaa87ad858a29bf5d88a8b9Robert Greenwalt loge("NDC Command {" + logCmd + "} took too long (" + (endTime - startTime) + "ms)"); 4255a0c320a820a0ccaafaa87ad858a29bf5d88a8b9Robert Greenwalt } 4265a0c320a820a0ccaafaa87ad858a29bf5d88a8b9Robert Greenwalt 42731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey if (event.isClassClientError()) { 428d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt throw new NativeDaemonArgumentException(logCmd, event); 42931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 43031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey if (event.isClassServerError()) { 431d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt throw new NativeDaemonFailureException(logCmd, event); 43267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 43367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 43431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey return events.toArray(new NativeDaemonEvent[events.size()]); 43531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 43631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 43731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey /** 43831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * Append the given argument to {@link StringBuilder}, escaping as needed, 43931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * and surrounding with quotes when it contains spaces. 44031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey */ 4418b2c3a14603d163d7564e6f60286995079687690Jeff Sharkey @VisibleForTesting 44231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey static void appendEscaped(StringBuilder builder, String arg) { 44331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey final boolean hasSpaces = arg.indexOf(' ') >= 0; 44431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey if (hasSpaces) { 44531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey builder.append('"'); 44631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 44731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 44831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey final int length = arg.length(); 44931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey for (int i = 0; i < length; i++) { 45031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey final char c = arg.charAt(i); 45131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 45231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey if (c == '"') { 45331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey builder.append("\\\""); 45431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } else if (c == '\\') { 45531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey builder.append("\\\\"); 45631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } else { 45731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey builder.append(c); 458deba6935c6595c724416cde3368a92c32d8f8683San Mehat } 459deba6935c6595c724416cde3368a92c32d8f8683San Mehat } 46031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 46131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey if (hasSpaces) { 46231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey builder.append('"'); 46331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 46431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 46531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 46631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private static class NativeDaemonArgumentException extends NativeDaemonConnectorException { 46731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey public NativeDaemonArgumentException(String command, NativeDaemonEvent event) { 46831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey super(command, event); 46931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 47031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 47131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey @Override 47231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey public IllegalArgumentException rethrowAsParcelableException() { 47331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey throw new IllegalArgumentException(getMessage(), this); 47431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 47531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 47631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 47731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private static class NativeDaemonFailureException extends NativeDaemonConnectorException { 47831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey public NativeDaemonFailureException(String command, NativeDaemonEvent event) { 47931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey super(command, event); 48031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 481deba6935c6595c724416cde3368a92c32d8f8683San Mehat } 482fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey 483ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey /** 4847b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * Command builder that handles argument list building. Any arguments must 4857b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * be separated from base command so they can be properly escaped. 486ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey */ 487ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey public static class Command { 488ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey private String mCmd; 489ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey private ArrayList<Object> mArguments = Lists.newArrayList(); 490ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey 491ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey public Command(String cmd, Object... args) { 492ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey mCmd = cmd; 493ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey for (Object arg : args) { 494ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey appendArg(arg); 495ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey } 496ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey } 497ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey 498ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey public Command appendArg(Object arg) { 499ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey mArguments.add(arg); 500ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey return this; 501ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey } 502ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey } 503ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey 504fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey /** {@inheritDoc} */ 505fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey public void monitor() { 506fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey synchronized (mDaemonLock) { } 507fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey } 508470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt 509470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 510470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt mLocalLog.dump(fd, pw, args); 511470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt pw.println(); 512470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt mResponseQueue.dump(fd, pw, args); 513470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt } 514470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt 515470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt private void log(String logstring) { 516470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt if (LOGD) Slog.d(TAG, logstring); 517470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt mLocalLog.log(logstring); 518470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt } 519470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 520470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt private void loge(String logstring) { 521470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt Slog.e(TAG, logstring); 522470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt mLocalLog.log(logstring); 523470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 524470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 525470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt private static class ResponseQueue { 526470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 527ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt private static class PendingCmd { 52856cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey public final int cmdNum; 52956cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey public final String logCmd; 53056cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey 531ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt public BlockingQueue<NativeDaemonEvent> responses = 532ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt new ArrayBlockingQueue<NativeDaemonEvent>(10); 533ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt 534ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // The availableResponseCount member is used to track when we can remove this 535ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // instance from the ResponseQueue. 536ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // This is used under the protection of a sync of the mPendingCmds object. 537ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // A positive value means we've had more writers retreive this object while 538ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // a negative value means we've had more readers. When we've had an equal number 539ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // (it goes to zero) we can remove this object from the mPendingCmds list. 540ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // Note that we may have more responses for this command (and more readers 541ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // coming), but that would result in a new PendingCmd instance being created 542ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // and added with the same cmdNum. 543ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // Also note that when this goes to zero it just means a parity of readers and 544ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // writers have retrieved this object - not that they are done using it. The 545ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // responses queue may well have more responses yet to be read or may get more 546ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // responses added to it. But all those readers/writers have retreived and 547ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // hold references to this instance already so it can be removed from 548ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // mPendingCmds queue. 549ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt public int availableResponseCount; 55056cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey 55156cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey public PendingCmd(int cmdNum, String logCmd) { 55256cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey this.cmdNum = cmdNum; 55356cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey this.logCmd = logCmd; 55456cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey } 555470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 556470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 557ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt private final LinkedList<PendingCmd> mPendingCmds; 558470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt private int mMaxCount; 559470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 560470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt ResponseQueue(int maxCount) { 561ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt mPendingCmds = new LinkedList<PendingCmd>(); 562470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt mMaxCount = maxCount; 563470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 564470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 565470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt public void add(int cmdNum, NativeDaemonEvent response) { 566ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt PendingCmd found = null; 567ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt synchronized (mPendingCmds) { 568ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt for (PendingCmd pendingCmd : mPendingCmds) { 569ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt if (pendingCmd.cmdNum == cmdNum) { 570ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt found = pendingCmd; 571470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt break; 572470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 573470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 574470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt if (found == null) { 575470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt // didn't find it - make sure our queue isn't too big before adding 576ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt while (mPendingCmds.size() >= mMaxCount) { 577470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt Slog.e("NativeDaemonConnector.ResponseQueue", 578ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt "more buffered than allowed: " + mPendingCmds.size() + 579470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt " >= " + mMaxCount); 580470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt // let any waiter timeout waiting for this 581ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt PendingCmd pendingCmd = mPendingCmds.remove(); 582470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt Slog.e("NativeDaemonConnector.ResponseQueue", 58356cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey "Removing request: " + pendingCmd.logCmd + " (" + 584ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt pendingCmd.cmdNum + ")"); 585470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 586ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt found = new PendingCmd(cmdNum, null); 587ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt mPendingCmds.add(found); 588470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 589ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt found.availableResponseCount++; 590ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // if a matching remove call has already retrieved this we can remove this 591ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // instance from our list 592ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt if (found.availableResponseCount == 0) mPendingCmds.remove(found); 593470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 594ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt try { 595ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt found.responses.put(response); 596ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt } catch (InterruptedException e) { } 597470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 598470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 599470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt // note that the timeout does not count time in deep sleep. If you don't want 600470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt // the device to sleep, hold a wakelock 60156cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey public NativeDaemonEvent remove(int cmdNum, int timeoutMs, String logCmd) { 602ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt PendingCmd found = null; 603ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt synchronized (mPendingCmds) { 604ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt for (PendingCmd pendingCmd : mPendingCmds) { 605ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt if (pendingCmd.cmdNum == cmdNum) { 606ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt found = pendingCmd; 607ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt break; 608470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 609470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 610ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt if (found == null) { 61156cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey found = new PendingCmd(cmdNum, logCmd); 612ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt mPendingCmds.add(found); 613470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 614ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt found.availableResponseCount--; 615ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // if a matching add call has already retrieved this we can remove this 616ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // instance from our list 617ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt if (found.availableResponseCount == 0) mPendingCmds.remove(found); 618ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt } 619ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt NativeDaemonEvent result = null; 620ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt try { 621ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt result = found.responses.poll(timeoutMs, TimeUnit.MILLISECONDS); 622ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt } catch (InterruptedException e) {} 623ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt if (result == null) { 624ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt Slog.e("NativeDaemonConnector.ResponseQueue", "Timeout waiting for response"); 625470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 626ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt return result; 627470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 628470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 629470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 630470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt pw.println("Pending requests:"); 631ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt synchronized (mPendingCmds) { 632ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt for (PendingCmd pendingCmd : mPendingCmds) { 63356cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey pw.println(" Cmd " + pendingCmd.cmdNum + " - " + pendingCmd.logCmd); 634470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 635470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 636470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 637470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 63867bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat} 639