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