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 17package com.android.commands.hid; 18 19import android.util.JsonReader; 20import android.util.JsonToken; 21import android.util.Log; 22 23import java.io.InputStreamReader; 24import java.io.IOException; 25import java.util.ArrayList; 26import java.util.Arrays; 27 28public class Event { 29 private static final String TAG = "HidEvent"; 30 31 public static final String COMMAND_REGISTER = "register"; 32 public static final String COMMAND_DELAY = "delay"; 33 public static final String COMMAND_REPORT = "report"; 34 35 private int mId; 36 private String mCommand; 37 private String mName; 38 private byte[] mDescriptor; 39 private int mVid; 40 private int mPid; 41 private byte[] mReport; 42 private int mDuration; 43 44 public int getId() { 45 return mId; 46 } 47 48 public String getCommand() { 49 return mCommand; 50 } 51 52 public String getName() { 53 return mName; 54 } 55 56 public byte[] getDescriptor() { 57 return mDescriptor; 58 } 59 60 public int getVendorId() { 61 return mVid; 62 } 63 64 public int getProductId() { 65 return mPid; 66 } 67 68 public byte[] getReport() { 69 return mReport; 70 } 71 72 public int getDuration() { 73 return mDuration; 74 } 75 76 public String toString() { 77 return "Event{id=" + mId 78 + ", command=" + String.valueOf(mCommand) 79 + ", name=" + String.valueOf(mName) 80 + ", descriptor=" + Arrays.toString(mDescriptor) 81 + ", vid=" + mVid 82 + ", pid=" + mPid 83 + ", report=" + Arrays.toString(mReport) 84 + ", duration=" + mDuration 85 + "}"; 86 } 87 88 private static class Builder { 89 private Event mEvent; 90 91 public Builder() { 92 mEvent = new Event(); 93 } 94 95 public void setId(int id) { 96 mEvent.mId = id; 97 } 98 99 private void setCommand(String command) { 100 mEvent.mCommand = command; 101 } 102 103 public void setName(String name) { 104 mEvent.mName = name; 105 } 106 107 public void setDescriptor(byte[] descriptor) { 108 mEvent.mDescriptor = descriptor; 109 } 110 111 public void setReport(byte[] report) { 112 mEvent.mReport = report; 113 } 114 115 public void setVid(int vid) { 116 mEvent.mVid = vid; 117 } 118 119 public void setPid(int pid) { 120 mEvent.mPid = pid; 121 } 122 123 public void setDuration(int duration) { 124 mEvent.mDuration = duration; 125 } 126 127 public Event build() { 128 if (mEvent.mId == -1) { 129 throw new IllegalStateException("No event id"); 130 } else if (mEvent.mCommand == null) { 131 throw new IllegalStateException("Event does not contain a command"); 132 } 133 if (COMMAND_REGISTER.equals(mEvent.mCommand)) { 134 if (mEvent.mDescriptor == null) { 135 throw new IllegalStateException("Device registration is missing descriptor"); 136 } 137 } else if (COMMAND_DELAY.equals(mEvent.mCommand)) { 138 if (mEvent.mDuration <= 0) { 139 throw new IllegalStateException("Delay has missing or invalid duration"); 140 } 141 } else if (COMMAND_REPORT.equals(mEvent.mCommand)) { 142 if (mEvent.mReport == null) { 143 throw new IllegalStateException("Report command is missing report data"); 144 } 145 } 146 return mEvent; 147 } 148 } 149 150 public static class Reader { 151 private JsonReader mReader; 152 153 public Reader(InputStreamReader in) { 154 mReader = new JsonReader(in); 155 mReader.setLenient(true); 156 } 157 158 public Event getNextEvent() throws IOException { 159 Event e = null; 160 while (e == null && mReader.peek() != JsonToken.END_DOCUMENT) { 161 Event.Builder eb = new Event.Builder(); 162 try { 163 mReader.beginObject(); 164 while (mReader.hasNext()) { 165 String name = mReader.nextName(); 166 switch (name) { 167 case "id": 168 eb.setId(readInt()); 169 break; 170 case "command": 171 eb.setCommand(mReader.nextString()); 172 break; 173 case "descriptor": 174 eb.setDescriptor(readData()); 175 break; 176 case "name": 177 eb.setName(mReader.nextString()); 178 break; 179 case "vid": 180 eb.setVid(readInt()); 181 break; 182 case "pid": 183 eb.setPid(readInt()); 184 break; 185 case "report": 186 eb.setReport(readData()); 187 break; 188 case "duration": 189 eb.setDuration(readInt()); 190 break; 191 default: 192 mReader.skipValue(); 193 } 194 } 195 mReader.endObject(); 196 } catch (IllegalStateException ex) { 197 error("Error reading in object, ignoring.", ex); 198 consumeRemainingElements(); 199 mReader.endObject(); 200 continue; 201 } 202 e = eb.build(); 203 } 204 205 return e; 206 } 207 208 private byte[] readData() throws IOException { 209 ArrayList<Integer> data = new ArrayList<Integer>(); 210 try { 211 mReader.beginArray(); 212 while (mReader.hasNext()) { 213 data.add(Integer.decode(mReader.nextString())); 214 } 215 mReader.endArray(); 216 } catch (IllegalStateException|NumberFormatException e) { 217 consumeRemainingElements(); 218 mReader.endArray(); 219 throw new IllegalStateException("Encountered malformed data.", e); 220 } 221 byte[] rawData = new byte[data.size()]; 222 for (int i = 0; i < data.size(); i++) { 223 int d = data.get(i); 224 if ((d & 0xFF) != d) { 225 throw new IllegalStateException("Invalid data, all values must be byte-sized"); 226 } 227 rawData[i] = (byte)d; 228 } 229 return rawData; 230 } 231 232 private int readInt() throws IOException { 233 String val = mReader.nextString(); 234 return Integer.decode(val); 235 } 236 237 private void consumeRemainingElements() throws IOException { 238 while (mReader.hasNext()) { 239 mReader.skipValue(); 240 } 241 } 242 } 243 244 private static void error(String msg) { 245 error(msg, null); 246 } 247 248 private static void error(String msg, Exception e) { 249 System.out.println(msg); 250 Log.e(TAG, msg); 251 if (e != null) { 252 Log.e(TAG, Log.getStackTraceString(e)); 253 } 254 } 255} 256