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