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