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