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; 23e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yehimport android.os.Message; 2467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehatimport android.os.SystemClock; 25470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwaltimport android.util.LocalLog; 268a9b22056b13477f59df934928c00c58b5871c95Joe Onoratoimport android.util.Slog; 2767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 288b2c3a14603d163d7564e6f60286995079687690Jeff Sharkeyimport com.android.internal.annotations.VisibleForTesting; 2931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkeyimport com.google.android.collect.Lists; 3031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 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; 36d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughesimport java.nio.charset.StandardCharsets; 3767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehatimport java.util.ArrayList; 38470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwaltimport java.util.concurrent.atomic.AtomicInteger; 39ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwaltimport java.util.concurrent.ArrayBlockingQueue; 40ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwaltimport java.util.concurrent.BlockingQueue; 41ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwaltimport java.util.concurrent.TimeUnit; 42470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwaltimport java.util.LinkedList; 4367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 4467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat/** 4531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * Generic connector class for interfacing with a native daemon which uses the 4631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * {@code libsysutils} FrameworkListener protocol. 4767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat */ 48fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkeyfinal class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor { 4931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private static final boolean LOGD = false; 5067bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 5131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private final String TAG; 5267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 5331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private String mSocket; 5431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private OutputStream mOutputStream; 55470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt private LocalLog mLocalLog; 56961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root 57470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt private final ResponseQueue mResponseQueue; 5867bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 5931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private INativeDaemonConnectorCallbacks mCallbacks; 6031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private Handler mCallbackHandler; 6167bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 62470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt private AtomicInteger mSequenceNumber; 63470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 64470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt private static final int DEFAULT_TIMEOUT = 1 * 60 * 1000; /* 1 minute */ 655a0c320a820a0ccaafaa87ad858a29bf5d88a8b9Robert Greenwalt private static final long WARN_EXECUTE_DELAY_MS = 500; /* .5 sec */ 66470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 6731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey /** Lock held whenever communicating with native daemon. */ 6831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private final Object mDaemonLock = new Object(); 6967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 7031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private final int BUFFER_SIZE = 4096; 7167bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 7231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket, 73470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt int responseQueueSize, String logTag, int maxLogSize) { 7467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat mCallbacks = callbacks; 7567bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat mSocket = socket; 76470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt mResponseQueue = new ResponseQueue(responseQueueSize); 77470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt mSequenceNumber = new AtomicInteger(0); 7831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey TAG = logTag != null ? logTag : "NativeDaemonConnector"; 79470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt mLocalLog = new LocalLog(maxLogSize); 8067bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 8167bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 82e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh @Override 8367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat public void run() { 848d044e8bc287c1a567d82aedbe30085b011544c3Dianne Hackborn mCallbackHandler = new Handler(FgThread.get().getLooper(), this); 8567bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 8667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat while (true) { 8767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat try { 8867bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat listenToSocket(); 8967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } catch (Exception e) { 90470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt loge("Error in NativeDaemonConnector: " + e); 914c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat SystemClock.sleep(5000); 9267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 9367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 9467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 9567bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 96e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh @Override 97e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh public boolean handleMessage(Message msg) { 98e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh String event = (String) msg.obj; 99e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh try { 1002d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) { 101470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt log(String.format("Unhandled event '%s'", event)); 102e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh } 103e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh } catch (Exception e) { 104470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt loge("Error handling '" + event + "': " + e); 105e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh } 106e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh return true; 107e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh } 108e5750a344a9c1a83a63d5119d39d2ea4897bc312Chia-chi Yeh 1097421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti private LocalSocketAddress determineSocketAddress() { 1107421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti // If we're testing, set up a socket in a namespace that's accessible to test code. 1117421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti // In order to ensure that unprivileged apps aren't able to impersonate native daemons on 1127421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti // production devices, even if said native daemons ill-advisedly pick a socket name that 1137421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti // starts with __test__, only allow this on debug builds. 1147421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti if (mSocket.startsWith("__test__") && Build.IS_DEBUGGABLE) { 1157421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti return new LocalSocketAddress(mSocket); 1167421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti } else { 1177421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti return new LocalSocketAddress(mSocket, LocalSocketAddress.Namespace.RESERVED); 1187421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti } 1197421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti } 1207421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti 1214c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat private void listenToSocket() throws IOException { 122961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root LocalSocket socket = null; 12367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 12467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat try { 12567bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat socket = new LocalSocket(); 1267421a01f18f34d554ca7a9fd987c4f96da2bdf2fLorenzo Colitti LocalSocketAddress address = determineSocketAddress(); 12767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 12867bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat socket.connect(address); 12967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 13067bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat InputStream inputStream = socket.getInputStream(); 131470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt synchronized (mDaemonLock) { 132470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt mOutputStream = socket.getOutputStream(); 133470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 13467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 135030bc88b4c09a41b0d7dbe249aec55f33d6b8b8aanga mCallbacks.onDaemonConnected(); 136030bc88b4c09a41b0d7dbe249aec55f33d6b8b8aanga 137961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root byte[] buffer = new byte[BUFFER_SIZE]; 138961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root int start = 0; 13967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 14067bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat while (true) { 141961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root int count = inputStream.read(buffer, start, BUFFER_SIZE - start); 142470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt if (count < 0) { 143470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt loge("got " + count + " reading with start = " + start); 144470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt break; 145470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 14667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 14712da9d7472ae87b841575d5358e19f143d12f900Kenny Root // Add our starting point to the count and reset the start. 14812da9d7472ae87b841575d5358e19f143d12f900Kenny Root count += start; 14912da9d7472ae87b841575d5358e19f143d12f900Kenny Root start = 0; 15012da9d7472ae87b841575d5358e19f143d12f900Kenny Root 15167bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat for (int i = 0; i < count; i++) { 15267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat if (buffer[i] == 0) { 153ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey final String rawEvent = new String( 154d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes buffer, start, i - start, StandardCharsets.UTF_8); 155470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt log("RCV <- {" + rawEvent + "}"); 15667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 15767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat try { 15831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent( 15931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey rawEvent); 16031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey if (event.isClassUnsolicited()) { 161ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey // TODO: migrate to sending NativeDaemonEvent instances 16231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage( 16331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey event.getCode(), event.getRawEvent())); 1641cd94ef9570a1534e32b27d5b174dc690c9be6b9Irfan Sheriff } else { 165470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt mResponseQueue.add(event.getCmdNumber(), event); 16667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 16731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } catch (IllegalArgumentException e) { 168470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt log("Problem parsing message: " + rawEvent + " - " + e); 16967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 17031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 17167bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat start = i + 1; 17267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 17367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 174f0be1d89bf1cf5592ea1786d837f4f2329bdf66dRobert Greenwalt if (start == 0) { 175d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes final String rawEvent = new String(buffer, start, count, StandardCharsets.UTF_8); 176f0be1d89bf1cf5592ea1786d837f4f2329bdf66dRobert Greenwalt log("RCV incomplete <- {" + rawEvent + "}"); 177f0be1d89bf1cf5592ea1786d837f4f2329bdf66dRobert Greenwalt } 17812da9d7472ae87b841575d5358e19f143d12f900Kenny Root 17912da9d7472ae87b841575d5358e19f143d12f900Kenny Root // We should end at the amount we read. If not, compact then 18012da9d7472ae87b841575d5358e19f143d12f900Kenny Root // buffer and read again. 181961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root if (start != count) { 182961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root final int remaining = BUFFER_SIZE - start; 183961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root System.arraycopy(buffer, start, buffer, 0, remaining); 184961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root start = remaining; 185961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root } else { 186961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root start = 0; 187961aa8c8879e9f68c0eddcaf87565200a4347134Kenny Root } 18867bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 18967bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } catch (IOException ex) { 190470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt loge("Communications error: " + ex); 1914c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat throw ex; 1924c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat } finally { 193fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey synchronized (mDaemonLock) { 1944c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat if (mOutputStream != null) { 1954c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat try { 196470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt loge("closing stream for " + mSocket); 1974c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat mOutputStream.close(); 1984c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat } catch (IOException e) { 199470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt loge("Failed closing output stream: " + e); 2004c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat } 2014c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat mOutputStream = null; 20267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 20367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 20467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 2054c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat try { 2064c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat if (socket != null) { 2074c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat socket.close(); 2084c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat } 2094c27e0e3bed006a9ba45c6c02be5fe49827b3feaSan Mehat } catch (IOException ex) { 210470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt loge("Failed closing socket: " + ex); 21167bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 21267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 21367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 21467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 21567bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat /** 21656cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey * Wrapper around argument that indicates it's sensitive and shouldn't be 21756cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey * logged. 21856cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey */ 21956cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey public static class SensitiveArg { 22056cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey private final Object mArg; 22156cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey 22256cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey public SensitiveArg(Object arg) { 22356cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey mArg = arg; 22456cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey } 22556cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey 22656cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey @Override 22756cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey public String toString() { 22856cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey return String.valueOf(mArg); 22956cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey } 23056cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey } 23156cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey 23256cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey /** 233470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt * Make command for daemon, escaping arguments as needed. 23467bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat */ 23556cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey @VisibleForTesting 23656cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey static void makeCommand(StringBuilder rawBuilder, StringBuilder logBuilder, int sequenceNumber, 23756cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey String cmd, Object... args) { 23831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey if (cmd.indexOf('\0') >= 0) { 2397b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey throw new IllegalArgumentException("Unexpected command: " + cmd); 2407b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey } 2417b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey if (cmd.indexOf(' ') >= 0) { 2427b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey throw new IllegalArgumentException("Arguments must be separate from command"); 243b0aec07f7462ff7563835c3107f4b46a28eae7a4Jeff Sharkey } 24431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 24556cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey rawBuilder.append(sequenceNumber).append(' ').append(cmd); 24656cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey logBuilder.append(sequenceNumber).append(' ').append(cmd); 24731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey for (Object arg : args) { 24831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey final String argString = String.valueOf(arg); 24931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey if (argString.indexOf('\0') >= 0) { 2507b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey throw new IllegalArgumentException("Unexpected argument: " + arg); 25131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 25231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 25356cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey rawBuilder.append(' '); 25456cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey logBuilder.append(' '); 25556cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey 25656cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey appendEscaped(rawBuilder, argString); 25756cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey if (arg instanceof SensitiveArg) { 25856cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey logBuilder.append("[scrubbed]"); 25956cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey } else { 26056cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey appendEscaped(logBuilder, argString); 26156cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey } 262b0aec07f7462ff7563835c3107f4b46a28eae7a4Jeff Sharkey } 26356cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey 26456cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey rawBuilder.append('\0'); 26567bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 26667bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 267deba6935c6595c724416cde3368a92c32d8f8683San Mehat /** 268ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * Issue the given command to the native daemon and return a single expected 269ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * response. 270ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * 271ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * @throws NativeDaemonConnectorException when problem communicating with 272ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * native daemon, or if the response matches 273ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassClientError()} or 274ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassServerError()}. 275deba6935c6595c724416cde3368a92c32d8f8683San Mehat */ 276ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey public NativeDaemonEvent execute(Command cmd) throws NativeDaemonConnectorException { 277ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey return execute(cmd.mCmd, cmd.mArguments.toArray()); 278ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey } 279ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey 280ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey /** 281ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * Issue the given command to the native daemon and return a single expected 2827b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * response. Any arguments must be separated from base command so they can 2837b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * be properly escaped. 284ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * 285ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * @throws NativeDaemonConnectorException when problem communicating with 286ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * native daemon, or if the response matches 287ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassClientError()} or 288ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassServerError()}. 289ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey */ 290ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey public NativeDaemonEvent execute(String cmd, Object... args) 291ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey throws NativeDaemonConnectorException { 292ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey final NativeDaemonEvent[] events = executeForList(cmd, args); 293ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey if (events.length != 1) { 294ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey throw new NativeDaemonConnectorException( 295ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey "Expected exactly one response, but received " + events.length); 296ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey } 297ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey return events[0]; 298ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey } 299ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey 300ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey /** 301ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * Issue the given command to the native daemon and return any 302ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassContinue()} responses, including the 303ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * final terminal response. 304ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * 305ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * @throws NativeDaemonConnectorException when problem communicating with 306ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * native daemon, or if the response matches 307ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassClientError()} or 308ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassServerError()}. 309ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey */ 310ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey public NativeDaemonEvent[] executeForList(Command cmd) throws NativeDaemonConnectorException { 311ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey return executeForList(cmd.mCmd, cmd.mArguments.toArray()); 312ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey } 313ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey 314ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey /** 315ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * Issue the given command to the native daemon and return any 316ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassContinue()} responses, including the 3177b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * final terminal response. Any arguments must be separated from base 3187b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * command so they can be properly escaped. 319ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * 320ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * @throws NativeDaemonConnectorException when problem communicating with 321ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * native daemon, or if the response matches 322ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassClientError()} or 323ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey * {@link NativeDaemonEvent#isClassServerError()}. 324ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey */ 325ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey public NativeDaemonEvent[] executeForList(String cmd, Object... args) 32631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey throws NativeDaemonConnectorException { 327470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt return execute(DEFAULT_TIMEOUT, cmd, args); 328fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey } 329fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey 330470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt /** 3317b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * Issue the given command to the native daemon and return any {@linke 3327b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * NativeDaemonEvent@isClassContinue()} responses, including the final 3337b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * terminal response. Note that the timeout does not count time in deep 3347b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * sleep. Any arguments must be separated from base command so they can be 3357b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * properly escaped. 336470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt * 337470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt * @throws NativeDaemonConnectorException when problem communicating with 338470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt * native daemon, or if the response matches 339470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt * {@link NativeDaemonEvent#isClassClientError()} or 340470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt * {@link NativeDaemonEvent#isClassServerError()}. 341470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt */ 342470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args) 34331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey throws NativeDaemonConnectorException { 34456cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey final long startTime = SystemClock.elapsedRealtime(); 34556cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey 34631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey final ArrayList<NativeDaemonEvent> events = Lists.newArrayList(); 347d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt 34856cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey final StringBuilder rawBuilder = new StringBuilder(); 34956cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey final StringBuilder logBuilder = new StringBuilder(); 350d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt final int sequenceNumber = mSequenceNumber.incrementAndGet(); 351d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt 35256cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey makeCommand(rawBuilder, logBuilder, sequenceNumber, cmd, args); 353d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt 35456cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey final String rawCmd = rawBuilder.toString(); 35556cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey final String logCmd = logBuilder.toString(); 356d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt 35756cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey log("SND -> {" + logCmd + "}"); 358d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt 359d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt synchronized (mDaemonLock) { 360d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt if (mOutputStream == null) { 361d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt throw new NativeDaemonConnectorException("missing output stream"); 362d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt } else { 363d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt try { 364b8292830f79fc76ffb9a1be5cd316212ac494d03Elliott Hughes mOutputStream.write(rawCmd.getBytes(StandardCharsets.UTF_8)); 365d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt } catch (IOException e) { 366d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt throw new NativeDaemonConnectorException("problem sending command", e); 367d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt } 368d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt } 369d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt } 37067bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 37131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey NativeDaemonEvent event = null; 37231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey do { 37356cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey event = mResponseQueue.remove(sequenceNumber, timeout, logCmd); 374470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt if (event == null) { 375d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt loge("timed-out waiting for response to " + logCmd); 376d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt throw new NativeDaemonFailureException(logCmd, event); 37767bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 378b5aff3fde346225fab0f2751d4fb51b92bb73b32Robert Greenwalt log("RMV <- {" + event + "}"); 37931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey events.add(event); 38031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } while (event.isClassContinue()); 38131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 3825a0c320a820a0ccaafaa87ad858a29bf5d88a8b9Robert Greenwalt final long endTime = SystemClock.elapsedRealtime(); 3835a0c320a820a0ccaafaa87ad858a29bf5d88a8b9Robert Greenwalt if (endTime - startTime > WARN_EXECUTE_DELAY_MS) { 3845a0c320a820a0ccaafaa87ad858a29bf5d88a8b9Robert Greenwalt loge("NDC Command {" + logCmd + "} took too long (" + (endTime - startTime) + "ms)"); 3855a0c320a820a0ccaafaa87ad858a29bf5d88a8b9Robert Greenwalt } 3865a0c320a820a0ccaafaa87ad858a29bf5d88a8b9Robert Greenwalt 38731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey if (event.isClassClientError()) { 388d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt throw new NativeDaemonArgumentException(logCmd, event); 38931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 39031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey if (event.isClassServerError()) { 391d192598d3e7c6f38fc9deb573b06ababa56d741aRobert Greenwalt throw new NativeDaemonFailureException(logCmd, event); 39267bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat } 39367bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat 39431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey return events.toArray(new NativeDaemonEvent[events.size()]); 39531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 39631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 39731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey /** 39831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * Append the given argument to {@link StringBuilder}, escaping as needed, 39931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * and surrounding with quotes when it contains spaces. 40031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey */ 4018b2c3a14603d163d7564e6f60286995079687690Jeff Sharkey @VisibleForTesting 40231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey static void appendEscaped(StringBuilder builder, String arg) { 40331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey final boolean hasSpaces = arg.indexOf(' ') >= 0; 40431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey if (hasSpaces) { 40531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey builder.append('"'); 40631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 40731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 40831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey final int length = arg.length(); 40931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey for (int i = 0; i < length; i++) { 41031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey final char c = arg.charAt(i); 41131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 41231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey if (c == '"') { 41331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey builder.append("\\\""); 41431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } else if (c == '\\') { 41531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey builder.append("\\\\"); 41631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } else { 41731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey builder.append(c); 418deba6935c6595c724416cde3368a92c32d8f8683San Mehat } 419deba6935c6595c724416cde3368a92c32d8f8683San Mehat } 42031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 42131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey if (hasSpaces) { 42231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey builder.append('"'); 42331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 42431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 42531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 42631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private static class NativeDaemonArgumentException extends NativeDaemonConnectorException { 42731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey public NativeDaemonArgumentException(String command, NativeDaemonEvent event) { 42831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey super(command, event); 42931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 43031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 43131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey @Override 43231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey public IllegalArgumentException rethrowAsParcelableException() { 43331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey throw new IllegalArgumentException(getMessage(), this); 43431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 43531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 43631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey 43731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey private static class NativeDaemonFailureException extends NativeDaemonConnectorException { 43831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey public NativeDaemonFailureException(String command, NativeDaemonEvent event) { 43931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey super(command, event); 44031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey } 441deba6935c6595c724416cde3368a92c32d8f8683San Mehat } 442fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey 443ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey /** 4447b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * Command builder that handles argument list building. Any arguments must 4457b4596fd68a24643145e33f3dc4da9285d0f53aaJeff Sharkey * be separated from base command so they can be properly escaped. 446ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey */ 447ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey public static class Command { 448ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey private String mCmd; 449ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey private ArrayList<Object> mArguments = Lists.newArrayList(); 450ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey 451ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey public Command(String cmd, Object... args) { 452ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey mCmd = cmd; 453ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey for (Object arg : args) { 454ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey appendArg(arg); 455ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey } 456ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey } 457ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey 458ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey public Command appendArg(Object arg) { 459ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey mArguments.add(arg); 460ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey return this; 461ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey } 462ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey } 463ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey 464fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey /** {@inheritDoc} */ 465fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey public void monitor() { 466fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey synchronized (mDaemonLock) { } 467fa23c5ae226c1a1d39f89c5c87d4f340e91d90e0Jeff Sharkey } 468470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt 469470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 470470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt mLocalLog.dump(fd, pw, args); 471470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt pw.println(); 472470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt mResponseQueue.dump(fd, pw, args); 473470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt } 474470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt 475470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt private void log(String logstring) { 476470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt if (LOGD) Slog.d(TAG, logstring); 477470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt mLocalLog.log(logstring); 478470fd72a06390d7a6b854583afd0ed76ce0a03eeRobert Greenwalt } 479470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 480470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt private void loge(String logstring) { 481470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt Slog.e(TAG, logstring); 482470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt mLocalLog.log(logstring); 483470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 484470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 485470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt private static class ResponseQueue { 486470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 487ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt private static class PendingCmd { 48856cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey public final int cmdNum; 48956cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey public final String logCmd; 49056cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey 491ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt public BlockingQueue<NativeDaemonEvent> responses = 492ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt new ArrayBlockingQueue<NativeDaemonEvent>(10); 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; 51056cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey 51156cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey public PendingCmd(int cmdNum, String logCmd) { 51256cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey this.cmdNum = cmdNum; 51356cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey this.logCmd = logCmd; 51456cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey } 515470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 516470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 517ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt private final LinkedList<PendingCmd> mPendingCmds; 518470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt private int mMaxCount; 519470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 520470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt ResponseQueue(int maxCount) { 521ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt mPendingCmds = new LinkedList<PendingCmd>(); 522470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt mMaxCount = maxCount; 523470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 524470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 525470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt public void add(int cmdNum, NativeDaemonEvent response) { 526ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt PendingCmd found = null; 527ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt synchronized (mPendingCmds) { 528ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt for (PendingCmd pendingCmd : mPendingCmds) { 529ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt if (pendingCmd.cmdNum == cmdNum) { 530ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt found = pendingCmd; 531470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt break; 532470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 533470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 534470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt if (found == null) { 535470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt // didn't find it - make sure our queue isn't too big before adding 536ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt while (mPendingCmds.size() >= mMaxCount) { 537470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt Slog.e("NativeDaemonConnector.ResponseQueue", 538ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt "more buffered than allowed: " + mPendingCmds.size() + 539470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt " >= " + mMaxCount); 540470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt // let any waiter timeout waiting for this 541ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt PendingCmd pendingCmd = mPendingCmds.remove(); 542470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt Slog.e("NativeDaemonConnector.ResponseQueue", 54356cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey "Removing request: " + pendingCmd.logCmd + " (" + 544ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt pendingCmd.cmdNum + ")"); 545470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 546ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt found = new PendingCmd(cmdNum, null); 547ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt mPendingCmds.add(found); 548470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 549ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt found.availableResponseCount++; 550ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // if a matching remove call has already retrieved this we can remove this 551ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // instance from our list 552ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt if (found.availableResponseCount == 0) mPendingCmds.remove(found); 553470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 554ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt try { 555ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt found.responses.put(response); 556ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt } catch (InterruptedException e) { } 557470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 558470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 559470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt // note that the timeout does not count time in deep sleep. If you don't want 560470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt // the device to sleep, hold a wakelock 56156cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey public NativeDaemonEvent remove(int cmdNum, int timeoutMs, String logCmd) { 562ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt PendingCmd found = null; 563ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt synchronized (mPendingCmds) { 564ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt for (PendingCmd pendingCmd : mPendingCmds) { 565ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt if (pendingCmd.cmdNum == cmdNum) { 566ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt found = pendingCmd; 567ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt break; 568470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 569470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 570ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt if (found == null) { 57156cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey found = new PendingCmd(cmdNum, logCmd); 572ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt mPendingCmds.add(found); 573470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 574ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt found.availableResponseCount--; 575ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // if a matching add call has already retrieved this we can remove this 576ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt // instance from our list 577ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt if (found.availableResponseCount == 0) mPendingCmds.remove(found); 578ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt } 579ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt NativeDaemonEvent result = null; 580ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt try { 581ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt result = found.responses.poll(timeoutMs, TimeUnit.MILLISECONDS); 582ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt } catch (InterruptedException e) {} 583ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt if (result == null) { 584ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt Slog.e("NativeDaemonConnector.ResponseQueue", "Timeout waiting for response"); 585470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 586ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt return result; 587470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 588470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt 589470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 590470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt pw.println("Pending requests:"); 591ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt synchronized (mPendingCmds) { 592ef21599b17bc081321bc49f1fb315c9e7ad31e3aRobert Greenwalt for (PendingCmd pendingCmd : mPendingCmds) { 59356cd646abeae51e806791f82ab0995fe047b1fe4Jeff Sharkey pw.println(" Cmd " + pendingCmd.cmdNum + " - " + pendingCmd.logCmd); 594470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 595470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 596470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 597470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt } 59867bd2cd75f0615c1a08a221f2114d2acda90d1ceSan Mehat} 599