blob: 59d50bde1b8d1e0ea99f65eee628eff809e0ccc7 [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
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700204 char terminator = quoted ? '\"' : ' ';
205 wordEnd = current;
206 while (wordEnd < length && rawEvent.charAt(wordEnd) != terminator) {
207 if (rawEvent.charAt(wordEnd) == '\\') {
208 // skip the escaped char
209 ++wordEnd;
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -0700210 }
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700211 ++wordEnd;
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -0700212 }
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700213 if (wordEnd > length) wordEnd = length;
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -0700214 String word = rawEvent.substring(current, wordEnd);
215 current += word.length();
216 if (!quoted) {
217 word = word.trim();
218 } else {
219 current++; // skip the trailing quote
220 }
221 // unescape stuff within the word
You Kimd7663952012-10-28 22:13:48 +0900222 word = word.replace("\\\\", "\\");
223 word = word.replace("\\\"", "\"");
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -0700224
225 if (DEBUG_ROUTINE) Slog.e(LOGTAG, "found '" + word + "'");
226 parsed.add(word);
227
228 // find the beginning of the next word - either of these options
229 int nextSpace = rawEvent.indexOf(' ', current);
230 int nextQuote = rawEvent.indexOf(" \"", current);
231 if (DEBUG_ROUTINE) {
232 Slog.e(LOGTAG, "nextSpace=" + nextSpace + ", nextQuote=" + nextQuote);
233 }
234 if (nextQuote > -1 && nextQuote <= nextSpace) {
235 quoted = true;
236 current = nextQuote + 2;
237 } else {
238 quoted = false;
239 if (nextSpace > -1) {
240 current = nextSpace + 1;
241 }
242 } // else we just start the next word after the current and read til the end
243 if (DEBUG_ROUTINE) {
244 Slog.e(LOGTAG, "next loop - current=" + current +
245 ", length=" + length + ", quoted=" + quoted);
246 }
247 }
248 return parsed.toArray(new String[parsed.size()]);
249 }
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800250}