blob: f11ae1d391eb8980db3f2a7799f3ca7180468f2f [file] [log] [blame]
Jeff Sharkey31c6e482011-11-18 17:09:01 -08001/*
2 * Copyright (C) 2011 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
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -070019import android.util.Slog;
Jeff Sharkeyba2896e2011-11-30 18:13:54 -080020import com.google.android.collect.Lists;
21
22import java.util.ArrayList;
23
Jeff Sharkey31c6e482011-11-18 17:09:01 -080024/**
25 * Parsed event from native side of {@link NativeDaemonConnector}.
26 */
27public class NativeDaemonEvent {
28
29 // TODO: keep class ranges in sync with ResponseCode.h
30 // TODO: swap client and server error ranges to roughly mirror HTTP spec
31
Robert Greenwalt470007f2012-02-07 11:36:55 -080032 private final int mCmdNumber;
Jeff Sharkey31c6e482011-11-18 17:09:01 -080033 private final int mCode;
34 private final String mMessage;
35 private final String mRawEvent;
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -070036 private String[] mParsed;
Jeff Sharkey31c6e482011-11-18 17:09:01 -080037
Robert Greenwalt470007f2012-02-07 11:36:55 -080038 private NativeDaemonEvent(int cmdNumber, int code, String message, String rawEvent) {
39 mCmdNumber = cmdNumber;
Jeff Sharkey31c6e482011-11-18 17:09:01 -080040 mCode = code;
41 mMessage = message;
42 mRawEvent = rawEvent;
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -070043 mParsed = null;
Jeff Sharkey31c6e482011-11-18 17:09:01 -080044 }
45
Robert Greenwalt470007f2012-02-07 11:36:55 -080046 public int getCmdNumber() {
47 return mCmdNumber;
48 }
49
Jeff Sharkey31c6e482011-11-18 17:09:01 -080050 public int getCode() {
51 return mCode;
52 }
53
54 public String getMessage() {
55 return mMessage;
56 }
57
58 @Deprecated
59 public String getRawEvent() {
60 return mRawEvent;
61 }
62
63 @Override
64 public String toString() {
65 return mRawEvent;
66 }
67
68 /**
69 * Test if event represents a partial response which is continued in
70 * additional subsequent events.
71 */
72 public boolean isClassContinue() {
73 return mCode >= 100 && mCode < 200;
74 }
75
76 /**
77 * Test if event represents a command success.
78 */
79 public boolean isClassOk() {
80 return mCode >= 200 && mCode < 300;
81 }
82
83 /**
84 * Test if event represents a remote native daemon error.
85 */
86 public boolean isClassServerError() {
87 return mCode >= 400 && mCode < 500;
88 }
89
90 /**
91 * Test if event represents a command syntax or argument error.
92 */
93 public boolean isClassClientError() {
94 return mCode >= 500 && mCode < 600;
95 }
96
97 /**
98 * Test if event represents an unsolicited event from native daemon.
99 */
100 public boolean isClassUnsolicited() {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800101 return isClassUnsolicited(mCode);
102 }
103
104 private static boolean isClassUnsolicited(int code) {
105 return code >= 600 && code < 700;
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800106 }
107
108 /**
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800109 * Verify this event matches the given code.
110 *
111 * @throws IllegalStateException if {@link #getCode()} doesn't match.
112 */
113 public void checkCode(int code) {
114 if (mCode != code) {
115 throw new IllegalStateException("Expected " + code + " but was: " + this);
116 }
117 }
118
119 /**
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800120 * Parse the given raw event into {@link NativeDaemonEvent} instance.
121 *
122 * @throws IllegalArgumentException when line doesn't match format expected
123 * from native side.
124 */
125 public static NativeDaemonEvent parseRawEvent(String rawEvent) {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800126 final String[] parsed = rawEvent.split(" ");
127 if (parsed.length < 2) {
128 throw new IllegalArgumentException("Insufficient arguments");
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800129 }
130
Robert Greenwalt470007f2012-02-07 11:36:55 -0800131 int skiplength = 0;
132
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800133 final int code;
134 try {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800135 code = Integer.parseInt(parsed[0]);
136 skiplength = parsed[0].length() + 1;
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800137 } catch (NumberFormatException e) {
138 throw new IllegalArgumentException("problem parsing code", e);
139 }
140
Robert Greenwalt470007f2012-02-07 11:36:55 -0800141 int cmdNumber = -1;
142 if (isClassUnsolicited(code) == false) {
143 if (parsed.length < 3) {
144 throw new IllegalArgumentException("Insufficient arguemnts");
145 }
146 try {
147 cmdNumber = Integer.parseInt(parsed[1]);
148 skiplength += parsed[1].length() + 1;
149 } catch (NumberFormatException e) {
150 throw new IllegalArgumentException("problem parsing cmdNumber", e);
151 }
152 }
153
154 final String message = rawEvent.substring(skiplength);
155
156 return new NativeDaemonEvent(cmdNumber, code, message, rawEvent);
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800157 }
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800158
159 /**
160 * Filter the given {@link NativeDaemonEvent} list, returning
161 * {@link #getMessage()} for any events matching the requested code.
162 */
163 public static String[] filterMessageList(NativeDaemonEvent[] events, int matchCode) {
164 final ArrayList<String> result = Lists.newArrayList();
165 for (NativeDaemonEvent event : events) {
166 if (event.getCode() == matchCode) {
167 result.add(event.getMessage());
168 }
169 }
170 return result.toArray(new String[result.size()]);
171 }
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -0700172
173 /**
174 * Find the Nth field of the event.
175 *
176 * This ignores and code or cmdNum, the first return value is given for N=0.
177 * Also understands "\"quoted\" multiword responses" and tries them as a single field
178 */
179 public String getField(int n) {
180 if (mParsed == null) {
181 mParsed = unescapeArgs(mRawEvent);
182 }
183 n += 2; // skip code and command#
184 if (n > mParsed.length) return null;
185 return mParsed[n];
186 }
187
188 public static String[] unescapeArgs(String rawEvent) {
189 final boolean DEBUG_ROUTINE = false;
190 final String LOGTAG = "unescapeArgs";
191 final ArrayList<String> parsed = new ArrayList<String>();
192 final int length = rawEvent.length();
193 int current = 0;
194 int wordEnd = -1;
195 boolean quoted = false;
196
197 if (DEBUG_ROUTINE) Slog.e(LOGTAG, "parsing '" + rawEvent + "'");
198 if (rawEvent.charAt(current) == '\"') {
199 quoted = true;
200 current++;
201 }
202 while (current < length) {
203 // find the end of the word
204 if (quoted) {
205 wordEnd = current;
206 while ((wordEnd = rawEvent.indexOf('\"', wordEnd)) != -1) {
207 if (rawEvent.charAt(wordEnd - 1) != '\\') {
208 break;
209 } else {
210 wordEnd++; // skip this escaped quote and keep looking
211 }
212 }
213 } else {
214 wordEnd = rawEvent.indexOf(' ', current);
215 }
216 // if we didn't find the end-o-word token, take the rest of the string
217 if (wordEnd == -1) wordEnd = length;
218 String word = rawEvent.substring(current, wordEnd);
219 current += word.length();
220 if (!quoted) {
221 word = word.trim();
222 } else {
223 current++; // skip the trailing quote
224 }
225 // unescape stuff within the word
226 word.replace("\\\\", "\\");
227 word.replace("\\\"", "\"");
228
229 if (DEBUG_ROUTINE) Slog.e(LOGTAG, "found '" + word + "'");
230 parsed.add(word);
231
232 // find the beginning of the next word - either of these options
233 int nextSpace = rawEvent.indexOf(' ', current);
234 int nextQuote = rawEvent.indexOf(" \"", current);
235 if (DEBUG_ROUTINE) {
236 Slog.e(LOGTAG, "nextSpace=" + nextSpace + ", nextQuote=" + nextQuote);
237 }
238 if (nextQuote > -1 && nextQuote <= nextSpace) {
239 quoted = true;
240 current = nextQuote + 2;
241 } else {
242 quoted = false;
243 if (nextSpace > -1) {
244 current = nextSpace + 1;
245 }
246 } // else we just start the next word after the current and read til the end
247 if (DEBUG_ROUTINE) {
248 Slog.e(LOGTAG, "next loop - current=" + current +
249 ", length=" + length + ", quoted=" + quoted);
250 }
251 }
252 return parsed.toArray(new String[parsed.size()]);
253 }
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800254}