11f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright/*
21f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright * Copyright (C) 2015 The Android Open Source Project
31f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright *
41f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright * Licensed under the Apache License, Version 2.0 (the "License");
51f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright * you may not use this file except in compliance with the License.
61f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright * You may obtain a copy of the License at
71f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright *
81f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright *      http://www.apache.org/licenses/LICENSE-2.0
91f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright *
101f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright * Unless required by applicable law or agreed to in writing, software
111f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright * distributed under the License is distributed on an "AS IS" BASIS,
121f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright * See the License for the specific language governing permissions and
141f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright * limitations under the License.
151f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright */
161f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
171f2c7688c1f673790d61645632ae5e1838f021a4Michael Wrightpackage com.android.commands.hid;
181f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
191f2c7688c1f673790d61645632ae5e1838f021a4Michael Wrightimport android.util.JsonReader;
201f2c7688c1f673790d61645632ae5e1838f021a4Michael Wrightimport android.util.JsonToken;
211f2c7688c1f673790d61645632ae5e1838f021a4Michael Wrightimport android.util.Log;
221f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
231f2c7688c1f673790d61645632ae5e1838f021a4Michael Wrightimport java.io.InputStreamReader;
241f2c7688c1f673790d61645632ae5e1838f021a4Michael Wrightimport java.io.IOException;
251f2c7688c1f673790d61645632ae5e1838f021a4Michael Wrightimport java.util.ArrayList;
261f2c7688c1f673790d61645632ae5e1838f021a4Michael Wrightimport java.util.Arrays;
271f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
281f2c7688c1f673790d61645632ae5e1838f021a4Michael Wrightpublic class Event {
291f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    private static final String TAG = "HidEvent";
301f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
311f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    public static final String COMMAND_REGISTER = "register";
321f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    public static final String COMMAND_DELAY = "delay";
331f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    public static final String COMMAND_REPORT = "report";
341f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
351f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    private int mId;
361f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    private String mCommand;
371f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    private String mName;
381f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    private byte[] mDescriptor;
391f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    private int mVid;
401f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    private int mPid;
411f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    private byte[] mReport;
421f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    private int mDuration;
431f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
441f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    public int getId() {
451f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        return mId;
461f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    }
471f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
481f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    public String getCommand() {
491f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        return mCommand;
501f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    }
511f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
521f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    public String getName() {
531f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        return mName;
541f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    }
551f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
561f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    public byte[] getDescriptor() {
571f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        return mDescriptor;
581f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    }
591f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
601f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    public int getVendorId() {
611f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        return mVid;
621f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    }
631f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
641f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    public int getProductId() {
651f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        return mPid;
661f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    }
671f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
681f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    public byte[] getReport() {
691f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        return mReport;
701f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    }
711f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
721f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    public int getDuration() {
731f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        return mDuration;
741f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    }
751f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
761f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    public String toString() {
771f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        return "Event{id=" + mId
781f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            + ", command=" + String.valueOf(mCommand)
791f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            + ", name=" + String.valueOf(mName)
801f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            + ", descriptor=" + Arrays.toString(mDescriptor)
811f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            + ", vid=" + mVid
821f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            + ", pid=" + mPid
831f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            + ", report=" + Arrays.toString(mReport)
841f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            + ", duration=" + mDuration
851f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            + "}";
861f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    }
871f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
881f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    private static class Builder {
891f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        private Event mEvent;
901f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
911f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        public Builder() {
921f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            mEvent = new Event();
931f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        }
941f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
951f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        public void setId(int id) {
961f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            mEvent.mId = id;
971f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        }
981f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
991f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        private void setCommand(String command) {
1001f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            mEvent.mCommand = command;
1011f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        }
1021f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
1031f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        public void setName(String name) {
1041f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            mEvent.mName = name;
1051f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        }
1061f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
1071f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        public void setDescriptor(byte[] descriptor) {
1081f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            mEvent.mDescriptor = descriptor;
1091f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        }
1101f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
1111f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        public void setReport(byte[] report) {
1121f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            mEvent.mReport = report;
1131f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        }
1141f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
1151f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        public void setVid(int vid) {
1161f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            mEvent.mVid = vid;
1171f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        }
1181f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
1191f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        public void setPid(int pid) {
1201f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            mEvent.mPid = pid;
1211f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        }
1221f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
1231f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        public void setDuration(int duration) {
1241f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            mEvent.mDuration = duration;
1251f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        }
1261f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
1271f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        public Event build() {
1281f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            if (mEvent.mId == -1) {
1291f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                throw new IllegalStateException("No event id");
1301f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            } else if (mEvent.mCommand == null) {
1311f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                throw new IllegalStateException("Event does not contain a command");
1321f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            }
1331f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            if (COMMAND_REGISTER.equals(mEvent.mCommand)) {
1341f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                if (mEvent.mDescriptor == null) {
1351f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                    throw new IllegalStateException("Device registration is missing descriptor");
1361f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                }
1371f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            } else if (COMMAND_DELAY.equals(mEvent.mCommand)) {
1381f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                if (mEvent.mDuration <= 0) {
1391f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                    throw new IllegalStateException("Delay has missing or invalid duration");
1401f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                }
1411f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            } else if (COMMAND_REPORT.equals(mEvent.mCommand)) {
1421f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                if (mEvent.mReport == null) {
1431f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                    throw new IllegalStateException("Report command is missing report data");
1441f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                }
1451f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            }
1461f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            return mEvent;
1471f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        }
1481f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    }
1491f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
1501f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    public static class Reader {
1511f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        private JsonReader mReader;
1521f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
1531f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        public Reader(InputStreamReader in) {
1541f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            mReader = new JsonReader(in);
1551f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            mReader.setLenient(true);
1561f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        }
1571f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
1581f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        public Event getNextEvent() throws IOException {
1591f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            Event e = null;
1601f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            while (e == null && mReader.peek() != JsonToken.END_DOCUMENT) {
1611f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                Event.Builder eb = new Event.Builder();
1621f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                try {
1631f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                    mReader.beginObject();
1641f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                    while (mReader.hasNext()) {
1651f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                        String name = mReader.nextName();
1661f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                        switch (name) {
1671f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                            case "id":
1681f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                                eb.setId(readInt());
1691f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                                break;
1701f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                            case "command":
1711f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                                eb.setCommand(mReader.nextString());
1721f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                                break;
1731f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                            case "descriptor":
1741f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                                eb.setDescriptor(readData());
1751f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                                break;
1761f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                            case "name":
1771f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                                eb.setName(mReader.nextString());
1781f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                                break;
1791f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                            case "vid":
1801f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                                eb.setVid(readInt());
1811f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                                break;
1821f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                            case "pid":
1831f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                                eb.setPid(readInt());
1841f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                                break;
1851f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                            case "report":
1861f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                                eb.setReport(readData());
1871f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                                break;
1881f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                            case "duration":
1891f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                                eb.setDuration(readInt());
1901f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                                break;
1911f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                            default:
1921f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                                mReader.skipValue();
1931f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                        }
1941f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                    }
1951f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                    mReader.endObject();
1961f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                } catch (IllegalStateException ex) {
1971f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                    error("Error reading in object, ignoring.", ex);
1981f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                    consumeRemainingElements();
1991f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                    mReader.endObject();
2001f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                    continue;
2011f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                }
2021f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                e = eb.build();
2031f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            }
2041f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
2051f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            return e;
2061f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        }
2071f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
2081f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        private byte[] readData() throws IOException {
2091f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            ArrayList<Integer> data = new ArrayList<Integer>();
2101f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            try {
2111f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                mReader.beginArray();
2121f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                while (mReader.hasNext()) {
2131f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                    data.add(Integer.decode(mReader.nextString()));
2141f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                }
2151f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                mReader.endArray();
2161f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            } catch (IllegalStateException|NumberFormatException e) {
2171f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                consumeRemainingElements();
2181f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                mReader.endArray();
2191f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                throw new IllegalStateException("Encountered malformed data.", e);
2201f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            }
2211f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            byte[] rawData = new byte[data.size()];
2221f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            for (int i = 0; i < data.size(); i++) {
2231f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                int d = data.get(i);
2241f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                if ((d & 0xFF) != d) {
2251f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                    throw new IllegalStateException("Invalid data, all values must be byte-sized");
2261f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                }
2271f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                rawData[i] = (byte)d;
2281f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            }
2291f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            return rawData;
2301f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        }
2311f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
2321f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        private int readInt() throws IOException {
2331f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            String val = mReader.nextString();
2341f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            return Integer.decode(val);
2351f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        }
2361f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
2371f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        private void consumeRemainingElements() throws IOException {
2381f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            while (mReader.hasNext()) {
2391f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright                mReader.skipValue();
2401f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            }
2411f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        }
2421f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    }
2431f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
2441f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    private static void error(String msg) {
2451f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        error(msg, null);
2461f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    }
2471f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright
2481f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    private static void error(String msg, Exception e) {
2491f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        System.out.println(msg);
2501f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        Log.e(TAG, msg);
2511f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        if (e != null) {
2521f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright            Log.e(TAG, Log.getStackTraceString(e));
2531f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright        }
2541f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright    }
2551f2c7688c1f673790d61645632ae5e1838f021a4Michael Wright}
256