blob: 4e61c0b09de40f91c115fa5c4f0acd220b9946e1 [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;
Paul Lawrencec38182f2014-11-11 12:23:22 -080036 private final String mLogMessage;
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -070037 private String[] mParsed;
Jeff Sharkey31c6e482011-11-18 17:09:01 -080038
Paul Lawrencec38182f2014-11-11 12:23:22 -080039 private NativeDaemonEvent(int cmdNumber, int code, String message,
40 String rawEvent, String logMessage) {
Robert Greenwalt470007f2012-02-07 11:36:55 -080041 mCmdNumber = cmdNumber;
Jeff Sharkey31c6e482011-11-18 17:09:01 -080042 mCode = code;
43 mMessage = message;
44 mRawEvent = rawEvent;
Paul Lawrencec38182f2014-11-11 12:23:22 -080045 mLogMessage = logMessage;
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -070046 mParsed = null;
Jeff Sharkey31c6e482011-11-18 17:09:01 -080047 }
48
Paul Lawrencec38182f2014-11-11 12:23:22 -080049 static public final String SENSITIVE_MARKER = "{{sensitive}}";
50
Robert Greenwalt470007f2012-02-07 11:36:55 -080051 public int getCmdNumber() {
52 return mCmdNumber;
53 }
54
Jeff Sharkey31c6e482011-11-18 17:09:01 -080055 public int getCode() {
56 return mCode;
57 }
58
59 public String getMessage() {
60 return mMessage;
61 }
62
63 @Deprecated
64 public String getRawEvent() {
65 return mRawEvent;
66 }
67
68 @Override
69 public String toString() {
Paul Lawrencec38182f2014-11-11 12:23:22 -080070 return mLogMessage;
Jeff Sharkey31c6e482011-11-18 17:09:01 -080071 }
72
73 /**
74 * Test if event represents a partial response which is continued in
75 * additional subsequent events.
76 */
77 public boolean isClassContinue() {
78 return mCode >= 100 && mCode < 200;
79 }
80
81 /**
82 * Test if event represents a command success.
83 */
84 public boolean isClassOk() {
85 return mCode >= 200 && mCode < 300;
86 }
87
88 /**
89 * Test if event represents a remote native daemon error.
90 */
91 public boolean isClassServerError() {
92 return mCode >= 400 && mCode < 500;
93 }
94
95 /**
96 * Test if event represents a command syntax or argument error.
97 */
98 public boolean isClassClientError() {
99 return mCode >= 500 && mCode < 600;
100 }
101
102 /**
103 * Test if event represents an unsolicited event from native daemon.
104 */
105 public boolean isClassUnsolicited() {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800106 return isClassUnsolicited(mCode);
107 }
108
109 private static boolean isClassUnsolicited(int code) {
110 return code >= 600 && code < 700;
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800111 }
112
113 /**
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800114 * Verify this event matches the given code.
115 *
116 * @throws IllegalStateException if {@link #getCode()} doesn't match.
117 */
118 public void checkCode(int code) {
119 if (mCode != code) {
120 throw new IllegalStateException("Expected " + code + " but was: " + this);
121 }
122 }
123
124 /**
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800125 * Parse the given raw event into {@link NativeDaemonEvent} instance.
126 *
127 * @throws IllegalArgumentException when line doesn't match format expected
128 * from native side.
129 */
130 public static NativeDaemonEvent parseRawEvent(String rawEvent) {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800131 final String[] parsed = rawEvent.split(" ");
132 if (parsed.length < 2) {
133 throw new IllegalArgumentException("Insufficient arguments");
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800134 }
135
Robert Greenwalt470007f2012-02-07 11:36:55 -0800136 int skiplength = 0;
137
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800138 final int code;
139 try {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800140 code = Integer.parseInt(parsed[0]);
141 skiplength = parsed[0].length() + 1;
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800142 } catch (NumberFormatException e) {
143 throw new IllegalArgumentException("problem parsing code", e);
144 }
145
Robert Greenwalt470007f2012-02-07 11:36:55 -0800146 int cmdNumber = -1;
147 if (isClassUnsolicited(code) == false) {
148 if (parsed.length < 3) {
149 throw new IllegalArgumentException("Insufficient arguemnts");
150 }
151 try {
152 cmdNumber = Integer.parseInt(parsed[1]);
153 skiplength += parsed[1].length() + 1;
154 } catch (NumberFormatException e) {
155 throw new IllegalArgumentException("problem parsing cmdNumber", e);
156 }
157 }
158
Paul Lawrencec38182f2014-11-11 12:23:22 -0800159 String logMessage = rawEvent;
160 if (parsed.length > 2 && parsed[2].equals(SENSITIVE_MARKER)) {
161 skiplength += parsed[2].length() + 1;
162 logMessage = parsed[0] + " " + parsed[1] + " {}";
163 }
164
Robert Greenwalt470007f2012-02-07 11:36:55 -0800165 final String message = rawEvent.substring(skiplength);
166
Paul Lawrencec38182f2014-11-11 12:23:22 -0800167 return new NativeDaemonEvent(cmdNumber, code, message, rawEvent, logMessage);
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800168 }
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800169
170 /**
171 * Filter the given {@link NativeDaemonEvent} list, returning
172 * {@link #getMessage()} for any events matching the requested code.
173 */
174 public static String[] filterMessageList(NativeDaemonEvent[] events, int matchCode) {
175 final ArrayList<String> result = Lists.newArrayList();
176 for (NativeDaemonEvent event : events) {
177 if (event.getCode() == matchCode) {
178 result.add(event.getMessage());
179 }
180 }
181 return result.toArray(new String[result.size()]);
182 }
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -0700183
184 /**
185 * Find the Nth field of the event.
186 *
187 * This ignores and code or cmdNum, the first return value is given for N=0.
188 * Also understands "\"quoted\" multiword responses" and tries them as a single field
189 */
190 public String getField(int n) {
191 if (mParsed == null) {
192 mParsed = unescapeArgs(mRawEvent);
193 }
194 n += 2; // skip code and command#
195 if (n > mParsed.length) return null;
196 return mParsed[n];
197 }
198
199 public static String[] unescapeArgs(String rawEvent) {
200 final boolean DEBUG_ROUTINE = false;
201 final String LOGTAG = "unescapeArgs";
202 final ArrayList<String> parsed = new ArrayList<String>();
203 final int length = rawEvent.length();
204 int current = 0;
205 int wordEnd = -1;
206 boolean quoted = false;
207
208 if (DEBUG_ROUTINE) Slog.e(LOGTAG, "parsing '" + rawEvent + "'");
209 if (rawEvent.charAt(current) == '\"') {
210 quoted = true;
211 current++;
212 }
213 while (current < length) {
214 // find the end of the word
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700215 char terminator = quoted ? '\"' : ' ';
216 wordEnd = current;
217 while (wordEnd < length && rawEvent.charAt(wordEnd) != terminator) {
218 if (rawEvent.charAt(wordEnd) == '\\') {
219 // skip the escaped char
220 ++wordEnd;
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -0700221 }
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700222 ++wordEnd;
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -0700223 }
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700224 if (wordEnd > length) wordEnd = length;
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -0700225 String word = rawEvent.substring(current, wordEnd);
226 current += word.length();
227 if (!quoted) {
228 word = word.trim();
229 } else {
230 current++; // skip the trailing quote
231 }
232 // unescape stuff within the word
You Kimd7663952012-10-28 22:13:48 +0900233 word = word.replace("\\\\", "\\");
234 word = word.replace("\\\"", "\"");
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -0700235
236 if (DEBUG_ROUTINE) Slog.e(LOGTAG, "found '" + word + "'");
237 parsed.add(word);
238
239 // find the beginning of the next word - either of these options
240 int nextSpace = rawEvent.indexOf(' ', current);
241 int nextQuote = rawEvent.indexOf(" \"", current);
242 if (DEBUG_ROUTINE) {
243 Slog.e(LOGTAG, "nextSpace=" + nextSpace + ", nextQuote=" + nextQuote);
244 }
245 if (nextQuote > -1 && nextQuote <= nextSpace) {
246 quoted = true;
247 current = nextQuote + 2;
248 } else {
249 quoted = false;
250 if (nextSpace > -1) {
251 current = nextSpace + 1;
252 }
253 } // else we just start the next word after the current and read til the end
254 if (DEBUG_ROUTINE) {
255 Slog.e(LOGTAG, "next loop - current=" + current +
256 ", length=" + length + ", quoted=" + quoted);
257 }
258 }
259 return parsed.toArray(new String[parsed.size()]);
260 }
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800261}