blob: f71125a839a4d1b6f215e0821d9f10902525928c [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 */
Robert Greenwalt5a0c3202012-05-22 16:07:46 -070061 private static final long WARN_EXECUTE_DELAY_MS = 500; /* .5 sec */
Robert Greenwalt470007f2012-02-07 11:36:55 -080062
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -070063 /** Lock held whenever communicating with native daemon. */
Jeff Sharkey31c6e482011-11-18 17:09:01 -080064 private final Object mDaemonLock = new Object();
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -070065
Kenny Root961aa8c2010-03-22 18:02:45 -070066 private final int BUFFER_SIZE = 4096;
67
Jeff Sharkey31c6e482011-11-18 17:09:01 -080068 NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
Robert Greenwalt470fd722012-01-18 12:51:15 -080069 int responseQueueSize, String logTag, int maxLogSize) {
San Mehat67bd2cd2010-01-12 12:18:49 -080070 mCallbacks = callbacks;
San Mehat67bd2cd2010-01-12 12:18:49 -080071 mSocket = socket;
Robert Greenwalt470007f2012-02-07 11:36:55 -080072 mResponseQueue = new ResponseQueue(responseQueueSize);
73 mSequenceNumber = new AtomicInteger(0);
Jeff Sharkey31c6e482011-11-18 17:09:01 -080074 TAG = logTag != null ? logTag : "NativeDaemonConnector";
Robert Greenwalt470fd722012-01-18 12:51:15 -080075 mLocalLog = new LocalLog(maxLogSize);
San Mehat67bd2cd2010-01-12 12:18:49 -080076 }
77
Chia-chi Yehe5750a32011-08-03 14:42:11 -070078 @Override
San Mehat67bd2cd2010-01-12 12:18:49 -080079 public void run() {
Chia-chi Yehe5750a32011-08-03 14:42:11 -070080 HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler");
81 thread.start();
82 mCallbackHandler = new Handler(thread.getLooper(), this);
San Mehat67bd2cd2010-01-12 12:18:49 -080083
84 while (true) {
85 try {
86 listenToSocket();
87 } catch (Exception e) {
Robert Greenwalt470007f2012-02-07 11:36:55 -080088 loge("Error in NativeDaemonConnector: " + e);
San Mehat4c27e0e2010-01-29 05:22:17 -080089 SystemClock.sleep(5000);
San Mehat67bd2cd2010-01-12 12:18:49 -080090 }
91 }
92 }
93
Chia-chi Yehe5750a32011-08-03 14:42:11 -070094 @Override
95 public boolean handleMessage(Message msg) {
96 String event = (String) msg.obj;
97 try {
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -070098 if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) {
Robert Greenwalt470007f2012-02-07 11:36:55 -080099 log(String.format("Unhandled event '%s'", event));
Chia-chi Yehe5750a32011-08-03 14:42:11 -0700100 }
101 } catch (Exception e) {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800102 loge("Error handling '" + event + "': " + e);
Chia-chi Yehe5750a32011-08-03 14:42:11 -0700103 }
104 return true;
105 }
106
San Mehat4c27e0e2010-01-29 05:22:17 -0800107 private void listenToSocket() throws IOException {
Kenny Root961aa8c2010-03-22 18:02:45 -0700108 LocalSocket socket = null;
San Mehat67bd2cd2010-01-12 12:18:49 -0800109
110 try {
111 socket = new LocalSocket();
112 LocalSocketAddress address = new LocalSocketAddress(mSocket,
113 LocalSocketAddress.Namespace.RESERVED);
114
115 socket.connect(address);
San Mehat67bd2cd2010-01-12 12:18:49 -0800116
117 InputStream inputStream = socket.getInputStream();
Robert Greenwalt470007f2012-02-07 11:36:55 -0800118 synchronized (mDaemonLock) {
119 mOutputStream = socket.getOutputStream();
120 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800121
anga030bc882011-02-01 14:10:25 +0100122 mCallbacks.onDaemonConnected();
123
Kenny Root961aa8c2010-03-22 18:02:45 -0700124 byte[] buffer = new byte[BUFFER_SIZE];
125 int start = 0;
San Mehat67bd2cd2010-01-12 12:18:49 -0800126
127 while (true) {
Kenny Root961aa8c2010-03-22 18:02:45 -0700128 int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
Robert Greenwalt470007f2012-02-07 11:36:55 -0800129 if (count < 0) {
130 loge("got " + count + " reading with start = " + start);
131 break;
132 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800133
Kenny Root12da9d72010-09-02 22:18:14 -0700134 // Add our starting point to the count and reset the start.
135 count += start;
136 start = 0;
137
San Mehat67bd2cd2010-01-12 12:18:49 -0800138 for (int i = 0; i < count; i++) {
139 if (buffer[i] == 0) {
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800140 final String rawEvent = new String(
141 buffer, start, i - start, Charsets.UTF_8);
Robert Greenwalt470fd722012-01-18 12:51:15 -0800142 log("RCV <- {" + rawEvent + "}");
San Mehat67bd2cd2010-01-12 12:18:49 -0800143
San Mehat67bd2cd2010-01-12 12:18:49 -0800144 try {
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800145 final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(
146 rawEvent);
147 if (event.isClassUnsolicited()) {
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800148 // TODO: migrate to sending NativeDaemonEvent instances
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800149 mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
150 event.getCode(), event.getRawEvent()));
Irfan Sheriff1cd94ef2011-01-16 14:31:55 -0800151 } else {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800152 mResponseQueue.add(event.getCmdNumber(), event);
San Mehat67bd2cd2010-01-12 12:18:49 -0800153 }
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800154 } catch (IllegalArgumentException e) {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800155 log("Problem parsing message: " + rawEvent + " - " + e);
San Mehat67bd2cd2010-01-12 12:18:49 -0800156 }
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800157
San Mehat67bd2cd2010-01-12 12:18:49 -0800158 start = i + 1;
159 }
160 }
Robert Greenwaltf0be1d892012-01-20 16:33:15 -0800161 if (start == 0) {
162 final String rawEvent = new String(buffer, start, count, Charsets.UTF_8);
163 log("RCV incomplete <- {" + rawEvent + "}");
164 }
Kenny Root12da9d72010-09-02 22:18:14 -0700165
166 // We should end at the amount we read. If not, compact then
167 // buffer and read again.
Kenny Root961aa8c2010-03-22 18:02:45 -0700168 if (start != count) {
169 final int remaining = BUFFER_SIZE - start;
170 System.arraycopy(buffer, start, buffer, 0, remaining);
171 start = remaining;
172 } else {
173 start = 0;
174 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800175 }
176 } catch (IOException ex) {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800177 loge("Communications error: " + ex);
San Mehat4c27e0e2010-01-29 05:22:17 -0800178 throw ex;
179 } finally {
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700180 synchronized (mDaemonLock) {
San Mehat4c27e0e2010-01-29 05:22:17 -0800181 if (mOutputStream != null) {
182 try {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800183 loge("closing stream for " + mSocket);
San Mehat4c27e0e2010-01-29 05:22:17 -0800184 mOutputStream.close();
185 } catch (IOException e) {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800186 loge("Failed closing output stream: " + e);
San Mehat4c27e0e2010-01-29 05:22:17 -0800187 }
188 mOutputStream = null;
San Mehat67bd2cd2010-01-12 12:18:49 -0800189 }
San Mehat4c27e0e2010-01-29 05:22:17 -0800190 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800191
San Mehat4c27e0e2010-01-29 05:22:17 -0800192 try {
193 if (socket != null) {
194 socket.close();
195 }
196 } catch (IOException ex) {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800197 loge("Failed closing socket: " + ex);
San Mehat67bd2cd2010-01-12 12:18:49 -0800198 }
199 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800200 }
201
San Mehat67bd2cd2010-01-12 12:18:49 -0800202 /**
Robert Greenwalt470007f2012-02-07 11:36:55 -0800203 * Make command for daemon, escaping arguments as needed.
San Mehat67bd2cd2010-01-12 12:18:49 -0800204 */
Robert Greenwaltd1925982012-03-12 15:37:40 -0700205 private void makeCommand(StringBuilder builder, String cmd, Object... args)
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700206 throws NativeDaemonConnectorException {
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800207 // TODO: eventually enforce that cmd doesn't contain arguments
208 if (cmd.indexOf('\0') >= 0) {
209 throw new IllegalArgumentException("unexpected command: " + cmd);
Jeff Sharkeyb0aec072011-10-14 18:32:24 -0700210 }
211
Robert Greenwaltd1925982012-03-12 15:37:40 -0700212 builder.append(cmd);
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800213 for (Object arg : args) {
214 final String argString = String.valueOf(arg);
215 if (argString.indexOf('\0') >= 0) {
216 throw new IllegalArgumentException("unexpected argument: " + arg);
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700217 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800218
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800219 builder.append(' ');
220 appendEscaped(builder, argString);
221 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800222 }
San Mehatdeba6932010-01-20 15:14:31 -0800223
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700224 /**
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800225 * Issue the given command to the native daemon and return a single expected
226 * response.
227 *
228 * @throws NativeDaemonConnectorException when problem communicating with
229 * native daemon, or if the response matches
230 * {@link NativeDaemonEvent#isClassClientError()} or
231 * {@link NativeDaemonEvent#isClassServerError()}.
San Mehatdeba6932010-01-20 15:14:31 -0800232 */
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800233 public NativeDaemonEvent execute(Command cmd) throws NativeDaemonConnectorException {
234 return execute(cmd.mCmd, cmd.mArguments.toArray());
235 }
236
237 /**
238 * Issue the given command to the native daemon and return a single expected
239 * response.
240 *
241 * @throws NativeDaemonConnectorException when problem communicating with
242 * native daemon, or if the response matches
243 * {@link NativeDaemonEvent#isClassClientError()} or
244 * {@link NativeDaemonEvent#isClassServerError()}.
245 */
246 public NativeDaemonEvent execute(String cmd, Object... args)
247 throws NativeDaemonConnectorException {
248 final NativeDaemonEvent[] events = executeForList(cmd, args);
249 if (events.length != 1) {
250 throw new NativeDaemonConnectorException(
251 "Expected exactly one response, but received " + events.length);
252 }
253 return events[0];
254 }
255
256 /**
257 * Issue the given command to the native daemon and return any
258 * {@link NativeDaemonEvent#isClassContinue()} responses, including the
259 * final terminal response.
260 *
261 * @throws NativeDaemonConnectorException when problem communicating with
262 * native daemon, or if the response matches
263 * {@link NativeDaemonEvent#isClassClientError()} or
264 * {@link NativeDaemonEvent#isClassServerError()}.
265 */
266 public NativeDaemonEvent[] executeForList(Command cmd) throws NativeDaemonConnectorException {
267 return executeForList(cmd.mCmd, cmd.mArguments.toArray());
268 }
269
270 /**
271 * Issue the given command to the native daemon and return any
272 * {@link NativeDaemonEvent#isClassContinue()} responses, including the
273 * final terminal response.
274 *
275 * @throws NativeDaemonConnectorException when problem communicating with
276 * native daemon, or if the response matches
277 * {@link NativeDaemonEvent#isClassClientError()} or
278 * {@link NativeDaemonEvent#isClassServerError()}.
279 */
280 public NativeDaemonEvent[] executeForList(String cmd, Object... args)
San Mehat4c27e0e2010-01-29 05:22:17 -0800281 throws NativeDaemonConnectorException {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800282 return execute(DEFAULT_TIMEOUT, cmd, args);
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800283 }
San Mehatdeba6932010-01-20 15:14:31 -0800284
Robert Greenwalt470007f2012-02-07 11:36:55 -0800285 /**
286 * Issue the given command to the native daemon and return any
287 * {@linke NativeDaemonEvent@isClassContinue()} responses, including the
288 * final terminal response. Note that the timeout does not count time in
289 * deep sleep.
290 *
291 * @throws NativeDaemonConnectorException when problem communicating with
292 * native daemon, or if the response matches
293 * {@link NativeDaemonEvent#isClassClientError()} or
294 * {@link NativeDaemonEvent#isClassServerError()}.
295 */
296 public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args)
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800297 throws NativeDaemonConnectorException {
298 final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();
Robert Greenwaltd1925982012-03-12 15:37:40 -0700299
300 final int sequenceNumber = mSequenceNumber.incrementAndGet();
301 final StringBuilder cmdBuilder =
302 new StringBuilder(Integer.toString(sequenceNumber)).append(' ');
Robert Greenwalt5a0c3202012-05-22 16:07:46 -0700303 final long startTime = SystemClock.elapsedRealtime();
Robert Greenwaltd1925982012-03-12 15:37:40 -0700304
305 makeCommand(cmdBuilder, cmd, args);
306
307 final String logCmd = cmdBuilder.toString(); /* includes cmdNum, cmd, args */
308 log("SND -> {" + logCmd + "}");
309
310 cmdBuilder.append('\0');
311 final String sentCmd = cmdBuilder.toString(); /* logCmd + \0 */
312
313 synchronized (mDaemonLock) {
314 if (mOutputStream == null) {
315 throw new NativeDaemonConnectorException("missing output stream");
316 } else {
317 try {
318 mOutputStream.write(sentCmd.getBytes(Charsets.UTF_8));
319 } catch (IOException e) {
320 throw new NativeDaemonConnectorException("problem sending command", e);
321 }
322 }
323 }
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800324
325 NativeDaemonEvent event = null;
326 do {
Robert Greenwaltd1925982012-03-12 15:37:40 -0700327 event = mResponseQueue.remove(sequenceNumber, timeout, sentCmd);
Robert Greenwalt470007f2012-02-07 11:36:55 -0800328 if (event == null) {
Robert Greenwaltd1925982012-03-12 15:37:40 -0700329 loge("timed-out waiting for response to " + logCmd);
330 throw new NativeDaemonFailureException(logCmd, event);
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800331 }
Robert Greenwaltb5aff3f2012-05-15 17:26:57 -0700332 log("RMV <- {" + event + "}");
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800333 events.add(event);
334 } while (event.isClassContinue());
335
Robert Greenwalt5a0c3202012-05-22 16:07:46 -0700336 final long endTime = SystemClock.elapsedRealtime();
337 if (endTime - startTime > WARN_EXECUTE_DELAY_MS) {
338 loge("NDC Command {" + logCmd + "} took too long (" + (endTime - startTime) + "ms)");
339 }
340
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800341 if (event.isClassClientError()) {
Robert Greenwaltd1925982012-03-12 15:37:40 -0700342 throw new NativeDaemonArgumentException(logCmd, event);
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800343 }
344 if (event.isClassServerError()) {
Robert Greenwaltd1925982012-03-12 15:37:40 -0700345 throw new NativeDaemonFailureException(logCmd, event);
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800346 }
347
348 return events.toArray(new NativeDaemonEvent[events.size()]);
349 }
350
351 /**
352 * Issue a command to the native daemon and return the raw responses.
353 *
354 * @deprecated callers should move to {@link #execute(String, Object...)}
355 * which returns parsed {@link NativeDaemonEvent}.
356 */
357 @Deprecated
358 public ArrayList<String> doCommand(String cmd) throws NativeDaemonConnectorException {
359 final ArrayList<String> rawEvents = Lists.newArrayList();
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800360 final NativeDaemonEvent[] events = executeForList(cmd);
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800361 for (NativeDaemonEvent event : events) {
362 rawEvents.add(event.getRawEvent());
363 }
364 return rawEvents;
365 }
366
367 /**
368 * Issues a list command and returns the cooked list of all
369 * {@link NativeDaemonEvent#getMessage()} which match requested code.
370 */
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800371 @Deprecated
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800372 public String[] doListCommand(String cmd, int expectedCode)
373 throws NativeDaemonConnectorException {
374 final ArrayList<String> list = Lists.newArrayList();
375
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800376 final NativeDaemonEvent[] events = executeForList(cmd);
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800377 for (int i = 0; i < events.length - 1; i++) {
378 final NativeDaemonEvent event = events[i];
379 final int code = event.getCode();
380 if (code == expectedCode) {
381 list.add(event.getMessage());
382 } else {
San Mehat4c27e0e2010-01-29 05:22:17 -0800383 throw new NativeDaemonConnectorException(
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800384 "unexpected list response " + code + " instead of " + expectedCode);
San Mehatdeba6932010-01-20 15:14:31 -0800385 }
386 }
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800387
388 final NativeDaemonEvent finalEvent = events[events.length - 1];
389 if (!finalEvent.isClassOk()) {
390 throw new NativeDaemonConnectorException("unexpected final event: " + finalEvent);
391 }
392
393 return list.toArray(new String[list.size()]);
394 }
395
396 /**
397 * Append the given argument to {@link StringBuilder}, escaping as needed,
398 * and surrounding with quotes when it contains spaces.
399 */
400 // @VisibleForTesting
401 static void appendEscaped(StringBuilder builder, String arg) {
402 final boolean hasSpaces = arg.indexOf(' ') >= 0;
403 if (hasSpaces) {
404 builder.append('"');
405 }
406
407 final int length = arg.length();
408 for (int i = 0; i < length; i++) {
409 final char c = arg.charAt(i);
410
411 if (c == '"') {
412 builder.append("\\\"");
413 } else if (c == '\\') {
414 builder.append("\\\\");
415 } else {
416 builder.append(c);
417 }
418 }
419
420 if (hasSpaces) {
421 builder.append('"');
422 }
423 }
424
425 private static class NativeDaemonArgumentException extends NativeDaemonConnectorException {
426 public NativeDaemonArgumentException(String command, NativeDaemonEvent event) {
427 super(command, event);
428 }
429
430 @Override
431 public IllegalArgumentException rethrowAsParcelableException() {
432 throw new IllegalArgumentException(getMessage(), this);
433 }
434 }
435
436 private static class NativeDaemonFailureException extends NativeDaemonConnectorException {
437 public NativeDaemonFailureException(String command, NativeDaemonEvent event) {
438 super(command, event);
439 }
San Mehatdeba6932010-01-20 15:14:31 -0800440 }
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700441
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800442 /**
443 * Command builder that handles argument list building.
444 */
445 public static class Command {
446 private String mCmd;
447 private ArrayList<Object> mArguments = Lists.newArrayList();
448
449 public Command(String cmd, Object... args) {
450 mCmd = cmd;
451 for (Object arg : args) {
452 appendArg(arg);
453 }
454 }
455
456 public Command appendArg(Object arg) {
457 mArguments.add(arg);
458 return this;
459 }
460 }
461
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700462 /** {@inheritDoc} */
463 public void monitor() {
464 synchronized (mDaemonLock) { }
465 }
Robert Greenwalt470fd722012-01-18 12:51:15 -0800466
467 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
468 mLocalLog.dump(fd, pw, args);
Robert Greenwalt470007f2012-02-07 11:36:55 -0800469 pw.println();
470 mResponseQueue.dump(fd, pw, args);
Robert Greenwalt470fd722012-01-18 12:51:15 -0800471 }
472
473 private void log(String logstring) {
474 if (LOGD) Slog.d(TAG, logstring);
475 mLocalLog.log(logstring);
476 }
Robert Greenwalt470007f2012-02-07 11:36:55 -0800477
478 private void loge(String logstring) {
479 Slog.e(TAG, logstring);
480 mLocalLog.log(logstring);
481 }
482
483 private static class ResponseQueue {
484
485 private static class Response {
486 public int cmdNum;
487 public LinkedList<NativeDaemonEvent> responses = new LinkedList<NativeDaemonEvent>();
488 public String request;
489 public Response(int c, String r) {cmdNum = c; request = r;}
490 }
491
492 private final LinkedList<Response> mResponses;
493 private int mMaxCount;
494
495 ResponseQueue(int maxCount) {
496 mResponses = new LinkedList<Response>();
497 mMaxCount = maxCount;
498 }
499
500 public void add(int cmdNum, NativeDaemonEvent response) {
501 Response found = null;
502 synchronized (mResponses) {
503 for (Response r : mResponses) {
504 if (r.cmdNum == cmdNum) {
505 found = r;
506 break;
507 }
508 }
509 if (found == null) {
510 // didn't find it - make sure our queue isn't too big before adding
511 // another..
512 while (mResponses.size() >= mMaxCount) {
513 Slog.e("NativeDaemonConnector.ResponseQueue",
514 "more buffered than allowed: " + mResponses.size() +
515 " >= " + mMaxCount);
516 // let any waiter timeout waiting for this
517 Response r = mResponses.remove();
518 Slog.e("NativeDaemonConnector.ResponseQueue",
519 "Removing request: " + r.request + " (" + r.cmdNum + ")");
520 }
521 found = new Response(cmdNum, null);
522 mResponses.add(found);
523 }
524 found.responses.add(response);
525 }
526 synchronized (found) {
527 found.notify();
528 }
529 }
530
531 // note that the timeout does not count time in deep sleep. If you don't want
532 // the device to sleep, hold a wakelock
533 public NativeDaemonEvent remove(int cmdNum, int timeoutMs, String origCmd) {
534 long endTime = SystemClock.uptimeMillis() + timeoutMs;
535 long nowTime;
536 Response found = null;
537 while (true) {
538 synchronized (mResponses) {
539 for (Response response : mResponses) {
540 if (response.cmdNum == cmdNum) {
541 found = response;
542 // how many response fragments are left
543 switch (response.responses.size()) {
544 case 0: // haven't got any - must wait
545 break;
546 case 1: // last one - remove this from the master list
547 mResponses.remove(response); // fall through
548 default: // take one and move on
549 response.request = origCmd;
550 return response.responses.remove();
551 }
552 }
553 }
554 nowTime = SystemClock.uptimeMillis();
555 if (endTime <= nowTime) {
556 Slog.e("NativeDaemonConnector.ResponseQueue",
557 "Timeout waiting for response");
558 return null;
559 }
560 /* pre-allocate so we have something unique to wait on */
561 if (found == null) {
562 found = new Response(cmdNum, origCmd);
563 mResponses.add(found);
564 }
565 }
566 try {
567 synchronized (found) {
568 found.wait(endTime - nowTime);
569 }
570 } catch (InterruptedException e) {
571 // loop around to check if we're done or if it's time to stop waiting
572 }
573 }
574 }
575
576 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
577 pw.println("Pending requests:");
578 synchronized (mResponses) {
579 for (Response response : mResponses) {
580 pw.println(" Cmd " + response.cmdNum + " - " + response.request);
581 }
582 }
583 }
584 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800585}