blob: 6593a0f43d3203d206893ba5c9fa12878ba742c2 [file] [log] [blame]
San Mehat67bd2cd2010-01-12 12:18:49 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
San Mehat67bd2cd2010-01-12 12:18:49 -080019import android.net.LocalSocket;
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -070020import android.net.LocalSocketAddress;
Chia-chi Yehe5750a32011-08-03 14:42:11 -070021import android.os.Handler;
22import android.os.HandlerThread;
23import android.os.Message;
San Mehat67bd2cd2010-01-12 12:18:49 -080024import android.os.SystemClock;
Robert Greenwalt470fd722012-01-18 12:51:15 -080025import android.util.LocalLog;
Joe Onorato8a9b2202010-02-26 18:56:32 -080026import android.util.Slog;
San Mehat67bd2cd2010-01-12 12:18:49 -080027
Jeff Sharkey31c6e482011-11-18 17:09:01 -080028import com.google.android.collect.Lists;
29
Robert Greenwalt470fd722012-01-18 12:51:15 -080030import java.nio.charset.Charsets;
31import java.io.FileDescriptor;
San Mehat67bd2cd2010-01-12 12:18:49 -080032import java.io.IOException;
33import java.io.InputStream;
34import java.io.OutputStream;
Robert Greenwalt470fd722012-01-18 12:51:15 -080035import java.io.PrintWriter;
San Mehat67bd2cd2010-01-12 12:18:49 -080036import java.util.ArrayList;
Robert Greenwalt470007f2012-02-07 11:36:55 -080037import java.util.concurrent.atomic.AtomicInteger;
38import java.util.LinkedList;
San Mehat67bd2cd2010-01-12 12:18:49 -080039
40/**
Jeff Sharkey31c6e482011-11-18 17:09:01 -080041 * Generic connector class for interfacing with a native daemon which uses the
42 * {@code libsysutils} FrameworkListener protocol.
San Mehat67bd2cd2010-01-12 12:18:49 -080043 */
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -070044final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
Jeff Sharkey31c6e482011-11-18 17:09:01 -080045 private static final boolean LOGD = false;
San Mehat67bd2cd2010-01-12 12:18:49 -080046
Jeff Sharkey31c6e482011-11-18 17:09:01 -080047 private final String TAG;
48
49 private String mSocket;
50 private OutputStream mOutputStream;
Robert Greenwalt470fd722012-01-18 12:51:15 -080051 private LocalLog mLocalLog;
Jeff Sharkey31c6e482011-11-18 17:09:01 -080052
Robert Greenwalt470007f2012-02-07 11:36:55 -080053 private final ResponseQueue mResponseQueue;
Jeff Sharkey31c6e482011-11-18 17:09:01 -080054
San Mehat67bd2cd2010-01-12 12:18:49 -080055 private INativeDaemonConnectorCallbacks mCallbacks;
Jeff Sharkey31c6e482011-11-18 17:09:01 -080056 private Handler mCallbackHandler;
San Mehat67bd2cd2010-01-12 12:18:49 -080057
Robert Greenwalt470007f2012-02-07 11:36:55 -080058 private AtomicInteger mSequenceNumber;
59
60 private static final int DEFAULT_TIMEOUT = 1 * 60 * 1000; /* 1 minute */
61
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -070062 /** Lock held whenever communicating with native daemon. */
Jeff Sharkey31c6e482011-11-18 17:09:01 -080063 private final Object mDaemonLock = new Object();
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -070064
Kenny Root961aa8c2010-03-22 18:02:45 -070065 private final int BUFFER_SIZE = 4096;
66
Jeff Sharkey31c6e482011-11-18 17:09:01 -080067 NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
Robert Greenwalt470fd722012-01-18 12:51:15 -080068 int responseQueueSize, String logTag, int maxLogSize) {
San Mehat67bd2cd2010-01-12 12:18:49 -080069 mCallbacks = callbacks;
San Mehat67bd2cd2010-01-12 12:18:49 -080070 mSocket = socket;
Robert Greenwalt470007f2012-02-07 11:36:55 -080071 mResponseQueue = new ResponseQueue(responseQueueSize);
72 mSequenceNumber = new AtomicInteger(0);
Jeff Sharkey31c6e482011-11-18 17:09:01 -080073 TAG = logTag != null ? logTag : "NativeDaemonConnector";
Robert Greenwalt470fd722012-01-18 12:51:15 -080074 mLocalLog = new LocalLog(maxLogSize);
San Mehat67bd2cd2010-01-12 12:18:49 -080075 }
76
Chia-chi Yehe5750a32011-08-03 14:42:11 -070077 @Override
San Mehat67bd2cd2010-01-12 12:18:49 -080078 public void run() {
Chia-chi Yehe5750a32011-08-03 14:42:11 -070079 HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler");
80 thread.start();
81 mCallbackHandler = new Handler(thread.getLooper(), this);
San Mehat67bd2cd2010-01-12 12:18:49 -080082
83 while (true) {
84 try {
85 listenToSocket();
86 } catch (Exception e) {
Robert Greenwalt470007f2012-02-07 11:36:55 -080087 loge("Error in NativeDaemonConnector: " + e);
San Mehat4c27e0e2010-01-29 05:22:17 -080088 SystemClock.sleep(5000);
San Mehat67bd2cd2010-01-12 12:18:49 -080089 }
90 }
91 }
92
Chia-chi Yehe5750a32011-08-03 14:42:11 -070093 @Override
94 public boolean handleMessage(Message msg) {
95 String event = (String) msg.obj;
96 try {
97 if (!mCallbacks.onEvent(msg.what, event, event.split(" "))) {
Robert Greenwalt470007f2012-02-07 11:36:55 -080098 log(String.format("Unhandled event '%s'", event));
Chia-chi Yehe5750a32011-08-03 14:42:11 -070099 }
100 } catch (Exception e) {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800101 loge("Error handling '" + event + "': " + e);
Chia-chi Yehe5750a32011-08-03 14:42:11 -0700102 }
103 return true;
104 }
105
San Mehat4c27e0e2010-01-29 05:22:17 -0800106 private void listenToSocket() throws IOException {
Kenny Root961aa8c2010-03-22 18:02:45 -0700107 LocalSocket socket = null;
San Mehat67bd2cd2010-01-12 12:18:49 -0800108
109 try {
110 socket = new LocalSocket();
111 LocalSocketAddress address = new LocalSocketAddress(mSocket,
112 LocalSocketAddress.Namespace.RESERVED);
113
114 socket.connect(address);
San Mehat67bd2cd2010-01-12 12:18:49 -0800115
116 InputStream inputStream = socket.getInputStream();
Robert Greenwalt470007f2012-02-07 11:36:55 -0800117 synchronized (mDaemonLock) {
118 mOutputStream = socket.getOutputStream();
119 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800120
anga030bc882011-02-01 14:10:25 +0100121 mCallbacks.onDaemonConnected();
122
Kenny Root961aa8c2010-03-22 18:02:45 -0700123 byte[] buffer = new byte[BUFFER_SIZE];
124 int start = 0;
San Mehat67bd2cd2010-01-12 12:18:49 -0800125
126 while (true) {
Kenny Root961aa8c2010-03-22 18:02:45 -0700127 int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
Robert Greenwalt470007f2012-02-07 11:36:55 -0800128 if (count < 0) {
129 loge("got " + count + " reading with start = " + start);
130 break;
131 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800132
Kenny Root12da9d72010-09-02 22:18:14 -0700133 // Add our starting point to the count and reset the start.
134 count += start;
135 start = 0;
136
San Mehat67bd2cd2010-01-12 12:18:49 -0800137 for (int i = 0; i < count; i++) {
138 if (buffer[i] == 0) {
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800139 final String rawEvent = new String(
140 buffer, start, i - start, Charsets.UTF_8);
Robert Greenwalt470fd722012-01-18 12:51:15 -0800141 log("RCV <- {" + rawEvent + "}");
San Mehat67bd2cd2010-01-12 12:18:49 -0800142
San Mehat67bd2cd2010-01-12 12:18:49 -0800143 try {
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800144 final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(
145 rawEvent);
146 if (event.isClassUnsolicited()) {
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800147 // TODO: migrate to sending NativeDaemonEvent instances
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800148 mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
149 event.getCode(), event.getRawEvent()));
Irfan Sheriff1cd94ef2011-01-16 14:31:55 -0800150 } else {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800151 mResponseQueue.add(event.getCmdNumber(), event);
San Mehat67bd2cd2010-01-12 12:18:49 -0800152 }
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800153 } catch (IllegalArgumentException e) {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800154 log("Problem parsing message: " + rawEvent + " - " + e);
San Mehat67bd2cd2010-01-12 12:18:49 -0800155 }
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800156
San Mehat67bd2cd2010-01-12 12:18:49 -0800157 start = i + 1;
158 }
159 }
Robert Greenwaltf0be1d892012-01-20 16:33:15 -0800160 if (start == 0) {
161 final String rawEvent = new String(buffer, start, count, Charsets.UTF_8);
162 log("RCV incomplete <- {" + rawEvent + "}");
163 }
Kenny Root12da9d72010-09-02 22:18:14 -0700164
165 // We should end at the amount we read. If not, compact then
166 // buffer and read again.
Kenny Root961aa8c2010-03-22 18:02:45 -0700167 if (start != count) {
168 final int remaining = BUFFER_SIZE - start;
169 System.arraycopy(buffer, start, buffer, 0, remaining);
170 start = remaining;
171 } else {
172 start = 0;
173 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800174 }
175 } catch (IOException ex) {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800176 loge("Communications error: " + ex);
San Mehat4c27e0e2010-01-29 05:22:17 -0800177 throw ex;
178 } finally {
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700179 synchronized (mDaemonLock) {
San Mehat4c27e0e2010-01-29 05:22:17 -0800180 if (mOutputStream != null) {
181 try {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800182 loge("closing stream for " + mSocket);
San Mehat4c27e0e2010-01-29 05:22:17 -0800183 mOutputStream.close();
184 } catch (IOException e) {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800185 loge("Failed closing output stream: " + e);
San Mehat4c27e0e2010-01-29 05:22:17 -0800186 }
187 mOutputStream = null;
San Mehat67bd2cd2010-01-12 12:18:49 -0800188 }
San Mehat4c27e0e2010-01-29 05:22:17 -0800189 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800190
San Mehat4c27e0e2010-01-29 05:22:17 -0800191 try {
192 if (socket != null) {
193 socket.close();
194 }
195 } catch (IOException ex) {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800196 loge("Failed closing socket: " + ex);
San Mehat67bd2cd2010-01-12 12:18:49 -0800197 }
198 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800199 }
200
San Mehat67bd2cd2010-01-12 12:18:49 -0800201 /**
Robert Greenwalt470007f2012-02-07 11:36:55 -0800202 * Make command for daemon, escaping arguments as needed.
San Mehat67bd2cd2010-01-12 12:18:49 -0800203 *
Robert Greenwalt470007f2012-02-07 11:36:55 -0800204 * @return the final command.
San Mehat67bd2cd2010-01-12 12:18:49 -0800205 */
Robert Greenwalt470007f2012-02-07 11:36:55 -0800206 private StringBuilder makeCommand(String cmd, Object... args)
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700207 throws NativeDaemonConnectorException {
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800208 // TODO: eventually enforce that cmd doesn't contain arguments
209 if (cmd.indexOf('\0') >= 0) {
210 throw new IllegalArgumentException("unexpected command: " + cmd);
Jeff Sharkeyb0aec072011-10-14 18:32:24 -0700211 }
212
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800213 final StringBuilder builder = new StringBuilder(cmd);
214 for (Object arg : args) {
215 final String argString = String.valueOf(arg);
216 if (argString.indexOf('\0') >= 0) {
217 throw new IllegalArgumentException("unexpected argument: " + arg);
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700218 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800219
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800220 builder.append(' ');
221 appendEscaped(builder, argString);
222 }
223
Robert Greenwalt470007f2012-02-07 11:36:55 -0800224 return builder;
225 }
226
227 private int sendCommand(StringBuilder builder)
228 throws NativeDaemonConnectorException {
229
230 int sequenceNumber = mSequenceNumber.incrementAndGet();
231
232 builder.insert(0, Integer.toString(sequenceNumber) + " ");
233
234 log("SND -> {" + builder.toString() + "}");
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800235
236 builder.append('\0');
237
Robert Greenwalt470007f2012-02-07 11:36:55 -0800238 synchronized (mDaemonLock) {
239 if (mOutputStream == null) {
240 throw new NativeDaemonConnectorException("missing output stream");
241 } else {
242 try {
243 mOutputStream.write(builder.toString().getBytes(Charsets.UTF_8));
244 } catch (IOException e) {
245 throw new NativeDaemonConnectorException("problem sending command", e);
246 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800247 }
248 }
249
Robert Greenwalt470007f2012-02-07 11:36:55 -0800250 return sequenceNumber;
San Mehat67bd2cd2010-01-12 12:18:49 -0800251 }
San Mehatdeba6932010-01-20 15:14:31 -0800252
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700253 /**
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800254 * Issue the given command to the native daemon and return a single expected
255 * response.
256 *
257 * @throws NativeDaemonConnectorException when problem communicating with
258 * native daemon, or if the response matches
259 * {@link NativeDaemonEvent#isClassClientError()} or
260 * {@link NativeDaemonEvent#isClassServerError()}.
San Mehatdeba6932010-01-20 15:14:31 -0800261 */
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800262 public NativeDaemonEvent execute(Command cmd) throws NativeDaemonConnectorException {
263 return execute(cmd.mCmd, cmd.mArguments.toArray());
264 }
265
266 /**
267 * Issue the given command to the native daemon and return a single expected
268 * response.
269 *
270 * @throws NativeDaemonConnectorException when problem communicating with
271 * native daemon, or if the response matches
272 * {@link NativeDaemonEvent#isClassClientError()} or
273 * {@link NativeDaemonEvent#isClassServerError()}.
274 */
275 public NativeDaemonEvent execute(String cmd, Object... args)
276 throws NativeDaemonConnectorException {
277 final NativeDaemonEvent[] events = executeForList(cmd, args);
278 if (events.length != 1) {
279 throw new NativeDaemonConnectorException(
280 "Expected exactly one response, but received " + events.length);
281 }
282 return events[0];
283 }
284
285 /**
286 * Issue the given command to the native daemon and return any
287 * {@link NativeDaemonEvent#isClassContinue()} responses, including the
288 * final terminal response.
289 *
290 * @throws NativeDaemonConnectorException when problem communicating with
291 * native daemon, or if the response matches
292 * {@link NativeDaemonEvent#isClassClientError()} or
293 * {@link NativeDaemonEvent#isClassServerError()}.
294 */
295 public NativeDaemonEvent[] executeForList(Command cmd) throws NativeDaemonConnectorException {
296 return executeForList(cmd.mCmd, cmd.mArguments.toArray());
297 }
298
299 /**
300 * Issue the given command to the native daemon and return any
301 * {@link NativeDaemonEvent#isClassContinue()} responses, including the
302 * final terminal response.
303 *
304 * @throws NativeDaemonConnectorException when problem communicating with
305 * native daemon, or if the response matches
306 * {@link NativeDaemonEvent#isClassClientError()} or
307 * {@link NativeDaemonEvent#isClassServerError()}.
308 */
309 public NativeDaemonEvent[] executeForList(String cmd, Object... args)
San Mehat4c27e0e2010-01-29 05:22:17 -0800310 throws NativeDaemonConnectorException {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800311 return execute(DEFAULT_TIMEOUT, cmd, args);
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800312 }
San Mehatdeba6932010-01-20 15:14:31 -0800313
Robert Greenwalt470007f2012-02-07 11:36:55 -0800314 /**
315 * Issue the given command to the native daemon and return any
316 * {@linke NativeDaemonEvent@isClassContinue()} responses, including the
317 * final terminal response. Note that the timeout does not count time in
318 * deep sleep.
319 *
320 * @throws NativeDaemonConnectorException when problem communicating with
321 * native daemon, or if the response matches
322 * {@link NativeDaemonEvent#isClassClientError()} or
323 * {@link NativeDaemonEvent#isClassServerError()}.
324 */
325 public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args)
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800326 throws NativeDaemonConnectorException {
327 final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();
Robert Greenwalt470007f2012-02-07 11:36:55 -0800328 final StringBuilder sentCommand = makeCommand(cmd, args);
329 final int cmdNumber = sendCommand(sentCommand);
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800330
331 NativeDaemonEvent event = null;
Robert Greenwalt470007f2012-02-07 11:36:55 -0800332 cmd = sentCommand.toString();
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800333 do {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800334 event = mResponseQueue.remove(cmdNumber, timeout, cmd);
335 if (event == null) {
336 loge("timed-out waiting for response to " + cmdNumber + " " + cmd);
337 throw new NativeDaemonFailureException(cmd, event);
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800338 }
339 events.add(event);
340 } while (event.isClassContinue());
341
342 if (event.isClassClientError()) {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800343 throw new NativeDaemonArgumentException(cmd, event);
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800344 }
345 if (event.isClassServerError()) {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800346 throw new NativeDaemonFailureException(cmd, event);
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800347 }
348
349 return events.toArray(new NativeDaemonEvent[events.size()]);
350 }
351
352 /**
353 * Issue a command to the native daemon and return the raw responses.
354 *
355 * @deprecated callers should move to {@link #execute(String, Object...)}
356 * which returns parsed {@link NativeDaemonEvent}.
357 */
358 @Deprecated
359 public ArrayList<String> doCommand(String cmd) throws NativeDaemonConnectorException {
360 final ArrayList<String> rawEvents = Lists.newArrayList();
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800361 final NativeDaemonEvent[] events = executeForList(cmd);
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800362 for (NativeDaemonEvent event : events) {
363 rawEvents.add(event.getRawEvent());
364 }
365 return rawEvents;
366 }
367
368 /**
369 * Issues a list command and returns the cooked list of all
370 * {@link NativeDaemonEvent#getMessage()} which match requested code.
371 */
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800372 @Deprecated
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800373 public String[] doListCommand(String cmd, int expectedCode)
374 throws NativeDaemonConnectorException {
375 final ArrayList<String> list = Lists.newArrayList();
376
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800377 final NativeDaemonEvent[] events = executeForList(cmd);
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800378 for (int i = 0; i < events.length - 1; i++) {
379 final NativeDaemonEvent event = events[i];
380 final int code = event.getCode();
381 if (code == expectedCode) {
382 list.add(event.getMessage());
383 } else {
San Mehat4c27e0e2010-01-29 05:22:17 -0800384 throw new NativeDaemonConnectorException(
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800385 "unexpected list response " + code + " instead of " + expectedCode);
San Mehatdeba6932010-01-20 15:14:31 -0800386 }
387 }
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800388
389 final NativeDaemonEvent finalEvent = events[events.length - 1];
390 if (!finalEvent.isClassOk()) {
391 throw new NativeDaemonConnectorException("unexpected final event: " + finalEvent);
392 }
393
394 return list.toArray(new String[list.size()]);
395 }
396
397 /**
398 * Append the given argument to {@link StringBuilder}, escaping as needed,
399 * and surrounding with quotes when it contains spaces.
400 */
401 // @VisibleForTesting
402 static void appendEscaped(StringBuilder builder, String arg) {
403 final boolean hasSpaces = arg.indexOf(' ') >= 0;
404 if (hasSpaces) {
405 builder.append('"');
406 }
407
408 final int length = arg.length();
409 for (int i = 0; i < length; i++) {
410 final char c = arg.charAt(i);
411
412 if (c == '"') {
413 builder.append("\\\"");
414 } else if (c == '\\') {
415 builder.append("\\\\");
416 } else {
417 builder.append(c);
418 }
419 }
420
421 if (hasSpaces) {
422 builder.append('"');
423 }
424 }
425
426 private static class NativeDaemonArgumentException extends NativeDaemonConnectorException {
427 public NativeDaemonArgumentException(String command, NativeDaemonEvent event) {
428 super(command, event);
429 }
430
431 @Override
432 public IllegalArgumentException rethrowAsParcelableException() {
433 throw new IllegalArgumentException(getMessage(), this);
434 }
435 }
436
437 private static class NativeDaemonFailureException extends NativeDaemonConnectorException {
438 public NativeDaemonFailureException(String command, NativeDaemonEvent event) {
439 super(command, event);
440 }
San Mehatdeba6932010-01-20 15:14:31 -0800441 }
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700442
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800443 /**
444 * Command builder that handles argument list building.
445 */
446 public static class Command {
447 private String mCmd;
448 private ArrayList<Object> mArguments = Lists.newArrayList();
449
450 public Command(String cmd, Object... args) {
451 mCmd = cmd;
452 for (Object arg : args) {
453 appendArg(arg);
454 }
455 }
456
457 public Command appendArg(Object arg) {
458 mArguments.add(arg);
459 return this;
460 }
461 }
462
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700463 /** {@inheritDoc} */
464 public void monitor() {
465 synchronized (mDaemonLock) { }
466 }
Robert Greenwalt470fd722012-01-18 12:51:15 -0800467
468 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
469 mLocalLog.dump(fd, pw, args);
Robert Greenwalt470007f2012-02-07 11:36:55 -0800470 pw.println();
471 mResponseQueue.dump(fd, pw, args);
Robert Greenwalt470fd722012-01-18 12:51:15 -0800472 }
473
474 private void log(String logstring) {
475 if (LOGD) Slog.d(TAG, logstring);
476 mLocalLog.log(logstring);
477 }
Robert Greenwalt470007f2012-02-07 11:36:55 -0800478
479 private void loge(String logstring) {
480 Slog.e(TAG, logstring);
481 mLocalLog.log(logstring);
482 }
483
484 private static class ResponseQueue {
485
486 private static class Response {
487 public int cmdNum;
488 public LinkedList<NativeDaemonEvent> responses = new LinkedList<NativeDaemonEvent>();
489 public String request;
490 public Response(int c, String r) {cmdNum = c; request = r;}
491 }
492
493 private final LinkedList<Response> mResponses;
494 private int mMaxCount;
495
496 ResponseQueue(int maxCount) {
497 mResponses = new LinkedList<Response>();
498 mMaxCount = maxCount;
499 }
500
501 public void add(int cmdNum, NativeDaemonEvent response) {
502 Response found = null;
503 synchronized (mResponses) {
504 for (Response r : mResponses) {
505 if (r.cmdNum == cmdNum) {
506 found = r;
507 break;
508 }
509 }
510 if (found == null) {
511 // didn't find it - make sure our queue isn't too big before adding
512 // another..
513 while (mResponses.size() >= mMaxCount) {
514 Slog.e("NativeDaemonConnector.ResponseQueue",
515 "more buffered than allowed: " + mResponses.size() +
516 " >= " + mMaxCount);
517 // let any waiter timeout waiting for this
518 Response r = mResponses.remove();
519 Slog.e("NativeDaemonConnector.ResponseQueue",
520 "Removing request: " + r.request + " (" + r.cmdNum + ")");
521 }
522 found = new Response(cmdNum, null);
523 mResponses.add(found);
524 }
525 found.responses.add(response);
526 }
527 synchronized (found) {
528 found.notify();
529 }
530 }
531
532 // note that the timeout does not count time in deep sleep. If you don't want
533 // the device to sleep, hold a wakelock
534 public NativeDaemonEvent remove(int cmdNum, int timeoutMs, String origCmd) {
535 long endTime = SystemClock.uptimeMillis() + timeoutMs;
536 long nowTime;
537 Response found = null;
538 while (true) {
539 synchronized (mResponses) {
540 for (Response response : mResponses) {
541 if (response.cmdNum == cmdNum) {
542 found = response;
543 // how many response fragments are left
544 switch (response.responses.size()) {
545 case 0: // haven't got any - must wait
546 break;
547 case 1: // last one - remove this from the master list
548 mResponses.remove(response); // fall through
549 default: // take one and move on
550 response.request = origCmd;
551 return response.responses.remove();
552 }
553 }
554 }
555 nowTime = SystemClock.uptimeMillis();
556 if (endTime <= nowTime) {
557 Slog.e("NativeDaemonConnector.ResponseQueue",
558 "Timeout waiting for response");
559 return null;
560 }
561 /* pre-allocate so we have something unique to wait on */
562 if (found == null) {
563 found = new Response(cmdNum, origCmd);
564 mResponses.add(found);
565 }
566 }
567 try {
568 synchronized (found) {
569 found.wait(endTime - nowTime);
570 }
571 } catch (InterruptedException e) {
572 // loop around to check if we're done or if it's time to stop waiting
573 }
574 }
575 }
576
577 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
578 pw.println("Pending requests:");
579 synchronized (mResponses) {
580 for (Response response : mResponses) {
581 pw.println(" Cmd " + response.cmdNum + " - " + response.request);
582 }
583 }
584 }
585 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800586}