summaryrefslogtreecommitdiffstats
path: root/services/core/java/com/android/server/NativeDaemonEvent.java
diff options
context:
space:
mode:
Diffstat (limited to 'services/core/java/com/android/server/NativeDaemonEvent.java')
-rw-r--r--services/core/java/com/android/server/NativeDaemonEvent.java254
1 files changed, 254 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/NativeDaemonEvent.java b/services/core/java/com/android/server/NativeDaemonEvent.java
new file mode 100644
index 0000000..2095152
--- /dev/null
+++ b/services/core/java/com/android/server/NativeDaemonEvent.java
@@ -0,0 +1,254 @@
+/*
+ * 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.Slog;
+import com.google.android.collect.Lists;
+
+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 String[] mParsed;
+
+ private NativeDaemonEvent(int cmdNumber, int code, String message, String rawEvent) {
+ mCmdNumber = cmdNumber;
+ mCode = code;
+ mMessage = message;
+ mRawEvent = rawEvent;
+ mParsed = null;
+ }
+
+ public int getCmdNumber() {
+ return mCmdNumber;
+ }
+
+ public int getCode() {
+ return mCode;
+ }
+
+ public String getMessage() {
+ return mMessage;
+ }
+
+ @Deprecated
+ public String getRawEvent() {
+ return mRawEvent;
+ }
+
+ @Override
+ public String toString() {
+ return mRawEvent;
+ }
+
+ /**
+ * 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) {
+ 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);
+ }
+ }
+
+ final String message = rawEvent.substring(skiplength);
+
+ return new NativeDaemonEvent(cmdNumber, code, message, rawEvent);
+ }
+
+ /**
+ * 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 = Lists.newArrayList();
+ 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) Slog.e(LOGTAG, "parsing '" + rawEvent + "'");
+ if (rawEvent.charAt(current) == '\"') {
+ quoted = true;
+ current++;
+ }
+ while (current < length) {
+ // find the end of the word
+ if (quoted) {
+ wordEnd = current;
+ while ((wordEnd = rawEvent.indexOf('\"', wordEnd)) != -1) {
+ if (rawEvent.charAt(wordEnd - 1) != '\\') {
+ break;
+ } else {
+ wordEnd++; // skip this escaped quote and keep looking
+ }
+ }
+ } else {
+ wordEnd = rawEvent.indexOf(' ', current);
+ }
+ // if we didn't find the end-o-word token, take the rest of the string
+ if (wordEnd == -1) 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) Slog.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) {
+ Slog.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) {
+ Slog.e(LOGTAG, "next loop - current=" + current +
+ ", length=" + length + ", quoted=" + quoted);
+ }
+ }
+ return parsed.toArray(new String[parsed.size()]);
+ }
+}