• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.commands.hid;
18 
19 import android.util.JsonReader;
20 import android.util.JsonToken;
21 import android.util.Log;
22 import android.util.SparseArray;
23 
24 import java.io.IOException;
25 import java.io.InputStreamReader;
26 import java.nio.ByteBuffer;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.HashMap;
30 import java.util.Map;
31 
32 public class Event {
33     private static final String TAG = "HidEvent";
34 
35     public static final String COMMAND_REGISTER = "register";
36     public static final String COMMAND_DELAY = "delay";
37     public static final String COMMAND_REPORT = "report";
38     public static final String COMMAND_SET_GET_REPORT_RESPONSE = "set_get_report_response";
39     public static final String COMMAND_SEND_SET_REPORT_REPLY = "send_set_report_reply";
40 
41     // These constants come from "include/uapi/linux/input.h" in the kernel
42     enum Bus {
43         USB(0x03), BLUETOOTH(0x05);
44 
Bus(int value)45         Bus(int value) {
46             mValue = value;
47         }
48 
getValue()49         int getValue() {
50             return mValue;
51         }
52 
53         private int mValue;
54     }
55 
56     private int mId;
57     private String mCommand;
58     private String mName;
59     private byte[] mDescriptor;
60     private int mVid;
61     private int mPid;
62     private Bus mBus;
63     private byte[] mReport;
64     private SparseArray<byte[]> mFeatureReports;
65     private Map<ByteBuffer, byte[]> mOutputs;
66     private int mDuration;
67     private Boolean mReply;
68 
getId()69     public int getId() {
70         return mId;
71     }
72 
getCommand()73     public String getCommand() {
74         return mCommand;
75     }
76 
getName()77     public String getName() {
78         return mName;
79     }
80 
getDescriptor()81     public byte[] getDescriptor() {
82         return mDescriptor;
83     }
84 
getVendorId()85     public int getVendorId() {
86         return mVid;
87     }
88 
getProductId()89     public int getProductId() {
90         return mPid;
91     }
92 
getBus()93     public int getBus() {
94         return mBus.getValue();
95     }
96 
getReport()97     public byte[] getReport() {
98         return mReport;
99     }
100 
getFeatureReports()101     public SparseArray<byte[]> getFeatureReports() {
102         return mFeatureReports;
103     }
104 
getOutputs()105     public Map<ByteBuffer, byte[]> getOutputs() {
106         return mOutputs;
107     }
108 
getDuration()109     public int getDuration() {
110         return mDuration;
111     }
112 
getReply()113     public Boolean getReply() {
114         return mReply;
115     }
116 
toString()117     public String toString() {
118         return "Event{id=" + mId
119             + ", command=" + String.valueOf(mCommand)
120             + ", name=" + String.valueOf(mName)
121             + ", descriptor=" + Arrays.toString(mDescriptor)
122             + ", vid=" + mVid
123             + ", pid=" + mPid
124             + ", bus=" + mBus
125             + ", report=" + Arrays.toString(mReport)
126             + ", feature_reports=" + mFeatureReports.toString()
127             + ", outputs=" + mOutputs.toString()
128             + ", duration=" + mDuration
129             + ", success=" + mReply.toString()
130             + "}";
131     }
132 
133     private static class Builder {
134         private Event mEvent;
135 
Builder()136         public Builder() {
137             mEvent = new Event();
138         }
139 
setId(int id)140         public void setId(int id) {
141             mEvent.mId = id;
142         }
143 
setCommand(String command)144         private void setCommand(String command) {
145             mEvent.mCommand = command;
146         }
147 
setName(String name)148         public void setName(String name) {
149             mEvent.mName = name;
150         }
151 
setDescriptor(byte[] descriptor)152         public void setDescriptor(byte[] descriptor) {
153             mEvent.mDescriptor = descriptor;
154         }
155 
setReport(byte[] report)156         public void setReport(byte[] report) {
157             mEvent.mReport = report;
158         }
159 
setFeatureReports(SparseArray<byte[]> reports)160         public void setFeatureReports(SparseArray<byte[]> reports) {
161             mEvent.mFeatureReports = reports;
162         }
163 
setOutputs(Map<ByteBuffer, byte[]> outputs)164         public void setOutputs(Map<ByteBuffer, byte[]> outputs) {
165             mEvent.mOutputs = outputs;
166         }
167 
setVid(int vid)168         public void setVid(int vid) {
169             mEvent.mVid = vid;
170         }
171 
setPid(int pid)172         public void setPid(int pid) {
173             mEvent.mPid = pid;
174         }
175 
setBus(Bus bus)176         public void setBus(Bus bus) {
177             mEvent.mBus = bus;
178         }
179 
setDuration(int duration)180         public void setDuration(int duration) {
181             mEvent.mDuration = duration;
182         }
183 
setReply(boolean success)184         public void setReply(boolean success) {
185             mEvent.mReply = success;
186         }
187 
build()188         public Event build() {
189             if (mEvent.mId == -1) {
190                 throw new IllegalStateException("No event id");
191             } else if (mEvent.mCommand == null) {
192                 throw new IllegalStateException("Event does not contain a command");
193             }
194             if (COMMAND_REGISTER.equals(mEvent.mCommand)) {
195                 if (mEvent.mDescriptor == null) {
196                     throw new IllegalStateException("Device registration is missing descriptor");
197                 }
198             }
199             if (COMMAND_SET_GET_REPORT_RESPONSE.equals(mEvent.mCommand)) {
200                 if (mEvent.mReport == null) {
201                     throw new IllegalStateException("Report command is missing response data");
202                 }
203             }
204             if (COMMAND_SEND_SET_REPORT_REPLY.equals(mEvent.mCommand)) {
205                 if (mEvent.mReply == null) {
206                     throw new IllegalStateException("Reply command is missing reply");
207                 }
208             } else if (COMMAND_DELAY.equals(mEvent.mCommand)) {
209                 if (mEvent.mDuration <= 0) {
210                     throw new IllegalStateException("Delay has missing or invalid duration");
211                 }
212             } else if (COMMAND_REPORT.equals(mEvent.mCommand)) {
213                 if (mEvent.mReport == null) {
214                     throw new IllegalStateException("Report command is missing report data");
215                 }
216             }
217             return mEvent;
218         }
219     }
220 
221     public static class Reader {
222         private JsonReader mReader;
223 
Reader(InputStreamReader in)224         public Reader(InputStreamReader in) {
225             mReader = new JsonReader(in);
226             mReader.setLenient(true);
227         }
228 
getNextEvent()229         public Event getNextEvent() throws IOException {
230             Event e = null;
231             while (e == null && mReader.peek() != JsonToken.END_DOCUMENT) {
232                 Event.Builder eb = new Event.Builder();
233                 try {
234                     mReader.beginObject();
235                     while (mReader.hasNext()) {
236                         String name = mReader.nextName();
237                         switch (name) {
238                             case "id":
239                                 eb.setId(readInt());
240                                 break;
241                             case "command":
242                                 eb.setCommand(mReader.nextString());
243                                 break;
244                             case "descriptor":
245                                 eb.setDescriptor(readData());
246                                 break;
247                             case "name":
248                                 eb.setName(mReader.nextString());
249                                 break;
250                             case "vid":
251                                 eb.setVid(readInt());
252                                 break;
253                             case "pid":
254                                 eb.setPid(readInt());
255                                 break;
256                             case "bus":
257                                 eb.setBus(readBus());
258                                 break;
259                             case "report":
260                                 eb.setReport(readData());
261                                 break;
262                             case "feature_reports":
263                                 eb.setFeatureReports(readFeatureReports());
264                                 break;
265                             case "outputs":
266                                 eb.setOutputs(readOutputs());
267                                 break;
268                             case "duration":
269                                 eb.setDuration(readInt());
270                                 break;
271                             case "success":
272                                 eb.setReply(readBool());
273                                 break;
274                             default:
275                                 mReader.skipValue();
276                         }
277                     }
278                     mReader.endObject();
279                 } catch (IllegalStateException ex) {
280                     error("Error reading in object, ignoring.", ex);
281                     consumeRemainingElements();
282                     mReader.endObject();
283                     continue;
284                 }
285                 e = eb.build();
286             }
287 
288             return e;
289         }
290 
readData()291         private byte[] readData() throws IOException {
292             ArrayList<Integer> data = new ArrayList<Integer>();
293             try {
294                 mReader.beginArray();
295                 while (mReader.hasNext()) {
296                     data.add(Integer.decode(mReader.nextString()));
297                 }
298                 mReader.endArray();
299             } catch (IllegalStateException|NumberFormatException e) {
300                 consumeRemainingElements();
301                 mReader.endArray();
302                 throw new IllegalStateException("Encountered malformed data.", e);
303             }
304             byte[] rawData = new byte[data.size()];
305             for (int i = 0; i < data.size(); i++) {
306                 int d = data.get(i);
307                 if ((d & 0xFF) != d) {
308                     throw new IllegalStateException("Invalid data, all values must be byte-sized");
309                 }
310                 rawData[i] = (byte)d;
311             }
312             return rawData;
313         }
314 
readInt()315         private int readInt() throws IOException {
316             String val = mReader.nextString();
317             return Integer.decode(val);
318         }
319 
readBool()320         private boolean readBool() throws IOException {
321             String val = mReader.nextString();
322             return Boolean.parseBoolean(val);
323         }
324 
readBus()325         private Bus readBus() throws IOException {
326             String val = mReader.nextString();
327             return Bus.valueOf(val.toUpperCase());
328         }
329 
readFeatureReports()330         private SparseArray<byte[]> readFeatureReports()
331                 throws IllegalStateException, IOException {
332             SparseArray<byte[]> featureReports = new SparseArray<>();
333             try {
334                 mReader.beginArray();
335                 while (mReader.hasNext()) {
336                     // If "id" is not specified, it defaults to 0, which means
337                     // report does not contain report ID (based on HID specs).
338                     int id = 0;
339                     byte[] data = null;
340                     mReader.beginObject();
341                     while (mReader.hasNext()) {
342                         String name = mReader.nextName();
343                         switch (name) {
344                             case "id":
345                                 id = readInt();
346                                 break;
347                             case "data":
348                                 data = readData();
349                                 break;
350                             default:
351                                 consumeRemainingElements();
352                                 mReader.endObject();
353                                 throw new IllegalStateException("Invalid key in feature report: "
354                                         + name);
355                         }
356                     }
357                     mReader.endObject();
358                     if (data != null) {
359                         featureReports.put(id, data);
360                     }
361                 }
362                 mReader.endArray();
363             } catch (IllegalStateException | NumberFormatException e) {
364                 consumeRemainingElements();
365                 mReader.endArray();
366                 throw new IllegalStateException("Encountered malformed data.", e);
367             }
368             return featureReports;
369         }
370 
readOutputs()371         private Map<ByteBuffer, byte[]> readOutputs()
372                 throws IllegalStateException, IOException {
373             Map<ByteBuffer, byte[]> outputs = new HashMap<>();
374 
375             try {
376                 mReader.beginArray();
377                 while (mReader.hasNext()) {
378                     byte[] output = null;
379                     byte[] response = null;
380                     mReader.beginObject();
381                     while (mReader.hasNext()) {
382                         String name = mReader.nextName();
383                         switch (name) {
384                             case "description":
385                                 // Description is only used to keep track of the output responses
386                                 mReader.nextString();
387                                 break;
388                             case "output":
389                                 output = readData();
390                                 break;
391                             case "response":
392                                 response = readData();
393                                 break;
394                             default:
395                                 consumeRemainingElements();
396                                 mReader.endObject();
397                                 throw new IllegalStateException("Invalid key in outputs: " + name);
398                         }
399                     }
400                     mReader.endObject();
401                     if (output != null) {
402                         outputs.put(ByteBuffer.wrap(output), response);
403                     }
404                 }
405                 mReader.endArray();
406             } catch (IllegalStateException | NumberFormatException e) {
407                 consumeRemainingElements();
408                 mReader.endArray();
409                 throw new IllegalStateException("Encountered malformed data.", e);
410             }
411             return outputs;
412         }
413 
consumeRemainingElements()414         private void consumeRemainingElements() throws IOException {
415             while (mReader.hasNext()) {
416                 mReader.skipValue();
417             }
418         }
419     }
420 
error(String msg, Exception e)421     private static void error(String msg, Exception e) {
422         System.out.println(msg);
423         Log.e(TAG, msg);
424         if (e != null) {
425             Log.e(TAG, Log.getStackTraceString(e));
426         }
427     }
428 }
429