blob: 568369434629b6aba2f899a7d6d5c612d36e92f3 [file] [log] [blame]
Jeff Sharkeycd257fb2011-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
paulhub2225702021-11-17 09:35:33 +080019import android.util.Log;
Jeff Sharkey36ff7052011-11-30 18:13:54 -080020
Daichi Hironodbccfac2015-11-19 16:58:57 +090021import java.io.FileDescriptor;
Jeff Sharkey36ff7052011-11-30 18:13:54 -080022import java.util.ArrayList;
23
Jeff Sharkeycd257fb2011-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 Greenwalt7f03e442012-02-07 11:36:55 -080032 private final int mCmdNumber;
Jeff Sharkeycd257fb2011-11-18 17:09:01 -080033 private final int mCode;
34 private final String mMessage;
35 private final String mRawEvent;
Paul Lawrence19a1c3f2014-11-11 12:23:22 -080036 private final String mLogMessage;
Robert Greenwalt2a9efc22012-04-20 13:08:02 -070037 private String[] mParsed;
Daichi Hironodbccfac2015-11-19 16:58:57 +090038 private FileDescriptor[] mFdList;
Jeff Sharkeycd257fb2011-11-18 17:09:01 -080039
Paul Lawrence19a1c3f2014-11-11 12:23:22 -080040 private NativeDaemonEvent(int cmdNumber, int code, String message,
Daichi Hironodbccfac2015-11-19 16:58:57 +090041 String rawEvent, String logMessage, FileDescriptor[] fdList) {
Robert Greenwalt7f03e442012-02-07 11:36:55 -080042 mCmdNumber = cmdNumber;
Jeff Sharkeycd257fb2011-11-18 17:09:01 -080043 mCode = code;
44 mMessage = message;
45 mRawEvent = rawEvent;
Paul Lawrence19a1c3f2014-11-11 12:23:22 -080046 mLogMessage = logMessage;
Robert Greenwalt2a9efc22012-04-20 13:08:02 -070047 mParsed = null;
Daichi Hironodbccfac2015-11-19 16:58:57 +090048 mFdList = fdList;
Jeff Sharkeycd257fb2011-11-18 17:09:01 -080049 }
50
Paul Lawrence19a1c3f2014-11-11 12:23:22 -080051 static public final String SENSITIVE_MARKER = "{{sensitive}}";
52
Robert Greenwalt7f03e442012-02-07 11:36:55 -080053 public int getCmdNumber() {
54 return mCmdNumber;
55 }
56
Jeff Sharkeycd257fb2011-11-18 17:09:01 -080057 public int getCode() {
58 return mCode;
59 }
60
61 public String getMessage() {
62 return mMessage;
63 }
64
Daichi Hironodbccfac2015-11-19 16:58:57 +090065 public FileDescriptor[] getFileDescriptors() {
66 return mFdList;
67 }
68
Jeff Sharkeycd257fb2011-11-18 17:09:01 -080069 @Deprecated
70 public String getRawEvent() {
71 return mRawEvent;
72 }
73
74 @Override
75 public String toString() {
Paul Lawrence19a1c3f2014-11-11 12:23:22 -080076 return mLogMessage;
Jeff Sharkeycd257fb2011-11-18 17:09:01 -080077 }
78
79 /**
80 * Test if event represents a partial response which is continued in
81 * additional subsequent events.
82 */
83 public boolean isClassContinue() {
84 return mCode >= 100 && mCode < 200;
85 }
86
87 /**
88 * Test if event represents a command success.
89 */
90 public boolean isClassOk() {
91 return mCode >= 200 && mCode < 300;
92 }
93
94 /**
95 * Test if event represents a remote native daemon error.
96 */
97 public boolean isClassServerError() {
98 return mCode >= 400 && mCode < 500;
99 }
100
101 /**
102 * Test if event represents a command syntax or argument error.
103 */
104 public boolean isClassClientError() {
105 return mCode >= 500 && mCode < 600;
106 }
107
108 /**
109 * Test if event represents an unsolicited event from native daemon.
110 */
111 public boolean isClassUnsolicited() {
Robert Greenwalt7f03e442012-02-07 11:36:55 -0800112 return isClassUnsolicited(mCode);
113 }
114
115 private static boolean isClassUnsolicited(int code) {
116 return code >= 600 && code < 700;
Jeff Sharkeycd257fb2011-11-18 17:09:01 -0800117 }
118
119 /**
Jeff Sharkey36ff7052011-11-30 18:13:54 -0800120 * Verify this event matches the given code.
121 *
122 * @throws IllegalStateException if {@link #getCode()} doesn't match.
123 */
124 public void checkCode(int code) {
125 if (mCode != code) {
126 throw new IllegalStateException("Expected " + code + " but was: " + this);
127 }
128 }
129
130 /**
Jeff Sharkeycd257fb2011-11-18 17:09:01 -0800131 * Parse the given raw event into {@link NativeDaemonEvent} instance.
132 *
133 * @throws IllegalArgumentException when line doesn't match format expected
134 * from native side.
135 */
Daichi Hironodbccfac2015-11-19 16:58:57 +0900136 public static NativeDaemonEvent parseRawEvent(String rawEvent, FileDescriptor[] fdList) {
Robert Greenwalt7f03e442012-02-07 11:36:55 -0800137 final String[] parsed = rawEvent.split(" ");
138 if (parsed.length < 2) {
139 throw new IllegalArgumentException("Insufficient arguments");
Jeff Sharkeycd257fb2011-11-18 17:09:01 -0800140 }
141
Robert Greenwalt7f03e442012-02-07 11:36:55 -0800142 int skiplength = 0;
143
Jeff Sharkeycd257fb2011-11-18 17:09:01 -0800144 final int code;
145 try {
Robert Greenwalt7f03e442012-02-07 11:36:55 -0800146 code = Integer.parseInt(parsed[0]);
147 skiplength = parsed[0].length() + 1;
Jeff Sharkeycd257fb2011-11-18 17:09:01 -0800148 } catch (NumberFormatException e) {
149 throw new IllegalArgumentException("problem parsing code", e);
150 }
151
Robert Greenwalt7f03e442012-02-07 11:36:55 -0800152 int cmdNumber = -1;
153 if (isClassUnsolicited(code) == false) {
154 if (parsed.length < 3) {
155 throw new IllegalArgumentException("Insufficient arguemnts");
156 }
157 try {
158 cmdNumber = Integer.parseInt(parsed[1]);
159 skiplength += parsed[1].length() + 1;
160 } catch (NumberFormatException e) {
161 throw new IllegalArgumentException("problem parsing cmdNumber", e);
162 }
163 }
164
Paul Lawrence19a1c3f2014-11-11 12:23:22 -0800165 String logMessage = rawEvent;
166 if (parsed.length > 2 && parsed[2].equals(SENSITIVE_MARKER)) {
167 skiplength += parsed[2].length() + 1;
168 logMessage = parsed[0] + " " + parsed[1] + " {}";
169 }
170
Robert Greenwalt7f03e442012-02-07 11:36:55 -0800171 final String message = rawEvent.substring(skiplength);
172
Daichi Hironodbccfac2015-11-19 16:58:57 +0900173 return new NativeDaemonEvent(cmdNumber, code, message, rawEvent, logMessage, fdList);
Jeff Sharkeycd257fb2011-11-18 17:09:01 -0800174 }
Jeff Sharkey36ff7052011-11-30 18:13:54 -0800175
176 /**
177 * Filter the given {@link NativeDaemonEvent} list, returning
178 * {@link #getMessage()} for any events matching the requested code.
179 */
180 public static String[] filterMessageList(NativeDaemonEvent[] events, int matchCode) {
paulhub2225702021-11-17 09:35:33 +0800181 final ArrayList<String> result = new ArrayList<>();
Jeff Sharkey36ff7052011-11-30 18:13:54 -0800182 for (NativeDaemonEvent event : events) {
183 if (event.getCode() == matchCode) {
184 result.add(event.getMessage());
185 }
186 }
187 return result.toArray(new String[result.size()]);
188 }
Robert Greenwalt2a9efc22012-04-20 13:08:02 -0700189
190 /**
191 * Find the Nth field of the event.
192 *
193 * This ignores and code or cmdNum, the first return value is given for N=0.
194 * Also understands "\"quoted\" multiword responses" and tries them as a single field
195 */
196 public String getField(int n) {
197 if (mParsed == null) {
198 mParsed = unescapeArgs(mRawEvent);
199 }
200 n += 2; // skip code and command#
201 if (n > mParsed.length) return null;
202 return mParsed[n];
203 }
204
205 public static String[] unescapeArgs(String rawEvent) {
206 final boolean DEBUG_ROUTINE = false;
207 final String LOGTAG = "unescapeArgs";
208 final ArrayList<String> parsed = new ArrayList<String>();
209 final int length = rawEvent.length();
210 int current = 0;
211 int wordEnd = -1;
212 boolean quoted = false;
213
paulhub2225702021-11-17 09:35:33 +0800214 if (DEBUG_ROUTINE) Log.e(LOGTAG, "parsing '" + rawEvent + "'");
Robert Greenwalt2a9efc22012-04-20 13:08:02 -0700215 if (rawEvent.charAt(current) == '\"') {
216 quoted = true;
217 current++;
218 }
219 while (current < length) {
220 // find the end of the word
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700221 char terminator = quoted ? '\"' : ' ';
222 wordEnd = current;
223 while (wordEnd < length && rawEvent.charAt(wordEnd) != terminator) {
224 if (rawEvent.charAt(wordEnd) == '\\') {
225 // skip the escaped char
226 ++wordEnd;
Robert Greenwalt2a9efc22012-04-20 13:08:02 -0700227 }
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700228 ++wordEnd;
Robert Greenwalt2a9efc22012-04-20 13:08:02 -0700229 }
Sreeram Ramachandrana53dd7f2014-09-03 15:45:59 -0700230 if (wordEnd > length) wordEnd = length;
Robert Greenwalt2a9efc22012-04-20 13:08:02 -0700231 String word = rawEvent.substring(current, wordEnd);
232 current += word.length();
233 if (!quoted) {
234 word = word.trim();
235 } else {
236 current++; // skip the trailing quote
237 }
238 // unescape stuff within the word
You Kim0f1fea42012-10-28 22:13:48 +0900239 word = word.replace("\\\\", "\\");
240 word = word.replace("\\\"", "\"");
Robert Greenwalt2a9efc22012-04-20 13:08:02 -0700241
paulhub2225702021-11-17 09:35:33 +0800242 if (DEBUG_ROUTINE) Log.e(LOGTAG, "found '" + word + "'");
Robert Greenwalt2a9efc22012-04-20 13:08:02 -0700243 parsed.add(word);
244
245 // find the beginning of the next word - either of these options
246 int nextSpace = rawEvent.indexOf(' ', current);
247 int nextQuote = rawEvent.indexOf(" \"", current);
248 if (DEBUG_ROUTINE) {
paulhub2225702021-11-17 09:35:33 +0800249 Log.e(LOGTAG, "nextSpace=" + nextSpace + ", nextQuote=" + nextQuote);
Robert Greenwalt2a9efc22012-04-20 13:08:02 -0700250 }
251 if (nextQuote > -1 && nextQuote <= nextSpace) {
252 quoted = true;
253 current = nextQuote + 2;
254 } else {
255 quoted = false;
256 if (nextSpace > -1) {
257 current = nextSpace + 1;
258 }
259 } // else we just start the next word after the current and read til the end
260 if (DEBUG_ROUTINE) {
paulhub2225702021-11-17 09:35:33 +0800261 Log.e(LOGTAG, "next loop - current=" + current
262 + ", length=" + length + ", quoted=" + quoted);
Robert Greenwalt2a9efc22012-04-20 13:08:02 -0700263 }
264 }
265 return parsed.toArray(new String[parsed.size()]);
266 }
Jeff Sharkeycd257fb2011-11-18 17:09:01 -0800267}