1 /*
2  * Copyright (C) 2018 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.cts.input;
18 
19 
20 import android.app.Instrumentation;
21 import android.util.Log;
22 
23 import androidx.annotation.GuardedBy;
24 
25 import org.json.JSONArray;
26 import org.json.JSONException;
27 import org.json.JSONObject;
28 
29 import java.io.IOException;
30 import java.util.ArrayList;
31 import java.util.List;
32 
33 /**
34  * Represents a virtual HID device registered through /dev/uhid.
35  */
36 public final class HidDevice extends VirtualInputDevice {
37     private static final String TAG = "HidDevice";
38     // hid executable expects "-" argument to read from stdin instead of a file
39     private static final String HID_COMMAND = "hid -";
40 
41     @GuardedBy("mLock")
42     private List<HidResultData> mResults = new ArrayList<HidResultData>();
43 
44     @Override
getShellCommand()45     protected String getShellCommand() {
46         return HID_COMMAND;
47     }
48 
49     @Override
readResults()50     protected void readResults() {
51         try {
52             mReader.beginObject();
53             HidResultData result = new HidResultData();
54             while (mReader.hasNext()) {
55                 String fieldName = mReader.nextName();
56                 if (fieldName.equals("eventId")) {
57                     result.eventId = Byte.decode(mReader.nextString());
58                 }
59                 if (fieldName.equals("deviceId")) {
60                     result.deviceId = Integer.decode(mReader.nextString());
61                 }
62                 if (fieldName.equals("reportType")) {
63                     result.reportType = Byte.decode(mReader.nextString());
64                 }
65                 if (fieldName.equals("reportData")) {
66                     result.reportData = readData();
67                 }
68             }
69             mReader.endObject();
70             addResult(result);
71         } catch (IOException ex) {
72             Log.w(TAG, "Exiting JSON Result reader. " + ex);
73         }
74     }
75 
HidDevice(Instrumentation instrumentation, int id, int vendorId, int productId, int sources, String registerCommand)76     public HidDevice(Instrumentation instrumentation, int id,
77             int vendorId, int productId, int sources, String registerCommand) {
78         super(instrumentation, id, vendorId, productId, sources, registerCommand);
79     }
80 
81     /**
82      * Get hid command return results as list of HidResultData
83      *
84      * @return List of HidResultData results
85      */
getResults(int deviceId, byte eventId)86     public synchronized List<HidResultData> getResults(int deviceId, byte eventId)
87             throws IOException {
88         List<HidResultData> results = new ArrayList<HidResultData>();
89         synchronized (mLock) {
90             for (HidResultData result : mResults) {
91                 if (deviceId == result.deviceId && eventId == result.eventId) {
92                     results.add(result);
93                 }
94             }
95         }
96         return results;
97     }
98 
99     /**
100      * Add hid command returned HidResultData result
101      *
102      * @param result HidResultData result
103      */
addResult(HidResultData result)104     public synchronized void addResult(HidResultData result) {
105         synchronized (mLock) {
106             if (mId == result.deviceId && mResults != null) {
107                 mResults.add(result);
108             }
109         }
110     }
111 
112     /**
113      * Send a HID report to the device. The report should follow the report descriptor
114      * that was specified during device registration.
115      * An example report:
116      * String report = "[0x01, 0x00, 0x00, 0x02]";
117      *
118      * @param report The report to send (a JSON-formatted array of hex)
119      */
sendHidReport(String report)120     public void sendHidReport(String report) {
121         JSONObject json = new JSONObject();
122         try {
123             json.put("command", "report");
124             json.put("id", mId);
125             json.put("report", new JSONArray(report));
126         } catch (JSONException e) {
127             throw new RuntimeException("Could not process HID report: " + report);
128         }
129         writeCommands(json.toString().getBytes());
130     }
131 
132 }
133