blob: e6feda3dad223e7e858ec27a294af52c12b8176a [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
Daichi Hironodda65522015-11-19 16:58:57 +090022import java.io.FileDescriptor;
Jeff Sharkeyba2896e2011-11-30 18:13:54 -080023import java.util.ArrayList;
24
Jeff Sharkey31c6e482011-11-18 17:09:01 -080025/**
26 * Parsed event from native side of {@link NativeDaemonConnector}.
27 */
28public class NativeDaemonEvent {
29
30 // TODO: keep class ranges in sync with ResponseCode.h
31 // TODO: swap client and server error ranges to roughly mirror HTTP spec
32
Robert Greenwalt470007f2012-02-07 11:36:55 -080033 private final int mCmdNumber;
Jeff Sharkey31c6e482011-11-18 17:09:01 -080034 private final int mCode;
35 private final String mMessage;
36 private final String mRawEvent;
Paul Lawrencec38182f2014-11-11 12:23:22 -080037 private final String mLogMessage;
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -070038 private String[] mParsed;
Daichi Hironodda65522015-11-19 16:58:57 +090039 private FileDescriptor[] mFdList;
Jeff Sharkey31c6e482011-11-18 17:09:01 -080040
Paul Lawrencec38182f2014-11-11 12:23:22 -080041 private NativeDaemonEvent(int cmdNumber, int code, String message,
Daichi Hironodda65522015-11-19 16:58:57 +090042 String rawEvent, String logMessage, FileDescriptor[] fdList) {
Robert Greenwalt470007f2012-02-07 11:36:55 -080043 mCmdNumber = cmdNumber;
Jeff Sharkey31c6e482011-11-18 17:09:01 -080044 mCode = code;
45 mMessage = message;
46 mRawEvent = rawEvent;
Paul Lawrencec38182f2014-11-11 12:23:22 -080047 mLogMessage = logMessage;
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -070048 mParsed = null;
Daichi Hironodda65522015-11-19 16:58:57 +090049 mFdList = fdList;
Jeff Sharkey31c6e482011-11-18 17:09:01 -080050 }
51
Paul Lawrencec38182f2014-11-11 12:23:22 -080052 static public final String SENSITIVE_MARKER = "{{sensitive}}";
53
Robert Greenwalt470007f2012-02-07 11:36:55 -080054 public int getCmdNumber() {
55 return mCmdNumber;
56 }
57
Jeff Sharkey31c6e482011-11-18 17:09:01 -080058 public int getCode() {
59 return mCode;
60 }
61
62 public String getMessage() {
63 return mMessage;
64 }
65
Daichi Hironodda65522015-11-19 16:58:57 +090066 public FileDescriptor[] getFileDescriptors() {
67 return mFdList;
68 }
69
Jeff Sharkey31c6e482011-11-18 17:09:01 -080070 @Deprecated
71 public String getRawEvent() {
72 return mRawEvent;
73 }
74
75 @Override
76 public String toString() {
Paul Lawrencec38182f2014-11-11 12:23:22 -080077 return mLogMessage;
Jeff Sharkey31c6e482011-11-18 17:09:01 -080078 }
79
80 /**
81 * Test if event represents a partial response which is continued in
82 * additional subsequent events.
83 */
84 public boolean isClassContinue() {
85 return mCode >= 100 && mCode < 200;
86 }
87
88 /**
89 * Test if event represents a command success.
90 */
91 public boolean isClassOk() {
92 return mCode >= 200 && mCode < 300;
93 }
94
95 /**
96 * Test if event represents a remote native daemon error.
97 */
98 public boolean isClassServerError() {
99 return mCode >= 400 && mCode < 500;
100 }
101
102 /**
103 * Test if event represents a command syntax or argument error.
104 */
105 public boolean isClassClientError() {
106 return mCode >= 500 && mCode < 600;
107 }
108
109 /**
110 * Test if event represents an unsolicited event from native daemon.
111 */
112 public boolean isClassUnsolicited() {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800113 return isClassUnsolicited(mCode);
114 }
115
116 private static boolean isClassUnsolicited(int code) {
117 return code >= 600 && code < 700;
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800118 }
119
120 /**
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800121 * Verify this event matches the given code.
122 *
123 * @throws IllegalStateException if {@link #getCode()} doesn't match.
124 */
125 public void checkCode(int code) {
126 if (mCode != code) {
127 throw new IllegalStateException("Expected " + code + " but was: " + this);
128 }
129 }
130
131 /**
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800132 * Parse the given raw event into {@link NativeDaemonEvent} instance.
133 *
134 * @throws IllegalArgumentException when line doesn't match format expected
135 * from native side.
136 */
Daichi Hironodda65522015-11-19 16:58:57 +0900137 public static NativeDaemonEvent parseRawEvent(String rawEvent, FileDescriptor[] fdList) {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800138 final String[] parsed = rawEvent.split(" ");
139 if (parsed.length < 2) {
140 throw new IllegalArgumentException("Insufficient arguments");
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800141 }
142
Robert Greenwalt470007f2012-02-07 11:36:55 -0800143 int skiplength = 0;
144
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800145 final int code;
146 try {
Robert Greenwalt470007f2012-02-07 11:36:55 -0800147 code = Integer.parseInt(parsed[0]);
148 skiplength = parsed[0].length() + 1;
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800149 } catch (NumberFormatException e) {
150 throw new IllegalArgumentException("problem parsing code", e);
151 }
152
Robert Greenwalt470007f2012-02-07 11:36:55 -0800153 int cmdNumber = -1;
154 if (isClassUnsolicited(code) == false) {
155 if (parsed.length < 3) {
156 throw new IllegalArgumentException("Insufficient arguemnts");
157 }
158 try {
159 cmdNumber = Integer.parseInt(parsed[1]);
160 skiplength += parsed[1].length() + 1;
161 } catch (NumberFormatException e) {
162 throw new IllegalArgumentException("problem parsing cmdNumber", e);
163 }
164 }
165
Paul Lawrencec38182f2014-11-11 12:23:22 -0800166 String logMessage = rawEvent;
167 if (parsed.length > 2 && parsed[2].equals(SENSITIVE_MARKER)) {
168 skiplength += parsed[2].length() + 1;
169 logMessage = parsed[0] + " " + parsed[1] + " {}";
170 }
171
Robert Greenwalt470007f2012-02-07 11:36:55 -0800172 final String message = rawEvent.substring(skiplength);
173
Daichi Hironodda65522015-11-19 16:58:57 +0900174 return new NativeDaemonEvent(cmdNumber, code, message, rawEvent, logMessage, fdList);
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800175 }
Jeff Sharkeyba2896e2011-11-30 18:13:54 -0800176
177 /**
178 * Filter the given {@link NativeDaemonEvent} list, returning
179 * {@link #getMessage()} for any events matching the requested code.
180 */
181 public static String[] filterMessageList(NativeDaemonEvent[] events, int matchCode) {
182 final ArrayList<String> result = Lists.newArrayList();
183 for (NativeDaemonEvent event : events) {
184 if (event.getCode() == matchCode) {
185 result.add(event.getMessage());
186 }
187 }
188 return result.toArray(new String[result.size()]);
189 }
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -0700190
191 /**
192 * Find the Nth field of the event.
193 *
194 * This ignores and code or cmdNum, the first return value is given for N=0.
195 * Also understands "\"quoted\" multiword responses" and tries them as a single field
196 */
197 public String getField(int n) {
198 if (mParsed == null) {
199 mParsed = unescapeArgs(mRawEvent);
200 }
201 n += 2; // skip code and command#
202 if (n > mParsed.length) return null;
203 return mParsed[n];
204 }
205
206 public static String[] unescapeArgs(String rawEvent) {
207 final boolean DEBUG_ROUTINE = false;
208 final String LOGTAG = "unescapeArgs";
209 final ArrayList<String> parsed = new ArrayList<String>();
210 final int length = rawEvent.length();
211 int current = 0;
212 int wordEnd = -1;
213 boolean quoted = false;
214
215 if (DEBUG_ROUTINE) Slog.e(LOGTAG, "parsing '" + rawEvent + "'");
216 if (rawEvent.charAt(current) == '\"') {
217 quoted = true;
218 current++;
219 }
220 while (current < length) {
221 // find the end of the word
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700222 char terminator = quoted ? '\"' : ' ';
223 wordEnd = current;
224 while (wordEnd < length && rawEvent.charAt(wordEnd) != terminator) {
225 if (rawEvent.charAt(wordEnd) == '\\') {
226 // skip the escaped char
227 ++wordEnd;
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -0700228 }
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700229 ++wordEnd;
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -0700230 }
Sreeram Ramachandranef128842014-09-03 15:45:59 -0700231 if (wordEnd > length) wordEnd = length;
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -0700232 String word = rawEvent.substring(current, wordEnd);
233 current += word.length();
234 if (!quoted) {
235 word = word.trim();
236 } else {
237 current++; // skip the trailing quote
238 }
239 // unescape stuff within the word
You Kimd7663952012-10-28 22:13:48 +0900240 word = word.replace("\\\\", "\\");
241 word = word.replace("\\\"", "\"");
Robert Greenwalt2d34b4a2012-04-20 13:08:02 -0700242
243 if (DEBUG_ROUTINE) Slog.e(LOGTAG, "found '" + word + "'");
244 parsed.add(word);
245
246 // find the beginning of the next word - either of these options
247 int nextSpace = rawEvent.indexOf(' ', current);
248 int nextQuote = rawEvent.indexOf(" \"", current);
249 if (DEBUG_ROUTINE) {
250 Slog.e(LOGTAG, "nextSpace=" + nextSpace + ", nextQuote=" + nextQuote);
251 }
252 if (nextQuote > -1 && nextQuote <= nextSpace) {
253 quoted = true;
254 current = nextQuote + 2;
255 } else {
256 quoted = false;
257 if (nextSpace > -1) {
258 current = nextSpace + 1;
259 }
260 } // else we just start the next word after the current and read til the end
261 if (DEBUG_ROUTINE) {
262 Slog.e(LOGTAG, "next loop - current=" + current +
263 ", length=" + length + ", quoted=" + quoted);
264 }
265 }
266 return parsed.toArray(new String[parsed.size()]);
267 }
Jeff Sharkey31c6e482011-11-18 17:09:01 -0800268}