• 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.commands.uinput;
18 
19 import android.util.Log;
20 import android.util.SparseArray;
21 
22 import java.io.File;
23 import java.io.FileInputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.InputStreamReader;
27 import java.io.UnsupportedEncodingException;
28 import java.util.Objects;
29 
30 /**
31  * Uinput class encapsulates execution of "uinput" command. It parses the provided input stream
32  * parameters as JSON file format, extract event entries and perform commands of event entries.
33  * Uinput device will be created when performing registration command and used to inject events.
34  */
35 public class Uinput {
36     private static final String TAG = "UINPUT";
37 
38     private final Event.Reader mReader;
39     private final SparseArray<Device> mDevices;
40 
usage()41     private static void usage() {
42         error("Usage: uinput [FILE]");
43     }
44 
45     /**
46      * Commandline "uinput" binary main entry
47      */
main(String[] args)48     public static void main(String[] args) {
49         if (args.length != 1) {
50             usage();
51             System.exit(1);
52         }
53 
54         InputStream stream = null;
55         try {
56             if (args[0].equals("-")) {
57                 stream = System.in;
58             } else {
59                 File f = new File(args[0]);
60                 stream = new FileInputStream(f);
61             }
62             (new Uinput(stream)).run();
63         } catch (Exception e) {
64             error("Uinput injection failed.", e);
65             System.exit(1);
66         } finally {
67             try {
68                 stream.close();
69             } catch (IOException e) {
70             }
71         }
72     }
73 
Uinput(InputStream in)74     private Uinput(InputStream in) {
75         mDevices = new SparseArray<Device>();
76         try {
77             mReader = new Event.Reader(new InputStreamReader(in, "UTF-8"));
78         } catch (UnsupportedEncodingException e) {
79             throw new RuntimeException(e);
80         }
81     }
82 
run()83     private void run() {
84         try {
85             Event e = null;
86             while ((e = mReader.getNextEvent()) != null) {
87                 process(e);
88             }
89         } catch (IOException ex) {
90             error("Error reading in events.", ex);
91         }
92 
93         for (int i = 0; i < mDevices.size(); i++) {
94             mDevices.valueAt(i).close();
95         }
96     }
97 
process(Event e)98     private void process(Event e) {
99         final int index = mDevices.indexOfKey(e.getId());
100         if (index < 0) {
101             if (e.getCommand() != Event.Command.REGISTER) {
102                 Log.e(TAG, "Unknown device id specified. Ignoring event.");
103                 return;
104             }
105             registerDevice(e);
106             return;
107         }
108 
109         final Device d = mDevices.valueAt(index);
110         switch (Objects.requireNonNull(e.getCommand())) {
111             case REGISTER ->
112                     error("Device id=" + e.getId() + " is already registered. Ignoring event.");
113             case INJECT -> d.injectEvent(e.getInjections());
114             case DELAY -> d.addDelay(e.getDuration());
115             case SYNC -> d.syncEvent(e.getSyncToken());
116         }
117     }
118 
registerDevice(Event e)119     private void registerDevice(Event e) {
120         if (!Event.Command.REGISTER.equals(e.getCommand())) {
121             throw new IllegalStateException(
122                     "Tried to send command \"" + e.getCommand() + "\" to an unregistered device!");
123         }
124         int id = e.getId();
125         Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(), e.getBus(),
126                 e.getConfiguration(), e.getFfEffectsMax(), e.getAbsInfo(), e.getPort());
127         mDevices.append(id, d);
128     }
129 
error(String msg)130     private static void error(String msg) {
131         error(msg, null);
132     }
133 
error(String msg, Exception e)134     private static void error(String msg, Exception e) {
135         Log.e(TAG, msg);
136         if (e != null) {
137             Log.e(TAG, Log.getStackTraceString(e));
138         }
139     }
140 }
141