1/*
2 * Copyright (C) 2015 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.commands.hid;
18
19import android.util.JsonReader;
20import android.util.JsonToken;
21import android.util.Log;
22
23import java.io.InputStreamReader;
24import java.io.IOException;
25import java.util.ArrayList;
26import java.util.Arrays;
27
28public class Event {
29    private static final String TAG = "HidEvent";
30
31    public static final String COMMAND_REGISTER = "register";
32    public static final String COMMAND_DELAY = "delay";
33    public static final String COMMAND_REPORT = "report";
34
35    private int mId;
36    private String mCommand;
37    private String mName;
38    private byte[] mDescriptor;
39    private int mVid;
40    private int mPid;
41    private byte[] mReport;
42    private int mDuration;
43
44    public int getId() {
45        return mId;
46    }
47
48    public String getCommand() {
49        return mCommand;
50    }
51
52    public String getName() {
53        return mName;
54    }
55
56    public byte[] getDescriptor() {
57        return mDescriptor;
58    }
59
60    public int getVendorId() {
61        return mVid;
62    }
63
64    public int getProductId() {
65        return mPid;
66    }
67
68    public byte[] getReport() {
69        return mReport;
70    }
71
72    public int getDuration() {
73        return mDuration;
74    }
75
76    public String toString() {
77        return "Event{id=" + mId
78            + ", command=" + String.valueOf(mCommand)
79            + ", name=" + String.valueOf(mName)
80            + ", descriptor=" + Arrays.toString(mDescriptor)
81            + ", vid=" + mVid
82            + ", pid=" + mPid
83            + ", report=" + Arrays.toString(mReport)
84            + ", duration=" + mDuration
85            + "}";
86    }
87
88    private static class Builder {
89        private Event mEvent;
90
91        public Builder() {
92            mEvent = new Event();
93        }
94
95        public void setId(int id) {
96            mEvent.mId = id;
97        }
98
99        private void setCommand(String command) {
100            mEvent.mCommand = command;
101        }
102
103        public void setName(String name) {
104            mEvent.mName = name;
105        }
106
107        public void setDescriptor(byte[] descriptor) {
108            mEvent.mDescriptor = descriptor;
109        }
110
111        public void setReport(byte[] report) {
112            mEvent.mReport = report;
113        }
114
115        public void setVid(int vid) {
116            mEvent.mVid = vid;
117        }
118
119        public void setPid(int pid) {
120            mEvent.mPid = pid;
121        }
122
123        public void setDuration(int duration) {
124            mEvent.mDuration = duration;
125        }
126
127        public Event build() {
128            if (mEvent.mId == -1) {
129                throw new IllegalStateException("No event id");
130            } else if (mEvent.mCommand == null) {
131                throw new IllegalStateException("Event does not contain a command");
132            }
133            if (COMMAND_REGISTER.equals(mEvent.mCommand)) {
134                if (mEvent.mDescriptor == null) {
135                    throw new IllegalStateException("Device registration is missing descriptor");
136                }
137            } else if (COMMAND_DELAY.equals(mEvent.mCommand)) {
138                if (mEvent.mDuration <= 0) {
139                    throw new IllegalStateException("Delay has missing or invalid duration");
140                }
141            } else if (COMMAND_REPORT.equals(mEvent.mCommand)) {
142                if (mEvent.mReport == null) {
143                    throw new IllegalStateException("Report command is missing report data");
144                }
145            }
146            return mEvent;
147        }
148    }
149
150    public static class Reader {
151        private JsonReader mReader;
152
153        public Reader(InputStreamReader in) {
154            mReader = new JsonReader(in);
155            mReader.setLenient(true);
156        }
157
158        public Event getNextEvent() throws IOException {
159            Event e = null;
160            while (e == null && mReader.peek() != JsonToken.END_DOCUMENT) {
161                Event.Builder eb = new Event.Builder();
162                try {
163                    mReader.beginObject();
164                    while (mReader.hasNext()) {
165                        String name = mReader.nextName();
166                        switch (name) {
167                            case "id":
168                                eb.setId(readInt());
169                                break;
170                            case "command":
171                                eb.setCommand(mReader.nextString());
172                                break;
173                            case "descriptor":
174                                eb.setDescriptor(readData());
175                                break;
176                            case "name":
177                                eb.setName(mReader.nextString());
178                                break;
179                            case "vid":
180                                eb.setVid(readInt());
181                                break;
182                            case "pid":
183                                eb.setPid(readInt());
184                                break;
185                            case "report":
186                                eb.setReport(readData());
187                                break;
188                            case "duration":
189                                eb.setDuration(readInt());
190                                break;
191                            default:
192                                mReader.skipValue();
193                        }
194                    }
195                    mReader.endObject();
196                } catch (IllegalStateException ex) {
197                    error("Error reading in object, ignoring.", ex);
198                    consumeRemainingElements();
199                    mReader.endObject();
200                    continue;
201                }
202                e = eb.build();
203            }
204
205            return e;
206        }
207
208        private byte[] readData() throws IOException {
209            ArrayList<Integer> data = new ArrayList<Integer>();
210            try {
211                mReader.beginArray();
212                while (mReader.hasNext()) {
213                    data.add(Integer.decode(mReader.nextString()));
214                }
215                mReader.endArray();
216            } catch (IllegalStateException|NumberFormatException e) {
217                consumeRemainingElements();
218                mReader.endArray();
219                throw new IllegalStateException("Encountered malformed data.", e);
220            }
221            byte[] rawData = new byte[data.size()];
222            for (int i = 0; i < data.size(); i++) {
223                int d = data.get(i);
224                if ((d & 0xFF) != d) {
225                    throw new IllegalStateException("Invalid data, all values must be byte-sized");
226                }
227                rawData[i] = (byte)d;
228            }
229            return rawData;
230        }
231
232        private int readInt() throws IOException {
233            String val = mReader.nextString();
234            return Integer.decode(val);
235        }
236
237        private void consumeRemainingElements() throws IOException {
238            while (mReader.hasNext()) {
239                mReader.skipValue();
240            }
241        }
242    }
243
244    private static void error(String msg) {
245        error(msg, null);
246    }
247
248    private static void error(String msg, Exception e) {
249        System.out.println(msg);
250        Log.e(TAG, msg);
251        if (e != null) {
252            Log.e(TAG, Log.getStackTraceString(e));
253        }
254    }
255}
256