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;
362d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt    private String[] mParsed;
3731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
38470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt    private NativeDaemonEvent(int cmdNumber, int code, String message, String rawEvent) {
39470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        mCmdNumber = cmdNumber;
4031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        mCode = code;
4131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        mMessage = message;
4231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        mRawEvent = rawEvent;
432d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        mParsed = null;
4431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
4531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
46470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt    public int getCmdNumber() {
47470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        return mCmdNumber;
48470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt    }
49470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
5031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public int getCode() {
5131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        return mCode;
5231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
5331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
5431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public String getMessage() {
5531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        return mMessage;
5631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
5731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
5831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    @Deprecated
5931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public String getRawEvent() {
6031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        return mRawEvent;
6131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
6231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
6331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    @Override
6431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public String toString() {
6531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        return mRawEvent;
6631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
6731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
6831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    /**
6931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * Test if event represents a partial response which is continued in
7031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * additional subsequent events.
7131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     */
7231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public boolean isClassContinue() {
7331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        return mCode >= 100 && mCode < 200;
7431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
7531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
7631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    /**
7731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * Test if event represents a command success.
7831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     */
7931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public boolean isClassOk() {
8031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        return mCode >= 200 && mCode < 300;
8131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
8231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
8331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    /**
8431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * Test if event represents a remote native daemon error.
8531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     */
8631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public boolean isClassServerError() {
8731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        return mCode >= 400 && mCode < 500;
8831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
8931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
9031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    /**
9131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * Test if event represents a command syntax or argument error.
9231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     */
9331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public boolean isClassClientError() {
9431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        return mCode >= 500 && mCode < 600;
9531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
9631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
9731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    /**
9831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * Test if event represents an unsolicited event from native daemon.
9931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     */
10031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public boolean isClassUnsolicited() {
101470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        return isClassUnsolicited(mCode);
102470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt    }
103470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
104470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt    private static boolean isClassUnsolicited(int code) {
105470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        return code >= 600 && code < 700;
10631c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
10731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
10831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    /**
109ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * Verify this event matches the given code.
110ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     *
111ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * @throws IllegalStateException if {@link #getCode()} doesn't match.
112ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     */
113ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    public void checkCode(int code) {
114ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        if (mCode != code) {
115ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey            throw new IllegalStateException("Expected " + code + " but was: " + this);
116ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        }
117ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    }
118ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey
119ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    /**
12031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * Parse the given raw event into {@link NativeDaemonEvent} instance.
12131c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     *
12231c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     * @throws IllegalArgumentException when line doesn't match format expected
12331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     *             from native side.
12431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey     */
12531c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    public static NativeDaemonEvent parseRawEvent(String rawEvent) {
126470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        final String[] parsed = rawEvent.split(" ");
127470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        if (parsed.length < 2) {
128470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            throw new IllegalArgumentException("Insufficient arguments");
12931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        }
13031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
131470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        int skiplength = 0;
132470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
13331c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        final int code;
13431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        try {
135470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            code = Integer.parseInt(parsed[0]);
136470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            skiplength = parsed[0].length() + 1;
13731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        } catch (NumberFormatException e) {
13831c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey            throw new IllegalArgumentException("problem parsing code", e);
13931c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey        }
14031c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey
141470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        int cmdNumber = -1;
142470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        if (isClassUnsolicited(code) == false) {
143470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            if (parsed.length < 3) {
144470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                throw new IllegalArgumentException("Insufficient arguemnts");
145470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            }
146470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            try {
147470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                cmdNumber = Integer.parseInt(parsed[1]);
148470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                skiplength += parsed[1].length() + 1;
149470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            } catch (NumberFormatException e) {
150470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt                throw new IllegalArgumentException("problem parsing cmdNumber", e);
151470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt            }
152470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        }
153470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
154470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        final String message = rawEvent.substring(skiplength);
155470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt
156470007f69a258ccebb7c04927210a091dbcbe181Robert Greenwalt        return new NativeDaemonEvent(cmdNumber, code, message, rawEvent);
15731c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey    }
158ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey
159ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    /**
160ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * Filter the given {@link NativeDaemonEvent} list, returning
161ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     * {@link #getMessage()} for any events matching the requested code.
162ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey     */
163ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    public static String[] filterMessageList(NativeDaemonEvent[] events, int matchCode) {
164ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        final ArrayList<String> result = Lists.newArrayList();
165ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        for (NativeDaemonEvent event : events) {
166ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey            if (event.getCode() == matchCode) {
167ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey                result.add(event.getMessage());
168ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey            }
169ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        }
170ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey        return result.toArray(new String[result.size()]);
171ba2896e939f359e5857149f1a27212db71be012bJeff Sharkey    }
1722d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt
1732d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt    /**
1742d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt     * Find the Nth field of the event.
1752d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt     *
1762d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt     * This ignores and code or cmdNum, the first return value is given for N=0.
1772d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt     * Also understands "\"quoted\" multiword responses" and tries them as a single field
1782d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt     */
1792d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt    public String getField(int n) {
1802d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        if (mParsed == null) {
1812d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            mParsed = unescapeArgs(mRawEvent);
1822d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        }
1832d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        n += 2; // skip code and command#
1842d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        if (n > mParsed.length) return null;
1852d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            return mParsed[n];
1862d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        }
1872d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt
1882d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt    public static String[] unescapeArgs(String rawEvent) {
1892d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        final boolean DEBUG_ROUTINE = false;
1902d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        final String LOGTAG = "unescapeArgs";
1912d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        final ArrayList<String> parsed = new ArrayList<String>();
1922d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        final int length = rawEvent.length();
1932d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        int current = 0;
1942d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        int wordEnd = -1;
1952d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        boolean quoted = false;
1962d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt
1972d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        if (DEBUG_ROUTINE) Slog.e(LOGTAG, "parsing '" + rawEvent + "'");
1982d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        if (rawEvent.charAt(current) == '\"') {
1992d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            quoted = true;
2002d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            current++;
2012d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        }
2022d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        while (current < length) {
2032d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            // find the end of the word
2042d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            if (quoted) {
2052d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                wordEnd = current;
2062d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                while ((wordEnd = rawEvent.indexOf('\"', wordEnd)) != -1) {
2072d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                    if (rawEvent.charAt(wordEnd - 1) != '\\') {
2082d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                        break;
2092d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                    } else {
2102d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                        wordEnd++; // skip this escaped quote and keep looking
2112d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                    }
2122d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                }
2132d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            } else {
2142d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                wordEnd = rawEvent.indexOf(' ', current);
2152d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            }
2162d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            // if we didn't find the end-o-word token, take the rest of the string
2172d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            if (wordEnd == -1) wordEnd = length;
2182d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            String word = rawEvent.substring(current, wordEnd);
2192d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            current += word.length();
2202d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            if (!quoted) {
2212d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                word = word.trim();
2222d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            } else {
2232d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                current++;  // skip the trailing quote
2242d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            }
2252d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            // unescape stuff within the word
2262d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            word.replace("\\\\", "\\");
2272d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            word.replace("\\\"", "\"");
2282d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt
2292d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            if (DEBUG_ROUTINE) Slog.e(LOGTAG, "found '" + word + "'");
2302d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            parsed.add(word);
2312d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt
2322d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            // find the beginning of the next word - either of these options
2332d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            int nextSpace = rawEvent.indexOf(' ', current);
2342d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            int nextQuote = rawEvent.indexOf(" \"", current);
2352d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            if (DEBUG_ROUTINE) {
2362d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                Slog.e(LOGTAG, "nextSpace=" + nextSpace + ", nextQuote=" + nextQuote);
2372d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            }
2382d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            if (nextQuote > -1 && nextQuote <= nextSpace) {
2392d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                quoted = true;
2402d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                current = nextQuote + 2;
2412d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            } else {
2422d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                quoted = false;
2432d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                if (nextSpace > -1) {
2442d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                    current = nextSpace + 1;
2452d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                }
2462d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            } // else we just start the next word after the current and read til the end
2472d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            if (DEBUG_ROUTINE) {
2482d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                Slog.e(LOGTAG, "next loop - current=" + current +
2492d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt                        ", length=" + length + ", quoted=" + quoted);
2502d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt            }
2512d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        }
2522d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt        return parsed.toArray(new String[parsed.size()]);
2532d34b4a88531e51982b030c43d672ec2cc3d8f36Robert Greenwalt    }
25431c6e4817f6c967fc4f61c4f1d9f25743958f7deJeff Sharkey}
255