Merge "Remove NativeDaemonConnector relevant files"
This commit is contained in:
@@ -1,25 +0,0 @@
|
|||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2007 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.server;
|
|
||||||
|
|
||||||
interface INativeDaemonConnectorCallbacks {
|
|
||||||
|
|
||||||
void onDaemonConnected();
|
|
||||||
boolean onCheckHoldWakeLock(int code);
|
|
||||||
boolean onEvent(int code, String raw, String[] cooked);
|
|
||||||
}
|
|
||||||
@@ -1,704 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2007 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.server;
|
|
||||||
|
|
||||||
import android.net.LocalSocket;
|
|
||||||
import android.net.LocalSocketAddress;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.HandlerThread;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.os.PowerManager;
|
|
||||||
import android.os.SystemClock;
|
|
||||||
import android.util.LocalLog;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generic connector class for interfacing with a native daemon which uses the
|
|
||||||
* {@code libsysutils} FrameworkListener protocol.
|
|
||||||
*/
|
|
||||||
final class NativeDaemonConnector implements Runnable, Handler.Callback {
|
|
||||||
private final static boolean VDBG = false;
|
|
||||||
|
|
||||||
private final String TAG;
|
|
||||||
|
|
||||||
private String mSocket;
|
|
||||||
private OutputStream mOutputStream;
|
|
||||||
private LocalLog mLocalLog;
|
|
||||||
|
|
||||||
private volatile boolean mDebug = false;
|
|
||||||
private volatile Object mWarnIfHeld;
|
|
||||||
|
|
||||||
private final ResponseQueue mResponseQueue;
|
|
||||||
|
|
||||||
private final PowerManager.WakeLock mWakeLock;
|
|
||||||
|
|
||||||
private final Looper mLooper;
|
|
||||||
|
|
||||||
private INativeDaemonConnectorCallbacks mCallbacks;
|
|
||||||
private Handler mCallbackHandler;
|
|
||||||
|
|
||||||
private AtomicInteger mSequenceNumber;
|
|
||||||
|
|
||||||
private static final long DEFAULT_TIMEOUT = 1 * 60 * 1000; /* 1 minute */
|
|
||||||
private static final long WARN_EXECUTE_DELAY_MS = 500; /* .5 sec */
|
|
||||||
|
|
||||||
/** Lock held whenever communicating with native daemon. */
|
|
||||||
private final Object mDaemonLock = new Object();
|
|
||||||
|
|
||||||
private final int BUFFER_SIZE = 4096;
|
|
||||||
|
|
||||||
NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
|
|
||||||
int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl) {
|
|
||||||
mCallbacks = callbacks;
|
|
||||||
mSocket = socket;
|
|
||||||
mResponseQueue = new ResponseQueue(responseQueueSize);
|
|
||||||
mWakeLock = wl;
|
|
||||||
if (mWakeLock != null) {
|
|
||||||
mWakeLock.setReferenceCounted(true);
|
|
||||||
}
|
|
||||||
mSequenceNumber = new AtomicInteger(0);
|
|
||||||
TAG = logTag != null ? logTag : "NativeDaemonConnector";
|
|
||||||
mLocalLog = new LocalLog(maxLogSize);
|
|
||||||
final HandlerThread thread = new HandlerThread(TAG);
|
|
||||||
thread.start();
|
|
||||||
mLooper = thread.getLooper();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable Set debugging mode, which causes messages to also be written to both
|
|
||||||
* {@link Log} in addition to internal log.
|
|
||||||
*/
|
|
||||||
public void setDebug(boolean debug) {
|
|
||||||
mDebug = debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Like SystemClock.uptimeMillis, except truncated to an int so it will fit in a message arg.
|
|
||||||
* Inaccurate across 49.7 days of uptime, but only used for debugging.
|
|
||||||
*/
|
|
||||||
private int uptimeMillisInt() {
|
|
||||||
return (int) SystemClock.uptimeMillis() & Integer.MAX_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Yell loudly if someone tries making future {@link #execute(Command)}
|
|
||||||
* calls while holding a lock on the given object.
|
|
||||||
*/
|
|
||||||
public void setWarnIfHeld(Object warnIfHeld) {
|
|
||||||
if (mWarnIfHeld != null) {
|
|
||||||
throw new IllegalStateException("warnIfHeld is already set.");
|
|
||||||
}
|
|
||||||
mWarnIfHeld = Objects.requireNonNull(warnIfHeld);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
mCallbackHandler = new Handler(mLooper, this);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
listenToSocket();
|
|
||||||
} catch (Exception e) {
|
|
||||||
loge("Error in NativeDaemonConnector: " + e);
|
|
||||||
SystemClock.sleep(5000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean handleMessage(Message msg) {
|
|
||||||
final String event = (String) msg.obj;
|
|
||||||
final int start = uptimeMillisInt();
|
|
||||||
final int sent = msg.arg1;
|
|
||||||
try {
|
|
||||||
if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) {
|
|
||||||
log(String.format("Unhandled event '%s'", event));
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
loge("Error handling '" + event + "': " + e);
|
|
||||||
} finally {
|
|
||||||
if (mCallbacks.onCheckHoldWakeLock(msg.what) && mWakeLock != null) {
|
|
||||||
mWakeLock.release();
|
|
||||||
}
|
|
||||||
final int end = uptimeMillisInt();
|
|
||||||
if (start > sent && start - sent > WARN_EXECUTE_DELAY_MS) {
|
|
||||||
loge(String.format("NDC event {%s} processed too late: %dms", event, start - sent));
|
|
||||||
}
|
|
||||||
if (end > start && end - start > WARN_EXECUTE_DELAY_MS) {
|
|
||||||
loge(String.format("NDC event {%s} took too long: %dms", event, end - start));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private LocalSocketAddress determineSocketAddress() {
|
|
||||||
// If we're testing, set up a socket in a namespace that's accessible to test code.
|
|
||||||
// In order to ensure that unprivileged apps aren't able to impersonate native daemons on
|
|
||||||
// production devices, even if said native daemons ill-advisedly pick a socket name that
|
|
||||||
// starts with __test__, only allow this on debug builds.
|
|
||||||
if (mSocket.startsWith("__test__") && Build.isDebuggable()) {
|
|
||||||
return new LocalSocketAddress(mSocket);
|
|
||||||
} else {
|
|
||||||
return new LocalSocketAddress(mSocket, LocalSocketAddress.Namespace.RESERVED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void listenToSocket() throws IOException {
|
|
||||||
LocalSocket socket = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
socket = new LocalSocket();
|
|
||||||
LocalSocketAddress address = determineSocketAddress();
|
|
||||||
|
|
||||||
socket.connect(address);
|
|
||||||
|
|
||||||
InputStream inputStream = socket.getInputStream();
|
|
||||||
synchronized (mDaemonLock) {
|
|
||||||
mOutputStream = socket.getOutputStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
mCallbacks.onDaemonConnected();
|
|
||||||
|
|
||||||
FileDescriptor[] fdList = null;
|
|
||||||
byte[] buffer = new byte[BUFFER_SIZE];
|
|
||||||
int start = 0;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
|
|
||||||
if (count < 0) {
|
|
||||||
loge("got " + count + " reading with start = " + start);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
fdList = socket.getAncillaryFileDescriptors();
|
|
||||||
|
|
||||||
// Add our starting point to the count and reset the start.
|
|
||||||
count += start;
|
|
||||||
start = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
if (buffer[i] == 0) {
|
|
||||||
// Note - do not log this raw message since it may contain
|
|
||||||
// sensitive data
|
|
||||||
final String rawEvent = new String(
|
|
||||||
buffer, start, i - start, StandardCharsets.UTF_8);
|
|
||||||
|
|
||||||
boolean releaseWl = false;
|
|
||||||
try {
|
|
||||||
final NativeDaemonEvent event =
|
|
||||||
NativeDaemonEvent.parseRawEvent(rawEvent, fdList);
|
|
||||||
|
|
||||||
log("RCV <- {" + event + "}");
|
|
||||||
|
|
||||||
if (event.isClassUnsolicited()) {
|
|
||||||
// TODO: migrate to sending NativeDaemonEvent instances
|
|
||||||
if (mCallbacks.onCheckHoldWakeLock(event.getCode())
|
|
||||||
&& mWakeLock != null) {
|
|
||||||
mWakeLock.acquire();
|
|
||||||
releaseWl = true;
|
|
||||||
}
|
|
||||||
Message msg = mCallbackHandler.obtainMessage(
|
|
||||||
event.getCode(), uptimeMillisInt(), 0, event.getRawEvent());
|
|
||||||
if (mCallbackHandler.sendMessage(msg)) {
|
|
||||||
releaseWl = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mResponseQueue.add(event.getCmdNumber(), event);
|
|
||||||
}
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
log("Problem parsing message " + e);
|
|
||||||
} finally {
|
|
||||||
if (releaseWl) {
|
|
||||||
mWakeLock.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
start = i + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start == 0) {
|
|
||||||
log("RCV incomplete");
|
|
||||||
}
|
|
||||||
|
|
||||||
// We should end at the amount we read. If not, compact then
|
|
||||||
// buffer and read again.
|
|
||||||
if (start != count) {
|
|
||||||
final int remaining = BUFFER_SIZE - start;
|
|
||||||
System.arraycopy(buffer, start, buffer, 0, remaining);
|
|
||||||
start = remaining;
|
|
||||||
} else {
|
|
||||||
start = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
|
||||||
loge("Communications error: " + ex);
|
|
||||||
throw ex;
|
|
||||||
} finally {
|
|
||||||
synchronized (mDaemonLock) {
|
|
||||||
if (mOutputStream != null) {
|
|
||||||
try {
|
|
||||||
loge("closing stream for " + mSocket);
|
|
||||||
mOutputStream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
loge("Failed closing output stream: " + e);
|
|
||||||
}
|
|
||||||
mOutputStream = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (socket != null) {
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
|
||||||
loge("Failed closing socket: " + ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper around argument that indicates it's sensitive and shouldn't be
|
|
||||||
* logged.
|
|
||||||
*/
|
|
||||||
public static class SensitiveArg {
|
|
||||||
private final Object mArg;
|
|
||||||
|
|
||||||
public SensitiveArg(Object arg) {
|
|
||||||
mArg = arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return String.valueOf(mArg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make command for daemon, escaping arguments as needed.
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
static void makeCommand(StringBuilder rawBuilder, StringBuilder logBuilder, int sequenceNumber,
|
|
||||||
String cmd, Object... args) {
|
|
||||||
if (cmd.indexOf('\0') >= 0) {
|
|
||||||
throw new IllegalArgumentException("Unexpected command: " + cmd);
|
|
||||||
}
|
|
||||||
if (cmd.indexOf(' ') >= 0) {
|
|
||||||
throw new IllegalArgumentException("Arguments must be separate from command");
|
|
||||||
}
|
|
||||||
|
|
||||||
rawBuilder.append(sequenceNumber).append(' ').append(cmd);
|
|
||||||
logBuilder.append(sequenceNumber).append(' ').append(cmd);
|
|
||||||
for (Object arg : args) {
|
|
||||||
final String argString = String.valueOf(arg);
|
|
||||||
if (argString.indexOf('\0') >= 0) {
|
|
||||||
throw new IllegalArgumentException("Unexpected argument: " + arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
rawBuilder.append(' ');
|
|
||||||
logBuilder.append(' ');
|
|
||||||
|
|
||||||
appendEscaped(rawBuilder, argString);
|
|
||||||
if (arg instanceof SensitiveArg) {
|
|
||||||
logBuilder.append("[scrubbed]");
|
|
||||||
} else {
|
|
||||||
appendEscaped(logBuilder, argString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rawBuilder.append('\0');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method that waits until all asychronous notifications sent by the native daemon have
|
|
||||||
* been processed. This method must not be called on the notification thread or an
|
|
||||||
* exception will be thrown.
|
|
||||||
*/
|
|
||||||
public void waitForCallbacks() {
|
|
||||||
if (Thread.currentThread() == mLooper.getThread()) {
|
|
||||||
throw new IllegalStateException("Must not call this method on callback thread");
|
|
||||||
}
|
|
||||||
|
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
mCallbackHandler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
latch.await();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Log.wtf(TAG, "Interrupted while waiting for unsolicited response handling", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issue the given command to the native daemon and return a single expected
|
|
||||||
* response.
|
|
||||||
*
|
|
||||||
* @throws NativeDaemonConnectorException when problem communicating with
|
|
||||||
* native daemon, or if the response matches
|
|
||||||
* {@link NativeDaemonEvent#isClassClientError()} or
|
|
||||||
* {@link NativeDaemonEvent#isClassServerError()}.
|
|
||||||
*/
|
|
||||||
public NativeDaemonEvent execute(Command cmd) throws NativeDaemonConnectorException {
|
|
||||||
return execute(cmd.mCmd, cmd.mArguments.toArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issue the given command to the native daemon and return a single expected
|
|
||||||
* response. Any arguments must be separated from base command so they can
|
|
||||||
* be properly escaped.
|
|
||||||
*
|
|
||||||
* @throws NativeDaemonConnectorException when problem communicating with
|
|
||||||
* native daemon, or if the response matches
|
|
||||||
* {@link NativeDaemonEvent#isClassClientError()} or
|
|
||||||
* {@link NativeDaemonEvent#isClassServerError()}.
|
|
||||||
*/
|
|
||||||
public NativeDaemonEvent execute(String cmd, Object... args)
|
|
||||||
throws NativeDaemonConnectorException {
|
|
||||||
return execute(DEFAULT_TIMEOUT, cmd, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public NativeDaemonEvent execute(long timeoutMs, String cmd, Object... args)
|
|
||||||
throws NativeDaemonConnectorException {
|
|
||||||
final NativeDaemonEvent[] events = executeForList(timeoutMs, cmd, args);
|
|
||||||
if (events.length != 1) {
|
|
||||||
throw new NativeDaemonConnectorException(
|
|
||||||
"Expected exactly one response, but received " + events.length);
|
|
||||||
}
|
|
||||||
return events[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issue the given command to the native daemon and return any
|
|
||||||
* {@link NativeDaemonEvent#isClassContinue()} responses, including the
|
|
||||||
* final terminal response.
|
|
||||||
*
|
|
||||||
* @throws NativeDaemonConnectorException when problem communicating with
|
|
||||||
* native daemon, or if the response matches
|
|
||||||
* {@link NativeDaemonEvent#isClassClientError()} or
|
|
||||||
* {@link NativeDaemonEvent#isClassServerError()}.
|
|
||||||
*/
|
|
||||||
public NativeDaemonEvent[] executeForList(Command cmd) throws NativeDaemonConnectorException {
|
|
||||||
return executeForList(cmd.mCmd, cmd.mArguments.toArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issue the given command to the native daemon and return any
|
|
||||||
* {@link NativeDaemonEvent#isClassContinue()} responses, including the
|
|
||||||
* final terminal response. Any arguments must be separated from base
|
|
||||||
* command so they can be properly escaped.
|
|
||||||
*
|
|
||||||
* @throws NativeDaemonConnectorException when problem communicating with
|
|
||||||
* native daemon, or if the response matches
|
|
||||||
* {@link NativeDaemonEvent#isClassClientError()} or
|
|
||||||
* {@link NativeDaemonEvent#isClassServerError()}.
|
|
||||||
*/
|
|
||||||
public NativeDaemonEvent[] executeForList(String cmd, Object... args)
|
|
||||||
throws NativeDaemonConnectorException {
|
|
||||||
return executeForList(DEFAULT_TIMEOUT, cmd, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issue the given command to the native daemon and return any {@linke
|
|
||||||
* NativeDaemonEvent@isClassContinue()} responses, including the final
|
|
||||||
* terminal response. Note that the timeout does not count time in deep
|
|
||||||
* sleep. Any arguments must be separated from base command so they can be
|
|
||||||
* properly escaped.
|
|
||||||
*
|
|
||||||
* @throws NativeDaemonConnectorException when problem communicating with
|
|
||||||
* native daemon, or if the response matches
|
|
||||||
* {@link NativeDaemonEvent#isClassClientError()} or
|
|
||||||
* {@link NativeDaemonEvent#isClassServerError()}.
|
|
||||||
*/
|
|
||||||
public NativeDaemonEvent[] executeForList(long timeoutMs, String cmd, Object... args)
|
|
||||||
throws NativeDaemonConnectorException {
|
|
||||||
if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
|
|
||||||
Log.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
|
|
||||||
+ Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable());
|
|
||||||
}
|
|
||||||
|
|
||||||
final long startTime = SystemClock.elapsedRealtime();
|
|
||||||
|
|
||||||
final ArrayList<NativeDaemonEvent> events = new ArrayList<>();
|
|
||||||
|
|
||||||
final StringBuilder rawBuilder = new StringBuilder();
|
|
||||||
final StringBuilder logBuilder = new StringBuilder();
|
|
||||||
final int sequenceNumber = mSequenceNumber.incrementAndGet();
|
|
||||||
|
|
||||||
makeCommand(rawBuilder, logBuilder, sequenceNumber, cmd, args);
|
|
||||||
|
|
||||||
final String rawCmd = rawBuilder.toString();
|
|
||||||
final String logCmd = logBuilder.toString();
|
|
||||||
|
|
||||||
log("SND -> {" + logCmd + "}");
|
|
||||||
|
|
||||||
synchronized (mDaemonLock) {
|
|
||||||
if (mOutputStream == null) {
|
|
||||||
throw new NativeDaemonConnectorException("missing output stream");
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
mOutputStream.write(rawCmd.getBytes(StandardCharsets.UTF_8));
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new NativeDaemonConnectorException("problem sending command", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NativeDaemonEvent event = null;
|
|
||||||
do {
|
|
||||||
event = mResponseQueue.remove(sequenceNumber, timeoutMs, logCmd);
|
|
||||||
if (event == null) {
|
|
||||||
loge("timed-out waiting for response to " + logCmd);
|
|
||||||
throw new NativeDaemonTimeoutException(logCmd, event);
|
|
||||||
}
|
|
||||||
if (VDBG) log("RMV <- {" + event + "}");
|
|
||||||
events.add(event);
|
|
||||||
} while (event.isClassContinue());
|
|
||||||
|
|
||||||
final long endTime = SystemClock.elapsedRealtime();
|
|
||||||
if (endTime - startTime > WARN_EXECUTE_DELAY_MS) {
|
|
||||||
loge("NDC Command {" + logCmd + "} took too long (" + (endTime - startTime) + "ms)");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.isClassClientError()) {
|
|
||||||
throw new NativeDaemonArgumentException(logCmd, event);
|
|
||||||
}
|
|
||||||
if (event.isClassServerError()) {
|
|
||||||
throw new NativeDaemonFailureException(logCmd, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
return events.toArray(new NativeDaemonEvent[events.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append the given argument to {@link StringBuilder}, escaping as needed,
|
|
||||||
* and surrounding with quotes when it contains spaces.
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
static void appendEscaped(StringBuilder builder, String arg) {
|
|
||||||
final boolean hasSpaces = arg.indexOf(' ') >= 0;
|
|
||||||
if (hasSpaces) {
|
|
||||||
builder.append('"');
|
|
||||||
}
|
|
||||||
|
|
||||||
final int length = arg.length();
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
final char c = arg.charAt(i);
|
|
||||||
|
|
||||||
if (c == '"') {
|
|
||||||
builder.append("\\\"");
|
|
||||||
} else if (c == '\\') {
|
|
||||||
builder.append("\\\\");
|
|
||||||
} else {
|
|
||||||
builder.append(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasSpaces) {
|
|
||||||
builder.append('"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class NativeDaemonArgumentException extends NativeDaemonConnectorException {
|
|
||||||
public NativeDaemonArgumentException(String command, NativeDaemonEvent event) {
|
|
||||||
super(command, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IllegalArgumentException rethrowAsParcelableException() {
|
|
||||||
throw new IllegalArgumentException(getMessage(), this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class NativeDaemonFailureException extends NativeDaemonConnectorException {
|
|
||||||
public NativeDaemonFailureException(String command, NativeDaemonEvent event) {
|
|
||||||
super(command, event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Command builder that handles argument list building. Any arguments must
|
|
||||||
* be separated from base command so they can be properly escaped.
|
|
||||||
*/
|
|
||||||
public static class Command {
|
|
||||||
private String mCmd;
|
|
||||||
private ArrayList<Object> mArguments = new ArrayList<>();
|
|
||||||
|
|
||||||
public Command(String cmd, Object... args) {
|
|
||||||
mCmd = cmd;
|
|
||||||
for (Object arg : args) {
|
|
||||||
appendArg(arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Command appendArg(Object arg) {
|
|
||||||
mArguments.add(arg);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
|
||||||
mLocalLog.dump(fd, pw, args);
|
|
||||||
pw.println();
|
|
||||||
mResponseQueue.dump(fd, pw, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void log(String logstring) {
|
|
||||||
if (mDebug) Log.d(TAG, logstring);
|
|
||||||
mLocalLog.log(logstring);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loge(String logstring) {
|
|
||||||
Log.e(TAG, logstring);
|
|
||||||
mLocalLog.log(logstring);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ResponseQueue {
|
|
||||||
|
|
||||||
private static class PendingCmd {
|
|
||||||
public final int cmdNum;
|
|
||||||
public final String logCmd;
|
|
||||||
|
|
||||||
public BlockingQueue<NativeDaemonEvent> responses =
|
|
||||||
new ArrayBlockingQueue<NativeDaemonEvent>(10);
|
|
||||||
|
|
||||||
// The availableResponseCount member is used to track when we can remove this
|
|
||||||
// instance from the ResponseQueue.
|
|
||||||
// This is used under the protection of a sync of the mPendingCmds object.
|
|
||||||
// A positive value means we've had more writers retreive this object while
|
|
||||||
// a negative value means we've had more readers. When we've had an equal number
|
|
||||||
// (it goes to zero) we can remove this object from the mPendingCmds list.
|
|
||||||
// Note that we may have more responses for this command (and more readers
|
|
||||||
// coming), but that would result in a new PendingCmd instance being created
|
|
||||||
// and added with the same cmdNum.
|
|
||||||
// Also note that when this goes to zero it just means a parity of readers and
|
|
||||||
// writers have retrieved this object - not that they are done using it. The
|
|
||||||
// responses queue may well have more responses yet to be read or may get more
|
|
||||||
// responses added to it. But all those readers/writers have retreived and
|
|
||||||
// hold references to this instance already so it can be removed from
|
|
||||||
// mPendingCmds queue.
|
|
||||||
public int availableResponseCount;
|
|
||||||
|
|
||||||
public PendingCmd(int cmdNum, String logCmd) {
|
|
||||||
this.cmdNum = cmdNum;
|
|
||||||
this.logCmd = logCmd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final LinkedList<PendingCmd> mPendingCmds;
|
|
||||||
private int mMaxCount;
|
|
||||||
|
|
||||||
ResponseQueue(int maxCount) {
|
|
||||||
mPendingCmds = new LinkedList<PendingCmd>();
|
|
||||||
mMaxCount = maxCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(int cmdNum, NativeDaemonEvent response) {
|
|
||||||
PendingCmd found = null;
|
|
||||||
synchronized (mPendingCmds) {
|
|
||||||
for (PendingCmd pendingCmd : mPendingCmds) {
|
|
||||||
if (pendingCmd.cmdNum == cmdNum) {
|
|
||||||
found = pendingCmd;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found == null) {
|
|
||||||
// didn't find it - make sure our queue isn't too big before adding
|
|
||||||
while (mPendingCmds.size() >= mMaxCount) {
|
|
||||||
Log.e("NativeDaemonConnector.ResponseQueue",
|
|
||||||
"more buffered than allowed: " + mPendingCmds.size() +
|
|
||||||
" >= " + mMaxCount);
|
|
||||||
// let any waiter timeout waiting for this
|
|
||||||
PendingCmd pendingCmd = mPendingCmds.remove();
|
|
||||||
Log.e("NativeDaemonConnector.ResponseQueue",
|
|
||||||
"Removing request: " + pendingCmd.logCmd + " (" +
|
|
||||||
pendingCmd.cmdNum + ")");
|
|
||||||
}
|
|
||||||
found = new PendingCmd(cmdNum, null);
|
|
||||||
mPendingCmds.add(found);
|
|
||||||
}
|
|
||||||
found.availableResponseCount++;
|
|
||||||
// if a matching remove call has already retrieved this we can remove this
|
|
||||||
// instance from our list
|
|
||||||
if (found.availableResponseCount == 0) mPendingCmds.remove(found);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
found.responses.put(response);
|
|
||||||
} catch (InterruptedException e) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
// note that the timeout does not count time in deep sleep. If you don't want
|
|
||||||
// the device to sleep, hold a wakelock
|
|
||||||
public NativeDaemonEvent remove(int cmdNum, long timeoutMs, String logCmd) {
|
|
||||||
PendingCmd found = null;
|
|
||||||
synchronized (mPendingCmds) {
|
|
||||||
for (PendingCmd pendingCmd : mPendingCmds) {
|
|
||||||
if (pendingCmd.cmdNum == cmdNum) {
|
|
||||||
found = pendingCmd;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found == null) {
|
|
||||||
found = new PendingCmd(cmdNum, logCmd);
|
|
||||||
mPendingCmds.add(found);
|
|
||||||
}
|
|
||||||
found.availableResponseCount--;
|
|
||||||
// if a matching add call has already retrieved this we can remove this
|
|
||||||
// instance from our list
|
|
||||||
if (found.availableResponseCount == 0) mPendingCmds.remove(found);
|
|
||||||
}
|
|
||||||
NativeDaemonEvent result = null;
|
|
||||||
try {
|
|
||||||
result = found.responses.poll(timeoutMs, TimeUnit.MILLISECONDS);
|
|
||||||
} catch (InterruptedException e) {}
|
|
||||||
if (result == null) {
|
|
||||||
Log.e("NativeDaemonConnector.ResponseQueue", "Timeout waiting for response");
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
|
||||||
pw.println("Pending requests:");
|
|
||||||
synchronized (mPendingCmds) {
|
|
||||||
for (PendingCmd pendingCmd : mPendingCmds) {
|
|
||||||
pw.println(" Cmd " + pendingCmd.cmdNum + " - " + pendingCmd.logCmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2006 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.server;
|
|
||||||
|
|
||||||
import android.os.Parcel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception that indicates there was an error with a
|
|
||||||
* {@link NativeDaemonConnector} operation.
|
|
||||||
*/
|
|
||||||
public class NativeDaemonConnectorException extends Exception {
|
|
||||||
private String mCmd;
|
|
||||||
private NativeDaemonEvent mEvent;
|
|
||||||
|
|
||||||
public NativeDaemonConnectorException(String detailMessage) {
|
|
||||||
super(detailMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
public NativeDaemonConnectorException(String detailMessage, Throwable throwable) {
|
|
||||||
super(detailMessage, throwable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public NativeDaemonConnectorException(String cmd, NativeDaemonEvent event) {
|
|
||||||
super("command '" + cmd + "' failed with '" + event + "'");
|
|
||||||
mCmd = cmd;
|
|
||||||
mEvent = event;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCode() {
|
|
||||||
return mEvent != null ? mEvent.getCode() : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCmd() {
|
|
||||||
return mCmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rethrow as a {@link RuntimeException} subclass that is handled by
|
|
||||||
* {@link Parcel#writeException(Exception)}.
|
|
||||||
*/
|
|
||||||
public IllegalArgumentException rethrowAsParcelableException() {
|
|
||||||
throw new IllegalStateException(getMessage(), this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,267 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2011 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.server;
|
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parsed event from native side of {@link NativeDaemonConnector}.
|
|
||||||
*/
|
|
||||||
public class NativeDaemonEvent {
|
|
||||||
|
|
||||||
// TODO: keep class ranges in sync with ResponseCode.h
|
|
||||||
// TODO: swap client and server error ranges to roughly mirror HTTP spec
|
|
||||||
|
|
||||||
private final int mCmdNumber;
|
|
||||||
private final int mCode;
|
|
||||||
private final String mMessage;
|
|
||||||
private final String mRawEvent;
|
|
||||||
private final String mLogMessage;
|
|
||||||
private String[] mParsed;
|
|
||||||
private FileDescriptor[] mFdList;
|
|
||||||
|
|
||||||
private NativeDaemonEvent(int cmdNumber, int code, String message,
|
|
||||||
String rawEvent, String logMessage, FileDescriptor[] fdList) {
|
|
||||||
mCmdNumber = cmdNumber;
|
|
||||||
mCode = code;
|
|
||||||
mMessage = message;
|
|
||||||
mRawEvent = rawEvent;
|
|
||||||
mLogMessage = logMessage;
|
|
||||||
mParsed = null;
|
|
||||||
mFdList = fdList;
|
|
||||||
}
|
|
||||||
|
|
||||||
static public final String SENSITIVE_MARKER = "{{sensitive}}";
|
|
||||||
|
|
||||||
public int getCmdNumber() {
|
|
||||||
return mCmdNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCode() {
|
|
||||||
return mCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMessage() {
|
|
||||||
return mMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileDescriptor[] getFileDescriptors() {
|
|
||||||
return mFdList;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public String getRawEvent() {
|
|
||||||
return mRawEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return mLogMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if event represents a partial response which is continued in
|
|
||||||
* additional subsequent events.
|
|
||||||
*/
|
|
||||||
public boolean isClassContinue() {
|
|
||||||
return mCode >= 100 && mCode < 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if event represents a command success.
|
|
||||||
*/
|
|
||||||
public boolean isClassOk() {
|
|
||||||
return mCode >= 200 && mCode < 300;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if event represents a remote native daemon error.
|
|
||||||
*/
|
|
||||||
public boolean isClassServerError() {
|
|
||||||
return mCode >= 400 && mCode < 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if event represents a command syntax or argument error.
|
|
||||||
*/
|
|
||||||
public boolean isClassClientError() {
|
|
||||||
return mCode >= 500 && mCode < 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if event represents an unsolicited event from native daemon.
|
|
||||||
*/
|
|
||||||
public boolean isClassUnsolicited() {
|
|
||||||
return isClassUnsolicited(mCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isClassUnsolicited(int code) {
|
|
||||||
return code >= 600 && code < 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify this event matches the given code.
|
|
||||||
*
|
|
||||||
* @throws IllegalStateException if {@link #getCode()} doesn't match.
|
|
||||||
*/
|
|
||||||
public void checkCode(int code) {
|
|
||||||
if (mCode != code) {
|
|
||||||
throw new IllegalStateException("Expected " + code + " but was: " + this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse the given raw event into {@link NativeDaemonEvent} instance.
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException when line doesn't match format expected
|
|
||||||
* from native side.
|
|
||||||
*/
|
|
||||||
public static NativeDaemonEvent parseRawEvent(String rawEvent, FileDescriptor[] fdList) {
|
|
||||||
final String[] parsed = rawEvent.split(" ");
|
|
||||||
if (parsed.length < 2) {
|
|
||||||
throw new IllegalArgumentException("Insufficient arguments");
|
|
||||||
}
|
|
||||||
|
|
||||||
int skiplength = 0;
|
|
||||||
|
|
||||||
final int code;
|
|
||||||
try {
|
|
||||||
code = Integer.parseInt(parsed[0]);
|
|
||||||
skiplength = parsed[0].length() + 1;
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
throw new IllegalArgumentException("problem parsing code", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
int cmdNumber = -1;
|
|
||||||
if (isClassUnsolicited(code) == false) {
|
|
||||||
if (parsed.length < 3) {
|
|
||||||
throw new IllegalArgumentException("Insufficient arguemnts");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
cmdNumber = Integer.parseInt(parsed[1]);
|
|
||||||
skiplength += parsed[1].length() + 1;
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
throw new IllegalArgumentException("problem parsing cmdNumber", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String logMessage = rawEvent;
|
|
||||||
if (parsed.length > 2 && parsed[2].equals(SENSITIVE_MARKER)) {
|
|
||||||
skiplength += parsed[2].length() + 1;
|
|
||||||
logMessage = parsed[0] + " " + parsed[1] + " {}";
|
|
||||||
}
|
|
||||||
|
|
||||||
final String message = rawEvent.substring(skiplength);
|
|
||||||
|
|
||||||
return new NativeDaemonEvent(cmdNumber, code, message, rawEvent, logMessage, fdList);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter the given {@link NativeDaemonEvent} list, returning
|
|
||||||
* {@link #getMessage()} for any events matching the requested code.
|
|
||||||
*/
|
|
||||||
public static String[] filterMessageList(NativeDaemonEvent[] events, int matchCode) {
|
|
||||||
final ArrayList<String> result = new ArrayList<>();
|
|
||||||
for (NativeDaemonEvent event : events) {
|
|
||||||
if (event.getCode() == matchCode) {
|
|
||||||
result.add(event.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toArray(new String[result.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the Nth field of the event.
|
|
||||||
*
|
|
||||||
* This ignores and code or cmdNum, the first return value is given for N=0.
|
|
||||||
* Also understands "\"quoted\" multiword responses" and tries them as a single field
|
|
||||||
*/
|
|
||||||
public String getField(int n) {
|
|
||||||
if (mParsed == null) {
|
|
||||||
mParsed = unescapeArgs(mRawEvent);
|
|
||||||
}
|
|
||||||
n += 2; // skip code and command#
|
|
||||||
if (n > mParsed.length) return null;
|
|
||||||
return mParsed[n];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String[] unescapeArgs(String rawEvent) {
|
|
||||||
final boolean DEBUG_ROUTINE = false;
|
|
||||||
final String LOGTAG = "unescapeArgs";
|
|
||||||
final ArrayList<String> parsed = new ArrayList<String>();
|
|
||||||
final int length = rawEvent.length();
|
|
||||||
int current = 0;
|
|
||||||
int wordEnd = -1;
|
|
||||||
boolean quoted = false;
|
|
||||||
|
|
||||||
if (DEBUG_ROUTINE) Log.e(LOGTAG, "parsing '" + rawEvent + "'");
|
|
||||||
if (rawEvent.charAt(current) == '\"') {
|
|
||||||
quoted = true;
|
|
||||||
current++;
|
|
||||||
}
|
|
||||||
while (current < length) {
|
|
||||||
// find the end of the word
|
|
||||||
char terminator = quoted ? '\"' : ' ';
|
|
||||||
wordEnd = current;
|
|
||||||
while (wordEnd < length && rawEvent.charAt(wordEnd) != terminator) {
|
|
||||||
if (rawEvent.charAt(wordEnd) == '\\') {
|
|
||||||
// skip the escaped char
|
|
||||||
++wordEnd;
|
|
||||||
}
|
|
||||||
++wordEnd;
|
|
||||||
}
|
|
||||||
if (wordEnd > length) wordEnd = length;
|
|
||||||
String word = rawEvent.substring(current, wordEnd);
|
|
||||||
current += word.length();
|
|
||||||
if (!quoted) {
|
|
||||||
word = word.trim();
|
|
||||||
} else {
|
|
||||||
current++; // skip the trailing quote
|
|
||||||
}
|
|
||||||
// unescape stuff within the word
|
|
||||||
word = word.replace("\\\\", "\\");
|
|
||||||
word = word.replace("\\\"", "\"");
|
|
||||||
|
|
||||||
if (DEBUG_ROUTINE) Log.e(LOGTAG, "found '" + word + "'");
|
|
||||||
parsed.add(word);
|
|
||||||
|
|
||||||
// find the beginning of the next word - either of these options
|
|
||||||
int nextSpace = rawEvent.indexOf(' ', current);
|
|
||||||
int nextQuote = rawEvent.indexOf(" \"", current);
|
|
||||||
if (DEBUG_ROUTINE) {
|
|
||||||
Log.e(LOGTAG, "nextSpace=" + nextSpace + ", nextQuote=" + nextQuote);
|
|
||||||
}
|
|
||||||
if (nextQuote > -1 && nextQuote <= nextSpace) {
|
|
||||||
quoted = true;
|
|
||||||
current = nextQuote + 2;
|
|
||||||
} else {
|
|
||||||
quoted = false;
|
|
||||||
if (nextSpace > -1) {
|
|
||||||
current = nextSpace + 1;
|
|
||||||
}
|
|
||||||
} // else we just start the next word after the current and read til the end
|
|
||||||
if (DEBUG_ROUTINE) {
|
|
||||||
Log.e(LOGTAG, "next loop - current=" + current
|
|
||||||
+ ", length=" + length + ", quoted=" + quoted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return parsed.toArray(new String[parsed.size()]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.server;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception that indicates there was a timeout with a
|
|
||||||
* {@link NativeDaemonConnector} operation.
|
|
||||||
*/
|
|
||||||
public class NativeDaemonTimeoutException extends NativeDaemonConnectorException {
|
|
||||||
public NativeDaemonTimeoutException(String command, NativeDaemonEvent event) {
|
|
||||||
super(command, event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -76,7 +76,6 @@ filegroup {
|
|||||||
"java/com/android/server/IpSecServiceParameterizedTest.java",
|
"java/com/android/server/IpSecServiceParameterizedTest.java",
|
||||||
"java/com/android/server/IpSecServiceRefcountedResourceTest.java",
|
"java/com/android/server/IpSecServiceRefcountedResourceTest.java",
|
||||||
"java/com/android/server/IpSecServiceTest.java",
|
"java/com/android/server/IpSecServiceTest.java",
|
||||||
"java/com/android/server/NativeDaemonConnectorTest.java",
|
|
||||||
"java/com/android/server/NetworkManagementServiceTest.java",
|
"java/com/android/server/NetworkManagementServiceTest.java",
|
||||||
"java/com/android/server/NsdServiceTest.java",
|
"java/com/android/server/NsdServiceTest.java",
|
||||||
"java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java",
|
"java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java",
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2011 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.server;
|
|
||||||
|
|
||||||
import static com.android.server.NativeDaemonConnector.appendEscaped;
|
|
||||||
import static com.android.server.NativeDaemonConnector.makeCommand;
|
|
||||||
|
|
||||||
import android.test.AndroidTestCase;
|
|
||||||
import android.test.suitebuilder.annotation.MediumTest;
|
|
||||||
|
|
||||||
import com.android.server.NativeDaemonConnector.SensitiveArg;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link NativeDaemonConnector}.
|
|
||||||
*/
|
|
||||||
@MediumTest
|
|
||||||
public class NativeDaemonConnectorTest extends AndroidTestCase {
|
|
||||||
private static final String TAG = "NativeDaemonConnectorTest";
|
|
||||||
|
|
||||||
public void testArgumentNormal() throws Exception {
|
|
||||||
final StringBuilder builder = new StringBuilder();
|
|
||||||
|
|
||||||
builder.setLength(0);
|
|
||||||
appendEscaped(builder, "");
|
|
||||||
assertEquals("", builder.toString());
|
|
||||||
|
|
||||||
builder.setLength(0);
|
|
||||||
appendEscaped(builder, "foo");
|
|
||||||
assertEquals("foo", builder.toString());
|
|
||||||
|
|
||||||
builder.setLength(0);
|
|
||||||
appendEscaped(builder, "foo\"bar");
|
|
||||||
assertEquals("foo\\\"bar", builder.toString());
|
|
||||||
|
|
||||||
builder.setLength(0);
|
|
||||||
appendEscaped(builder, "foo\\bar\\\"baz");
|
|
||||||
assertEquals("foo\\\\bar\\\\\\\"baz", builder.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testArgumentWithSpaces() throws Exception {
|
|
||||||
final StringBuilder builder = new StringBuilder();
|
|
||||||
|
|
||||||
builder.setLength(0);
|
|
||||||
appendEscaped(builder, "foo bar");
|
|
||||||
assertEquals("\"foo bar\"", builder.toString());
|
|
||||||
|
|
||||||
builder.setLength(0);
|
|
||||||
appendEscaped(builder, "foo\"bar\\baz foo");
|
|
||||||
assertEquals("\"foo\\\"bar\\\\baz foo\"", builder.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testArgumentWithUtf() throws Exception {
|
|
||||||
final StringBuilder builder = new StringBuilder();
|
|
||||||
|
|
||||||
builder.setLength(0);
|
|
||||||
appendEscaped(builder, "caf\u00E9 c\u00F6ffee");
|
|
||||||
assertEquals("\"caf\u00E9 c\u00F6ffee\"", builder.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testSensitiveArgs() throws Exception {
|
|
||||||
final StringBuilder rawBuilder = new StringBuilder();
|
|
||||||
final StringBuilder logBuilder = new StringBuilder();
|
|
||||||
|
|
||||||
rawBuilder.setLength(0);
|
|
||||||
logBuilder.setLength(0);
|
|
||||||
makeCommand(rawBuilder, logBuilder, 1, "foo", "bar", "baz");
|
|
||||||
assertEquals("1 foo bar baz\0", rawBuilder.toString());
|
|
||||||
assertEquals("1 foo bar baz", logBuilder.toString());
|
|
||||||
|
|
||||||
rawBuilder.setLength(0);
|
|
||||||
logBuilder.setLength(0);
|
|
||||||
makeCommand(rawBuilder, logBuilder, 1, "foo", new SensitiveArg("bar"), "baz");
|
|
||||||
assertEquals("1 foo bar baz\0", rawBuilder.toString());
|
|
||||||
assertEquals("1 foo [scrubbed] baz", logBuilder.toString());
|
|
||||||
|
|
||||||
rawBuilder.setLength(0);
|
|
||||||
logBuilder.setLength(0);
|
|
||||||
makeCommand(rawBuilder, logBuilder, 1, "foo", new SensitiveArg("foo bar"), "baz baz",
|
|
||||||
new SensitiveArg("wat"));
|
|
||||||
assertEquals("1 foo \"foo bar\" \"baz baz\" wat\0", rawBuilder.toString());
|
|
||||||
assertEquals("1 foo [scrubbed] \"baz baz\" [scrubbed]", logBuilder.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user