• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 import android.app.Instrumentation;
20 import android.util.Log;
21 import android.view.Display;
22 
23 import androidx.annotation.GuardedBy;
24 import androidx.annotation.Nullable;
25 
26 import org.json.JSONArray;
27 import org.json.JSONException;
28 import org.json.JSONObject;
29 
30 import java.io.IOException;
31 import java.util.ArrayList;
32 import java.util.List;
33 
34 /**
35  * Represents a virtual UINPUT device registered through /dev/uinput.
36  */
37 public class UinputDevice extends VirtualInputDevice {
38     private static final String TAG = "UinputDevice";
39     // uinput executable expects "-" argument to read from stdin instead of a file
40     private static final String UINPUT_COMMAND = "uinput -";
41 
42     @GuardedBy("mLock")
43     private final List<UinputResultData> mResults = new ArrayList<>();
44 
45     @Override
getShellCommand()46     protected String getShellCommand() {
47         return UINPUT_COMMAND;
48     }
49 
50     @Override
readResults()51     protected void readResults() {
52         try {
53             mReader.beginObject();
54             UinputResultData result = new UinputResultData();
55             while (mReader.hasNext()) {
56                 String fieldName = mReader.nextName();
57                 if (fieldName.equals("reason")) {
58                     result.reason = mReader.nextString();
59                 }
60                 if (fieldName.equals("id")) {
61                     result.deviceId = Integer.decode(mReader.nextString());
62                 }
63                 if (fieldName.equals("status")) {
64                     result.status = Integer.decode(mReader.nextString());
65                 }
66             }
67             mReader.endObject();
68             addResult(result);
69         } catch (IOException ex) {
70             Log.w(TAG, "Exiting JSON Result reader. " + ex);
71         }
72     }
73 
UinputDevice(Instrumentation instrumentation, int sources, UinputRegisterCommand cmd, @Nullable Display display)74     public UinputDevice(Instrumentation instrumentation, int sources, UinputRegisterCommand cmd,
75             @Nullable Display display) {
76         super(instrumentation, cmd.getId(), cmd.getVid(), cmd.getPid(), sources, cmd, display);
77     }
78 
79     /**
80      * Get uinput command return results as list of UinputResultData
81      *
82      * @return List of UinputResultData results
83      */
getResults(int deviceId, String reason)84     public synchronized List<UinputResultData> getResults(int deviceId, String reason)
85             throws IOException {
86         List<UinputResultData> results = new ArrayList<UinputResultData>();
87         synchronized (mLock) {
88             for (UinputResultData result : mResults) {
89                 if (deviceId == result.deviceId && reason.equals(result.reason)) {
90                     results.add(result);
91                 }
92             }
93         }
94         return results;
95     }
96 
97     /**
98      * Add uinput command returned UinputResultData result
99      *
100      * @param result UinputResultData result
101      */
addResult(UinputResultData result)102     public synchronized void addResult(UinputResultData result) {
103         synchronized (mLock) {
104             if (mId == result.deviceId && mResults != null) {
105                 mResults.add(result);
106             }
107         }
108     }
109 
110     /**
111      * Inject array of uinput events to the device.  The events array should follow the below
112      * format:
113      *
114      * String evdevEvents = "[1, 10, 1, 0, 0, 0]"
115      * The above string represents an event array of [EV_KEY, KEY_9, DOWN, EV_SYN, SYN_REPORT, 0]
116      * Hex strings ("0x01") are not supported inside the incoming string.
117      * The number of entries in the provided string has to be a multiple of 3.
118      *
119      * @param evdevEvents The uinput events to be injected.  (a JSON-formatted array of numbers)
120      */
injectEvents(String evdevEvents)121     public void injectEvents(String evdevEvents) {
122         JSONObject json = new JSONObject();
123         try {
124             json.put("command", "inject");
125             json.put("id", mId);
126             json.put("events", new JSONArray(evdevEvents));
127         } catch (JSONException e) {
128             throw new RuntimeException("Could not inject events: " + evdevEvents);
129         }
130         writeCommands(json.toString().getBytes());
131     }
132 
133     /**
134      * Inject a delay into the uinput process, guaranteeing that it will wait for at least the
135      * specified time before executing any more commands.
136      *
137      * @param delayMs The amount of time to delay, in milliseconds.
138      */
injectDelay(int delayMs)139     public void injectDelay(int delayMs) {
140         JSONObject json = new JSONObject();
141         try {
142             json.put("command", "delay");
143             json.put("id", mId);
144             json.put("duration", delayMs);
145         } catch (JSONException e) {
146             throw new RuntimeException("Could not inject delay of " + delayMs + "ms");
147         }
148         writeCommands(json.toString().getBytes());
149     }
150 }
151