131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey/*
231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * Copyright (C) 2011 The Android Open Source Project
331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey *
431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * you may not use this file except in compliance with the License.
631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * You may obtain a copy of the License at
731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey *
831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey *
1031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * Unless required by applicable law or agreed to in writing, software
1131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
1231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * See the License for the specific language governing permissions and
1431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * limitations under the License.
1531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey */
1631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
1731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkeypackage com.android.server;
1831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
192d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwaltimport android.util.Slog;
20ba2896e939f359e5857149f1a27212db71be012bJeff Sharkeyimport com.google.android.collect.Lists;
21ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey
22ba2896e939f359e5857149f1a27212db71be012bJeff Sharkeyimport java.util.ArrayList;
23ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey
2431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey/**
2531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey * Parsed event from native side of {@link NativeDaemonConnector}.
2631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey */
2731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkeypublic class NativeDaemonEvent {
2831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
2931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    // TODO: keep class ranges in sync with ResponseCode.h
3031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    // TODO: swap client and server error ranges to roughly mirror HTTP spec
3131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
32470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt    private final int mCmdNumber;
3331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    private final int mCode;
3431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    private final String mMessage;
3531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    private final String mRawEvent;
36c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence    private final String mLogMessage;
372d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt    private String[] mParsed;
3831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
39c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence    private NativeDaemonEvent(int cmdNumber, int code, String message,
40c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence                              String rawEvent, String logMessage) {
41470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        mCmdNumber = cmdNumber;
4231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        mCode = code;
4331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        mMessage = message;
4431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        mRawEvent = rawEvent;
45c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence        mLogMessage = logMessage;
462d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        mParsed = null;
4731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
4831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
49c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence    static public final String SENSITIVE_MARKER = "{{sensitive}}";
50c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence
51470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt    public int getCmdNumber() {
52470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        return mCmdNumber;
53470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt    }
54470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
5531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public int getCode() {
5631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        return mCode;
5731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
5831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
5931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public String getMessage() {
6031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        return mMessage;
6131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
6231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
6331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    @Deprecated
6431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public String getRawEvent() {
6531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        return mRawEvent;
6631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
6731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
6831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    @Override
6931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public String toString() {
70c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence        return mLogMessage;
7131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
7231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
7331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    /**
7431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * Test if event represents a partial response which is continued in
7531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * additional subsequent events.
7631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     */
7731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public boolean isClassContinue() {
7831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        return mCode >= 100 && mCode < 200;
7931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
8031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
8131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    /**
8231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * Test if event represents a command success.
8331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     */
8431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public boolean isClassOk() {
8531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        return mCode >= 200 && mCode < 300;
8631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
8731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
8831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    /**
8931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * Test if event represents a remote native daemon error.
9031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     */
9131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public boolean isClassServerError() {
9231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        return mCode >= 400 && mCode < 500;
9331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
9431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
9531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    /**
9631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * Test if event represents a command syntax or argument error.
9731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     */
9831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public boolean isClassClientError() {
9931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        return mCode >= 500 && mCode < 600;
10031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
10131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
10231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    /**
10331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * Test if event represents an unsolicited event from native daemon.
10431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     */
10531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public boolean isClassUnsolicited() {
106470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        return isClassUnsolicited(mCode);
107470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt    }
108470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
109470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt    private static boolean isClassUnsolicited(int code) {
110470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        return code >= 600 && code < 700;
11131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
11231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
11331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    /**
114ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * Verify this event matches the given code.
115ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     *
116ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * @throws IllegalStateException if {@link #getCode()} doesn't match.
117ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     */
118ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    public void checkCode(int code) {
119ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        if (mCode != code) {
120ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey            throw new IllegalStateException("Expected " + code + " but was: " + this);
121ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        }
122ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    }
123ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey
124ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    /**
12531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * Parse the given raw event into {@link NativeDaemonEvent} instance.
12631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     *
12731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * @throws IllegalArgumentException when line doesn't match format expected
12831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     *             from native side.
12931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     */
13031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public static NativeDaemonEvent parseRawEvent(String rawEvent) {
131470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        final String[] parsed = rawEvent.split(" ");
132470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        if (parsed.length < 2) {
133470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            throw new IllegalArgumentException("Insufficient arguments");
13431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        }
13531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
136470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        int skiplength = 0;
137470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
13831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        final int code;
13931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        try {
140470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            code = Integer.parseInt(parsed[0]);
141470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            skiplength = parsed[0].length() + 1;
14231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        } catch (NumberFormatException e) {
14331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            throw new IllegalArgumentException("problem parsing code", e);
14431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        }
14531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
146470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        int cmdNumber = -1;
147470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        if (isClassUnsolicited(code) == false) {
148470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            if (parsed.length < 3) {
149470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                throw new IllegalArgumentException("Insufficient arguemnts");
150470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            }
151470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            try {
152470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                cmdNumber = Integer.parseInt(parsed[1]);
153470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                skiplength += parsed[1].length() + 1;
154470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            } catch (NumberFormatException e) {
155470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                throw new IllegalArgumentException("problem parsing cmdNumber", e);
156470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            }
157470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        }
158470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
159c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence        String logMessage = rawEvent;
160c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence        if (parsed.length > 2 && parsed[2].equals(SENSITIVE_MARKER)) {
161c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence            skiplength += parsed[2].length() + 1;
162c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence            logMessage = parsed[0] + " " + parsed[1] + " {}";
163c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence        }
164c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence
165470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        final String message = rawEvent.substring(skiplength);
166470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
167c38182ff3b1ecaf5e7a7270074bbab7f37819d3dPaul Lawrence        return new NativeDaemonEvent(cmdNumber, code, message, rawEvent, logMessage);
16831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
169ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey
170ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    /**
171ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * Filter the given {@link NativeDaemonEvent} list, returning
172ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * {@link #getMessage()} for any events matching the requested code.
173ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     */
174ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    public static String[] filterMessageList(NativeDaemonEvent[] events, int matchCode) {
175ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        final ArrayList<String> result = Lists.newArrayList();
176ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        for (NativeDaemonEvent event : events) {
177ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey            if (event.getCode() == matchCode) {
178ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey                result.add(event.getMessage());
179ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey            }
180ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        }
181ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        return result.toArray(new String[result.size()]);
182ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    }
1832d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt
1842d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt    /**
1852d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt     * Find the Nth field of the event.
1862d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt     *
1872d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt     * This ignores and code or cmdNum, the first return value is given for N=0.
1882d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt     * Also understands "\"quoted\" multiword responses" and tries them as a single field
1892d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt     */
1902d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt    public String getField(int n) {
1912d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        if (mParsed == null) {
1922d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            mParsed = unescapeArgs(mRawEvent);
1932d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        }
1942d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        n += 2; // skip code and command#
1952d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        if (n > mParsed.length) return null;
1962d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            return mParsed[n];
1972d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        }
1982d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt
1992d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt    public static String[] unescapeArgs(String rawEvent) {
2002d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        final boolean DEBUG_ROUTINE = false;
2012d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        final String LOGTAG = "unescapeArgs";
2022d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        final ArrayList<String> parsed = new ArrayList<String>();
2032d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        final int length = rawEvent.length();
2042d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        int current = 0;
2052d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        int wordEnd = -1;
2062d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        boolean quoted = false;
2072d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt
2082d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        if (DEBUG_ROUTINE) Slog.e(LOGTAG, "parsing '" + rawEvent + "'");
2092d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        if (rawEvent.charAt(current) == '\"') {
2102d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            quoted = true;
2112d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            current++;
2122d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        }
2132d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        while (current < length) {
2142d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            // find the end of the word
215ef12884da7c8844f8dd27cbc9c9980f685b73a2cSreeram Ramachandran            char terminator = quoted ? '\"' : ' ';
216ef12884da7c8844f8dd27cbc9c9980f685b73a2cSreeram Ramachandran            wordEnd = current;
217ef12884da7c8844f8dd27cbc9c9980f685b73a2cSreeram Ramachandran            while (wordEnd < length && rawEvent.charAt(wordEnd) != terminator) {
218ef12884da7c8844f8dd27cbc9c9980f685b73a2cSreeram Ramachandran                if (rawEvent.charAt(wordEnd) == '\\') {
219ef12884da7c8844f8dd27cbc9c9980f685b73a2cSreeram Ramachandran                    // skip the escaped char
220ef12884da7c8844f8dd27cbc9c9980f685b73a2cSreeram Ramachandran                    ++wordEnd;
2212d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                }
222ef12884da7c8844f8dd27cbc9c9980f685b73a2cSreeram Ramachandran                ++wordEnd;
2232d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            }
224ef12884da7c8844f8dd27cbc9c9980f685b73a2cSreeram Ramachandran            if (wordEnd > length) wordEnd = length;
2252d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            String word = rawEvent.substring(current, wordEnd);
2262d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            current += word.length();
2272d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            if (!quoted) {
2282d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                word = word.trim();
2292d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            } else {
2302d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                current++;  // skip the trailing quote
2312d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            }
2322d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            // unescape stuff within the word
233d7663955d0e00ddc7e93e1cc70559857c3fdb2ebYou Kim            word = word.replace("\\\\", "\\");
234d7663955d0e00ddc7e93e1cc70559857c3fdb2ebYou Kim            word = word.replace("\\\"", "\"");
2352d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt
2362d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            if (DEBUG_ROUTINE) Slog.e(LOGTAG, "found '" + word + "'");
2372d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            parsed.add(word);
2382d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt
2392d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            // find the beginning of the next word - either of these options
2402d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            int nextSpace = rawEvent.indexOf(' ', current);
2412d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            int nextQuote = rawEvent.indexOf(" \"", current);
2422d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            if (DEBUG_ROUTINE) {
2432d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                Slog.e(LOGTAG, "nextSpace=" + nextSpace + ", nextQuote=" + nextQuote);
2442d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            }
2452d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            if (nextQuote > -1 && nextQuote <= nextSpace) {
2462d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                quoted = true;
2472d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                current = nextQuote + 2;
2482d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            } else {
2492d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                quoted = false;
2502d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                if (nextSpace > -1) {
2512d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                    current = nextSpace + 1;
2522d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                }
2532d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            } // else we just start the next word after the current and read til the end
2542d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            if (DEBUG_ROUTINE) {
2552d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                Slog.e(LOGTAG, "next loop - current=" + current +
2562d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                        ", length=" + length + ", quoted=" + quoted);
2572d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            }
2582d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        }
2592d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        return parsed.toArray(new String[parsed.size()]);
2602d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt    }
26131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey}
262