Service and KitchenSink logic to read the kernel's input event queues
This patch provides a native service that can manually be built, installed to a device, and launched, e.g.
sh# /system/bin/com.android.car.keventreader /dev/input/event*
The service will monitor the files provided as input (expecting them to be kernel input queues in the format
described at, e.g. https://github.com/torvalds/linux/blob/master/include/uapi/linux/input.h), and generate
events upon reading EV_KEY events from each such file.
The patch also includes a KitchenSink hook to talk to the native service and receive key events from it.
Bug: 78258802
Test: manual on Mojave
Change-Id: If7f2f1f72e4dc6a26cd9d32b31e90e494d55650b
Merged-In: If7f2f1f72e4dc6a26cd9d32b31e90e494d55650b
diff --git a/tests/EmbeddedKitchenSinkApp/Android.mk b/tests/EmbeddedKitchenSinkApp/Android.mk
index 3c04fd5..7a8c145 100644
--- a/tests/EmbeddedKitchenSinkApp/Android.mk
+++ b/tests/EmbeddedKitchenSinkApp/Android.mk
@@ -47,7 +47,8 @@
LOCAL_STATIC_JAVA_LIBRARIES += \
android.hidl.base-V1.0-java \
android.hardware.automotive.vehicle-V2.0-java \
- vehicle-hal-support-lib
+ vehicle-hal-support-lib \
+ com.android.car.keventreader-client
include packages/services/Car/car-support-lib/car-support.mk
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/input_test.xml b/tests/EmbeddedKitchenSinkApp/res/layout/input_test.xml
index 1d9f317..03a90e0 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/input_test.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/input_test.xml
@@ -24,4 +24,21 @@
android:id="@+id/input_buttons">
<!-- Filled at runtime. -->
</LinearLayout>
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="15dp"
+ android:layout_marginRight="15dp"
+ android:layout_marginTop="20dp"
+ android:fillViewport="true">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="bottom"
+ android:scrollbars="vertical"
+ android:textSize="24px"
+ android:typeface="monospace"
+ android:id="@+id/events_list"/>
+ </ScrollView>
</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/input/InputTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/input/InputTestFragment.java
index ff6ef21..4b9ec1d 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/input/InputTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/input/InputTestFragment.java
@@ -30,6 +30,7 @@
import android.os.Bundle;
import android.os.RemoteException;
import android.support.v4.app.Fragment;
+import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -40,6 +41,12 @@
import android.widget.TextView;
import android.widget.Toast;
+import android.support.v4.app.Fragment;
+
+import com.android.car.keventreader.EventReaderService;
+import com.android.car.keventreader.IEventCallback;
+import com.android.car.keventreader.KeypressEvent;
+
import com.google.android.car.kitchensink.R;
import com.google.android.collect.Lists;
@@ -67,6 +74,25 @@
private IVehicle mVehicle;
+ private EventReaderService mEventReaderService;
+
+ private final IEventCallback.Stub mKeypressEventHandler = new IEventCallback.Stub() {
+ private String prettyPrint(KeypressEvent event) {
+ return String.format("Event{source = %s, keycode = %s, key%s}\n",
+ event.source,
+ event.keycodeToString(),
+ event.isKeydown ? "down" : "up");
+ }
+
+ @Override
+ public void onEvent(KeypressEvent keypressEvent) throws RemoteException {
+ Log.d(TAG, "received event " + keypressEvent);
+ mInputEventsList.append(prettyPrint(keypressEvent));
+ }
+ };
+
+ private TextView mInputEventsList;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -79,6 +105,12 @@
throw new RuntimeException("Failed to connect to IVehicle");
}
Log.d(TAG, "Connected to IVehicle service: " + mVehicle);
+
+ mEventReaderService = EventReaderService.tryGet();
+ Log.d(TAG, "Key Event Reader service: " + mEventReaderService);
+ if (mEventReaderService != null) {
+ mEventReaderService.registerCallback(mKeypressEventHandler);
+ }
}
@Nullable
@@ -87,6 +119,9 @@
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.input_test, container, false);
+ mInputEventsList = view.findViewById(R.id.events_list);
+ mInputEventsList.setMovementMethod(new ScrollingMovementMethod());
+
TextView steeringWheelLabel = new TextView(getActivity() /*context*/);
steeringWheelLabel.setText(R.string.steering_wheel);
steeringWheelLabel.setTextSize(getResources().getDimension(R.dimen.car_title2_size));
@@ -161,6 +196,9 @@
public void onDestroyView() {
super.onDestroyView();
mButtons.clear();
+ if (mEventReaderService != null) {
+ mEventReaderService.unregisterCallback(mKeypressEventHandler);
+ }
}
private void addButtonsToPanel(LinearLayout root, List<View> buttons) {
diff --git a/tools/keventreader/Android.mk b/tools/keventreader/Android.mk
new file mode 100644
index 0000000..1220a1a
--- /dev/null
+++ b/tools/keventreader/Android.mk
@@ -0,0 +1,20 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+
+LOCAL_PATH := $(call my-dir)
+
+# Include the sub-makefiles
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/keventreader/client/Android.mk b/tools/keventreader/client/Android.mk
new file mode 100644
index 0000000..529feea
--- /dev/null
+++ b/tools/keventreader/client/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := com.android.car.keventreader-client
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SRC_FILES += ../common/com/android/car/keventreader/IEventCallback.aidl
+LOCAL_SRC_FILES += ../common/com/android/car/keventreader/IEventProvider.aidl
+
+LOCAL_AIDL_INCLUDES += $(LOCAL_PATH)/../common
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tools/keventreader/client/src/com/android/car/keventreader/EventReaderService.java b/tools/keventreader/client/src/com/android/car/keventreader/EventReaderService.java
new file mode 100644
index 0000000..b50bbb1
--- /dev/null
+++ b/tools/keventreader/client/src/com/android/car/keventreader/EventReaderService.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.keventreader;
+
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import java.util.Objects;
+
+public final class EventReaderService {
+ private static final String TAG = "car.keventreader";
+ private static final String SERVICE_NAME = "com.android.car.keventreader";
+ private final IEventProvider mService;
+
+ private EventReaderService(IEventProvider service) {
+ mService = Objects.requireNonNull(service);
+ }
+
+ @Nullable
+ private static IEventProvider getService() {
+ return IEventProvider.Stub.asInterface(ServiceManager.getService(SERVICE_NAME));
+ }
+
+ @Nullable
+ public static EventReaderService tryGet() {
+ IEventProvider provider = getService();
+ if (provider == null) return null;
+ return new EventReaderService(provider);
+ }
+
+ public boolean registerCallback(IEventCallback callback) {
+ try {
+ mService.registerCallback(callback);
+ return true;
+ } catch (RemoteException e) {
+ Log.e(TAG, "unable to register new callback", e);
+ return false;
+ }
+ }
+
+ public boolean unregisterCallback(IEventCallback callback) {
+ try {
+ mService.unregisterCallback(callback);
+ return true;
+ } catch (RemoteException e) {
+ Log.e(TAG, "unable to remove callback registration", e);
+ return false;
+ }
+ }
+}
diff --git a/tools/keventreader/client/src/com/android/car/keventreader/KeypressEvent.java b/tools/keventreader/client/src/com/android/car/keventreader/KeypressEvent.java
new file mode 100644
index 0000000..34aa992
--- /dev/null
+++ b/tools/keventreader/client/src/com/android/car/keventreader/KeypressEvent.java
@@ -0,0 +1,633 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.keventreader;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import java.util.HashMap;
+import java.util.Map;
+
+public final class KeypressEvent implements Parcelable {
+ private static final Map<Integer, String> KEYCODE_NAME_MAP = new HashMap<Integer, String>() {{
+ put(0,"RESERVED");
+ put(1,"ESC");
+ put(2,"1");
+ put(3,"2");
+ put(4,"3");
+ put(5,"4");
+ put(6,"5");
+ put(7,"6");
+ put(8,"7");
+ put(9,"8");
+ put(10,"9");
+ put(11,"0");
+ put(12,"MINUS");
+ put(13,"EQUAL");
+ put(14,"BACKSPACE");
+ put(15,"TAB");
+ put(16,"Q");
+ put(17,"W");
+ put(18,"E");
+ put(19,"R");
+ put(20,"T");
+ put(21,"Y");
+ put(22,"U");
+ put(23,"I");
+ put(24,"O");
+ put(25,"P");
+ put(26,"LEFTBRACE");
+ put(27,"RIGHTBRACE");
+ put(28,"ENTER");
+ put(29,"LEFTCTRL");
+ put(30,"A");
+ put(31,"S");
+ put(32,"D");
+ put(33,"F");
+ put(34,"G");
+ put(35,"H");
+ put(36,"J");
+ put(37,"K");
+ put(38,"L");
+ put(39,"SEMICOLON");
+ put(40,"APOSTROPHE");
+ put(41,"GRAVE");
+ put(42,"LEFTSHIFT");
+ put(43,"BACKSLASH");
+ put(44,"Z");
+ put(45,"X");
+ put(46,"C");
+ put(47,"V");
+ put(48,"B");
+ put(49,"N");
+ put(50,"M");
+ put(51,"COMMA");
+ put(52,"DOT");
+ put(53,"SLASH");
+ put(54,"RIGHTSHIFT");
+ put(55,"KPASTERISK");
+ put(56,"LEFTALT");
+ put(57,"SPACE");
+ put(58,"CAPSLOCK");
+ put(59,"F1");
+ put(60,"F2");
+ put(61,"F3");
+ put(62,"F4");
+ put(63,"F5");
+ put(64,"F6");
+ put(65,"F7");
+ put(66,"F8");
+ put(67,"F9");
+ put(68,"F10");
+ put(69,"NUMLOCK");
+ put(70,"SCROLLLOCK");
+ put(71,"KP7");
+ put(72,"KP8");
+ put(73,"KP9");
+ put(74,"KPMINUS");
+ put(75,"KP4");
+ put(76,"KP5");
+ put(77,"KP6");
+ put(78,"KPPLUS");
+ put(79,"KP1");
+ put(80,"KP2");
+ put(81,"KP3");
+ put(82,"KP0");
+ put(83,"KPDOT");
+ put(85,"ZENKAKUHANKAKU");
+ put(86,"102ND");
+ put(87,"F11");
+ put(88,"F12");
+ put(89,"RO");
+ put(90,"KATAKANA");
+ put(91,"HIRAGANA");
+ put(92,"HENKAN");
+ put(93,"KATAKANAHIRAGANA");
+ put(94,"MUHENKAN");
+ put(95,"KPJPCOMMA");
+ put(96,"KPENTER");
+ put(97,"RIGHTCTRL");
+ put(98,"KPSLASH");
+ put(99,"SYSRQ");
+ put(100,"RIGHTALT");
+ put(101,"LINEFEED");
+ put(102,"HOME");
+ put(103,"UP");
+ put(104,"PAGEUP");
+ put(105,"LEFT");
+ put(106,"RIGHT");
+ put(107,"END");
+ put(108,"DOWN");
+ put(109,"PAGEDOWN");
+ put(110,"INSERT");
+ put(111,"DELETE");
+ put(112,"MACRO");
+ put(113,"MUTE");
+ put(114,"VOLUMEDOWN");
+ put(115,"VOLUMEUP");
+ put(116,"POWER");
+ put(117,"KPEQUAL");
+ put(118,"KPPLUSMINUS");
+ put(119,"PAUSE");
+ put(120,"SCALE");
+ put(121,"KPCOMMA");
+ put(122,"HANGEUL");
+ put(123,"HANJA");
+ put(124,"YEN");
+ put(125,"LEFTMETA");
+ put(126,"RIGHTMETA");
+ put(127,"COMPOSE");
+ put(128,"STOP");
+ put(129,"AGAIN");
+ put(130,"PROPS");
+ put(131,"UNDO");
+ put(132,"FRONT");
+ put(133,"COPY");
+ put(134,"OPEN");
+ put(135,"PASTE");
+ put(136,"FIND");
+ put(137,"CUT");
+ put(138,"HELP");
+ put(139,"MENU");
+ put(140,"CALC");
+ put(141,"SETUP");
+ put(142,"SLEEP");
+ put(143,"WAKEUP");
+ put(144,"FILE");
+ put(145,"SENDFILE");
+ put(146,"DELETEFILE");
+ put(147,"XFER");
+ put(148,"PROG1");
+ put(149,"PROG2");
+ put(150,"WWW");
+ put(151,"MSDOS");
+ put(152,"SCREENLOCK");
+ put(153,"ROTATE_DISPLAY");
+ put(154,"CYCLEWINDOWS");
+ put(155,"MAIL");
+ put(156,"BOOKMARKS");
+ put(157,"COMPUTER");
+ put(158,"BACK");
+ put(159,"FORWARD");
+ put(160,"CLOSECD");
+ put(161,"EJECTCD");
+ put(162,"EJECTCLOSECD");
+ put(163,"NEXTSONG");
+ put(164,"PLAYPAUSE");
+ put(165,"PREVIOUSSONG");
+ put(166,"STOPCD");
+ put(167,"RECORD");
+ put(168,"REWIND");
+ put(169,"PHONE");
+ put(170,"ISO");
+ put(171,"CONFIG");
+ put(172,"HOMEPAGE");
+ put(173,"REFRESH");
+ put(174,"EXIT");
+ put(175,"MOVE");
+ put(176,"EDIT");
+ put(177,"SCROLLUP");
+ put(178,"SCROLLDOWN");
+ put(179,"KPLEFTPAREN");
+ put(180,"KPRIGHTPAREN");
+ put(181,"NEW");
+ put(182,"REDO");
+ put(183,"F13");
+ put(184,"F14");
+ put(185,"F15");
+ put(186,"F16");
+ put(187,"F17");
+ put(188,"F18");
+ put(189,"F19");
+ put(190,"F20");
+ put(191,"F21");
+ put(192,"F22");
+ put(193,"F23");
+ put(194,"F24");
+ put(200,"PLAYCD");
+ put(201,"PAUSECD");
+ put(202,"PROG3");
+ put(203,"PROG4");
+ put(204,"DASHBOARD");
+ put(205,"SUSPEND");
+ put(206,"CLOSE");
+ put(207,"PLAY");
+ put(208,"FASTFORWARD");
+ put(209,"BASSBOOST");
+ put(210,"PRINT");
+ put(211,"HP");
+ put(212,"CAMERA");
+ put(213,"SOUND");
+ put(214,"QUESTION");
+ put(215,"EMAIL");
+ put(216,"CHAT");
+ put(217,"SEARCH");
+ put(218,"CONNECT");
+ put(219,"FINANCE");
+ put(220,"SPORT");
+ put(221,"SHOP");
+ put(222,"ALTERASE");
+ put(223,"CANCEL");
+ put(224,"BRIGHTNESSDOWN");
+ put(225,"BRIGHTNESSUP");
+ put(226,"MEDIA");
+ put(227,"SWITCHVIDEOMODE");
+ put(228,"KBDILLUMTOGGLE");
+ put(229,"KBDILLUMDOWN");
+ put(230,"KBDILLUMUP");
+ put(231,"SEND");
+ put(232,"REPLY");
+ put(233,"FORWARDMAIL");
+ put(234,"SAVE");
+ put(235,"DOCUMENTS");
+ put(236,"BATTERY");
+ put(237,"BLUETOOTH");
+ put(238,"WLAN");
+ put(239,"UWB");
+ put(240,"UNKNOWN");
+ put(241,"VIDEO_NEXT");
+ put(242,"VIDEO_PREV");
+ put(243,"BRIGHTNESS_CYCLE");
+ put(244,"BRIGHTNESS_AUTO");
+ put(245,"DISPLAY_OFF");
+ put(246,"WWAN");
+ put(247,"RFKILL");
+ put(248,"MICMUTE");
+ put(0x160,"OK");
+ put(0x161,"SELECT");
+ put(0x162,"GOTO");
+ put(0x163,"CLEAR");
+ put(0x164,"POWER2");
+ put(0x165,"OPTION");
+ put(0x166,"INFO");
+ put(0x167,"TIME");
+ put(0x168,"VENDOR");
+ put(0x169,"ARCHIVE");
+ put(0x16a,"PROGRAM");
+ put(0x16b,"CHANNEL");
+ put(0x16c,"FAVORITES");
+ put(0x16d,"EPG");
+ put(0x16e,"PVR");
+ put(0x16f,"MHP");
+ put(0x170,"LANGUAGE");
+ put(0x171,"TITLE");
+ put(0x172,"SUBTITLE");
+ put(0x173,"ANGLE");
+ put(0x174,"ZOOM");
+ put(0x175,"MODE");
+ put(0x176,"KEYBOARD");
+ put(0x177,"SCREEN");
+ put(0x178,"PC");
+ put(0x179,"TV");
+ put(0x17a,"TV2");
+ put(0x17b,"VCR");
+ put(0x17c,"VCR2");
+ put(0x17d,"SAT");
+ put(0x17e,"SAT2");
+ put(0x17f,"CD");
+ put(0x180,"TAPE");
+ put(0x181,"RADIO");
+ put(0x182,"TUNER");
+ put(0x183,"PLAYER");
+ put(0x184,"TEXT");
+ put(0x185,"DVD");
+ put(0x186,"AUX");
+ put(0x187,"MP3");
+ put(0x188,"AUDIO");
+ put(0x189,"VIDEO");
+ put(0x18a,"DIRECTORY");
+ put(0x18b,"LIST");
+ put(0x18c,"MEMO");
+ put(0x18d,"CALENDAR");
+ put(0x18e,"RED");
+ put(0x18f,"GREEN");
+ put(0x190,"YELLOW");
+ put(0x191,"BLUE");
+ put(0x192,"CHANNELUP");
+ put(0x193,"CHANNELDOWN");
+ put(0x194,"FIRST");
+ put(0x195,"LAST");
+ put(0x196,"AB");
+ put(0x197,"NEXT");
+ put(0x198,"RESTART");
+ put(0x199,"SLOW");
+ put(0x19a,"SHUFFLE");
+ put(0x19b,"BREAK");
+ put(0x19c,"PREVIOUS");
+ put(0x19d,"DIGITS");
+ put(0x19e,"TEEN");
+ put(0x19f,"TWEN");
+ put(0x1a0,"VIDEOPHONE");
+ put(0x1a1,"GAMES");
+ put(0x1a2,"ZOOMIN");
+ put(0x1a3,"ZOOMOUT");
+ put(0x1a4,"ZOOMRESET");
+ put(0x1a5,"WORDPROCESSOR");
+ put(0x1a6,"EDITOR");
+ put(0x1a7,"SPREADSHEET");
+ put(0x1a8,"GRAPHICSEDITOR");
+ put(0x1a9,"PRESENTATION");
+ put(0x1aa,"DATABASE");
+ put(0x1ab,"NEWS");
+ put(0x1ac,"VOICEMAIL");
+ put(0x1ad,"ADDRESSBOOK");
+ put(0x1ae,"MESSENGER");
+ put(0x1af,"DISPLAYTOGGLE");
+ put(0x1b0,"SPELLCHECK");
+ put(0x1b1,"LOGOFF");
+ put(0x1b2,"DOLLAR");
+ put(0x1b3,"EURO");
+ put(0x1b4,"FRAMEBACK");
+ put(0x1b5,"FRAMEFORWARD");
+ put(0x1b6,"CONTEXT_MENU");
+ put(0x1b7,"MEDIA_REPEAT");
+ put(0x1b8,"10CHANNELSUP");
+ put(0x1b9,"10CHANNELSDOWN");
+ put(0x1ba,"IMAGES");
+ put(0x1c0,"DEL_EOL");
+ put(0x1c1,"DEL_EOS");
+ put(0x1c2,"INS_LINE");
+ put(0x1c3,"DEL_LINE");
+ put(0x1d0,"FN");
+ put(0x1d1,"FN_ESC");
+ put(0x1d2,"FN_F1");
+ put(0x1d3,"FN_F2");
+ put(0x1d4,"FN_F3");
+ put(0x1d5,"FN_F4");
+ put(0x1d6,"FN_F5");
+ put(0x1d7,"FN_F6");
+ put(0x1d8,"FN_F7");
+ put(0x1d9,"FN_F8");
+ put(0x1da,"FN_F9");
+ put(0x1db,"FN_F10");
+ put(0x1dc,"FN_F11");
+ put(0x1dd,"FN_F12");
+ put(0x1de,"FN_1");
+ put(0x1df,"FN_2");
+ put(0x1e0,"FN_D");
+ put(0x1e1,"FN_E");
+ put(0x1e2,"FN_F");
+ put(0x1e3,"FN_S");
+ put(0x1e4,"FN_B");
+ put(0x1f1,"BRL_DOT1");
+ put(0x1f2,"BRL_DOT2");
+ put(0x1f3,"BRL_DOT3");
+ put(0x1f4,"BRL_DOT4");
+ put(0x1f5,"BRL_DOT5");
+ put(0x1f6,"BRL_DOT6");
+ put(0x1f7,"BRL_DOT7");
+ put(0x1f8,"BRL_DOT8");
+ put(0x1f9,"BRL_DOT9");
+ put(0x1fa,"BRL_DOT10");
+ put(0x200,"NUMERIC_0");
+ put(0x201,"NUMERIC_1");
+ put(0x202,"NUMERIC_2");
+ put(0x203,"NUMERIC_3");
+ put(0x204,"NUMERIC_4");
+ put(0x205,"NUMERIC_5");
+ put(0x206,"NUMERIC_6");
+ put(0x207,"NUMERIC_7");
+ put(0x208,"NUMERIC_8");
+ put(0x209,"NUMERIC_9");
+ put(0x20a,"NUMERIC_STAR");
+ put(0x20b,"NUMERIC_POUND");
+ put(0x20c,"NUMERIC_A");
+ put(0x20d,"NUMERIC_B");
+ put(0x20e,"NUMERIC_C");
+ put(0x20f,"NUMERIC_D");
+ put(0x210,"CAMERA_FOCUS");
+ put(0x211,"WPS_BUTTON");
+ put(0x212,"TOUCHPAD_TOGGLE");
+ put(0x213,"TOUCHPAD_ON");
+ put(0x214,"TOUCHPAD_OFF");
+ put(0x215,"CAMERA_ZOOMIN");
+ put(0x216,"CAMERA_ZOOMOUT");
+ put(0x217,"CAMERA_UP");
+ put(0x218,"CAMERA_DOWN");
+ put(0x219,"CAMERA_LEFT");
+ put(0x21a,"CAMERA_RIGHT");
+ put(0x21b,"ATTENDANT_ON");
+ put(0x21c,"ATTENDANT_OFF");
+ put(0x21d,"ATTENDANT_TOGGLE");
+ put(0x21e,"LIGHTS_TOGGLE");
+ put(0x230,"ALS_TOGGLE");
+ put(0x240,"BUTTONCONFIG");
+ put(0x241,"TASKMANAGER");
+ put(0x242,"JOURNAL");
+ put(0x243,"CONTROLPANEL");
+ put(0x244,"APPSELECT");
+ put(0x245,"SCREENSAVER");
+ put(0x246,"VOICECOMMAND");
+ put(0x247,"ASSISTANT");
+ put(0x250,"BRIGHTNESS_MIN");
+ put(0x251,"BRIGHTNESS_MAX");
+ put(0x260,"KBDINPUTASSIST_PREV");
+ put(0x261,"KBDINPUTASSIST_NEXT");
+ put(0x262,"KBDINPUTASSIST_PREVGROUP");
+ put(0x263,"KBDINPUTASSIST_NEXTGROUP");
+ put(0x264,"KBDINPUTASSIST_ACCEPT");
+ put(0x265,"KBDINPUTASSIST_CANCEL");
+ put(0x266,"RIGHT_UP");
+ put(0x267,"RIGHT_DOWN");
+ put(0x268,"LEFT_UP");
+ put(0x269,"LEFT_DOWN");
+ put(0x26a,"ROOT_MENU");
+ put(0x26b,"MEDIA_TOP_MENU");
+ put(0x26c,"NUMERIC_11");
+ put(0x26d,"NUMERIC_12");
+ put(0x26e,"AUDIO_DESC");
+ put(0x26f,"3D_MODE");
+ put(0x270,"NEXT_FAVORITE");
+ put(0x271,"STOP_RECORD");
+ put(0x272,"PAUSE_RECORD");
+ put(0x273,"VOD");
+ put(0x274,"UNMUTE");
+ put(0x275,"FASTREVERSE");
+ put(0x276,"SLOWREVERSE");
+ put(0x277,"DATA");
+ put(0x278,"ONSCREEN_KEYBOARD");
+ put(113,"MIN_INTERESTING");
+ put(0x2ff,"MAX");
+ put(0x100,"MISC");
+ put(0x100,"0");
+ put(0x101,"1");
+ put(0x102,"2");
+ put(0x103,"3");
+ put(0x104,"4");
+ put(0x105,"5");
+ put(0x106,"6");
+ put(0x107,"7");
+ put(0x108,"8");
+ put(0x109,"9");
+ put(0x110,"MOUSE");
+ put(0x110,"LEFT");
+ put(0x111,"RIGHT");
+ put(0x112,"MIDDLE");
+ put(0x113,"SIDE");
+ put(0x114,"EXTRA");
+ put(0x115,"FORWARD");
+ put(0x116,"BACK");
+ put(0x117,"TASK");
+ put(0x120,"JOYSTICK");
+ put(0x120,"TRIGGER");
+ put(0x121,"THUMB");
+ put(0x122,"THUMB2");
+ put(0x123,"TOP");
+ put(0x124,"TOP2");
+ put(0x125,"PINKIE");
+ put(0x126,"BASE");
+ put(0x127,"BASE2");
+ put(0x128,"BASE3");
+ put(0x129,"BASE4");
+ put(0x12a,"BASE5");
+ put(0x12b,"BASE6");
+ put(0x12f,"DEAD");
+ put(0x130,"GAMEPAD");
+ put(0x130,"SOUTH");
+ put(0x131,"EAST");
+ put(0x132,"C");
+ put(0x133,"NORTH");
+ put(0x134,"WEST");
+ put(0x135,"Z");
+ put(0x136,"TL");
+ put(0x137,"TR");
+ put(0x138,"TL2");
+ put(0x139,"TR2");
+ put(0x13a,"SELECT");
+ put(0x13b,"START");
+ put(0x13c,"MODE");
+ put(0x13d,"THUMBL");
+ put(0x13e,"THUMBR");
+ put(0x140,"DIGI");
+ put(0x140,"TOOL_PEN");
+ put(0x141,"TOOL_RUBBER");
+ put(0x142,"TOOL_BRUSH");
+ put(0x143,"TOOL_PENCIL");
+ put(0x144,"TOOL_AIRBRUSH");
+ put(0x145,"TOOL_FINGER");
+ put(0x146,"TOOL_MOUSE");
+ put(0x147,"TOOL_LENS");
+ put(0x148,"TOOL_QUINTTAP");
+ put(0x149,"STYLUS3");
+ put(0x14a,"TOUCH");
+ put(0x14b,"STYLUS");
+ put(0x14c,"STYLUS2");
+ put(0x14d,"TOOL_DOUBLETAP");
+ put(0x14e,"TOOL_TRIPLETAP");
+ put(0x14f,"TOOL_QUADTAP");
+ put(0x150,"WHEEL");
+ put(0x150,"GEAR_DOWN");
+ put(0x151,"GEAR_UP");
+ put(0x220,"DPAD_UP");
+ put(0x221,"DPAD_DOWN");
+ put(0x222,"DPAD_LEFT");
+ put(0x223,"DPAD_RIGHT");
+ put(0x2c0,"TRIGGER_HAPPY");
+ put(0x2c0,"TRIGGER_HAPPY1");
+ put(0x2c1,"TRIGGER_HAPPY2");
+ put(0x2c2,"TRIGGER_HAPPY3");
+ put(0x2c3,"TRIGGER_HAPPY4");
+ put(0x2c4,"TRIGGER_HAPPY5");
+ put(0x2c5,"TRIGGER_HAPPY6");
+ put(0x2c6,"TRIGGER_HAPPY7");
+ put(0x2c7,"TRIGGER_HAPPY8");
+ put(0x2c8,"TRIGGER_HAPPY9");
+ put(0x2c9,"TRIGGER_HAPPY10");
+ put(0x2ca,"TRIGGER_HAPPY11");
+ put(0x2cb,"TRIGGER_HAPPY12");
+ put(0x2cc,"TRIGGER_HAPPY13");
+ put(0x2cd,"TRIGGER_HAPPY14");
+ put(0x2ce,"TRIGGER_HAPPY15");
+ put(0x2cf,"TRIGGER_HAPPY16");
+ put(0x2d0,"TRIGGER_HAPPY17");
+ put(0x2d1,"TRIGGER_HAPPY18");
+ put(0x2d2,"TRIGGER_HAPPY19");
+ put(0x2d3,"TRIGGER_HAPPY20");
+ put(0x2d4,"TRIGGER_HAPPY21");
+ put(0x2d5,"TRIGGER_HAPPY22");
+ put(0x2d6,"TRIGGER_HAPPY23");
+ put(0x2d7,"TRIGGER_HAPPY24");
+ put(0x2d8,"TRIGGER_HAPPY25");
+ put(0x2d9,"TRIGGER_HAPPY26");
+ put(0x2da,"TRIGGER_HAPPY27");
+ put(0x2db,"TRIGGER_HAPPY28");
+ put(0x2dc,"TRIGGER_HAPPY29");
+ put(0x2dd,"TRIGGER_HAPPY30");
+ put(0x2de,"TRIGGER_HAPPY31");
+ put(0x2df,"TRIGGER_HAPPY32");
+ put(0x2e0,"TRIGGER_HAPPY33");
+ put(0x2e1,"TRIGGER_HAPPY34");
+ put(0x2e2,"TRIGGER_HAPPY35");
+ put(0x2e3,"TRIGGER_HAPPY36");
+ put(0x2e4,"TRIGGER_HAPPY37");
+ put(0x2e5,"TRIGGER_HAPPY38");
+ put(0x2e6,"TRIGGER_HAPPY39");
+ put(0x2e7,"TRIGGER_HAPPY40");
+ }};
+
+ public final String source;
+ public final int keycode;
+ public final boolean isKeydown;
+
+ public static final Parcelable.Creator<KeypressEvent> CREATOR =
+ new Parcelable.Creator<KeypressEvent>() {
+ public KeypressEvent createFromParcel(Parcel in) {
+ return new KeypressEvent(in);
+ }
+
+ public KeypressEvent[] newArray(int size) {
+ return new KeypressEvent[size];
+ }
+ };
+
+ public KeypressEvent(Parcel in) {
+ source = in.readString();
+ keycode = in.readInt();
+ isKeydown = (in.readInt() != 0);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(source);
+ dest.writeInt(keycode);
+ dest.writeInt(isKeydown ? 1 : 0);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof KeypressEvent) {
+ KeypressEvent other = (KeypressEvent)o;
+ return other.source.equals(source) &&
+ other.keycode == keycode &&
+ other.isKeydown == isKeydown;
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return"Event{source = " + source + ", keycode = " + keycode +
+ ", isKeydown = " + isKeydown + "}";
+ }
+
+ public String keycodeToString() {
+ return KEYCODE_NAME_MAP.getOrDefault(keycode, Integer.toHexString(keycode));
+ }
+}
diff --git a/tools/keventreader/common/com/android/car/keventreader/IEventCallback.aidl b/tools/keventreader/common/com/android/car/keventreader/IEventCallback.aidl
new file mode 100644
index 0000000..5df8354
--- /dev/null
+++ b/tools/keventreader/common/com/android/car/keventreader/IEventCallback.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.keventreader;
+
+import com.android.car.keventreader.KeypressEvent;
+
+oneway interface IEventCallback {
+ void onEvent(in KeypressEvent event);
+}
diff --git a/tools/keventreader/common/com/android/car/keventreader/IEventProvider.aidl b/tools/keventreader/common/com/android/car/keventreader/IEventProvider.aidl
new file mode 100644
index 0000000..744d0b2
--- /dev/null
+++ b/tools/keventreader/common/com/android/car/keventreader/IEventProvider.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.keventreader;
+
+import com.android.car.keventreader.IEventCallback;
+
+interface IEventProvider {
+ void registerCallback(in IEventCallback callback);
+ void unregisterCallback(in IEventCallback callback);
+}
diff --git a/tools/keventreader/common/com/android/car/keventreader/KeypressEvent.aidl b/tools/keventreader/common/com/android/car/keventreader/KeypressEvent.aidl
new file mode 100644
index 0000000..197be3a
--- /dev/null
+++ b/tools/keventreader/common/com/android/car/keventreader/KeypressEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.keventreader;
+
+parcelable KeypressEvent cpp_header "event.h";
diff --git a/tools/keventreader/server/Android.mk b/tools/keventreader/server/Android.mk
new file mode 100644
index 0000000..726e2fc
--- /dev/null
+++ b/tools/keventreader/server/Android.mk
@@ -0,0 +1,47 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ main.cpp \
+ eventgatherer.cpp \
+ inputsource.cpp \
+ keymap.cpp \
+ event.cpp \
+ eventprovider.cpp \
+ ../common/com/android/car/keventreader/IEventCallback.aidl \
+ ../common/com/android/car/keventreader/IEventProvider.aidl \
+
+LOCAL_C_INCLUDES += \
+ frameworks/base/include
+
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ liblog \
+ libutils
+
+LOCAL_AIDL_INCLUDES = $(LOCAL_PATH)/../common
+
+LOCAL_MODULE := com.android.car.keventreader
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CPPFLAGS += -std=c++17
+
+include $(BUILD_EXECUTABLE)
diff --git a/tools/keventreader/server/defines.h b/tools/keventreader/server/defines.h
new file mode 100644
index 0000000..59ff2c7
--- /dev/null
+++ b/tools/keventreader/server/defines.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "com.android.car.keventreader"
+#define SERVICE_NAME "com.android.car.keventreader"
diff --git a/tools/keventreader/server/event.cpp b/tools/keventreader/server/event.cpp
new file mode 100644
index 0000000..68ef512
--- /dev/null
+++ b/tools/keventreader/server/event.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "event.h"
+
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+#include <utils/String16.h>
+
+using namespace com::android::car::keventreader;
+
+KeypressEvent::KeypressEvent(const std::string source, uint32_t keycode, bool keydown) {
+ this->source = source;
+ this->keycode = keycode;
+ this->keydown = keydown;
+}
+
+status_t KeypressEvent::writeToParcel(Parcel* parcel) const {
+ String16 s16 = String16(source.c_str());
+ parcel->writeString16(s16);
+ parcel->writeUint32(keycode);
+ parcel->writeBool(keydown);
+
+ return OK;
+}
+
+status_t KeypressEvent::readFromParcel(const Parcel* parcel) {
+ String16 s16 = parcel->readString16();
+ source = std::string(String8(s16).c_str());
+ keycode = parcel->readUint32();
+ keydown = parcel->readBool();
+
+ return OK;
+}
diff --git a/tools/keventreader/server/event.h b/tools/keventreader/server/event.h
new file mode 100644
index 0000000..d4941f1
--- /dev/null
+++ b/tools/keventreader/server/event.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef CAR_KEVENTREADER_EVENT
+#define CAR_KEVENTREADER_EVENT
+
+#include <binder/Parcelable.h>
+#include <string>
+#include <string_view>
+
+using namespace android;
+
+namespace com::android::car::keventreader {
+ struct KeypressEvent : public Parcelable {
+ // required to be callable as KeypressEvent() because Parcelable
+ KeypressEvent(const std::string source = "", uint32_t keycode = 0, bool keydown = false);
+
+ std::string source;
+ uint32_t keycode;
+ bool keydown;
+
+ virtual status_t writeToParcel(Parcel* parcel) const override;
+ virtual status_t readFromParcel(const Parcel* parcel) override;
+ };
+}
+
+#endif //CAR_KEVENTREADER_EVENT
diff --git a/tools/keventreader/server/eventgatherer.cpp b/tools/keventreader/server/eventgatherer.cpp
new file mode 100644
index 0000000..a157d0d
--- /dev/null
+++ b/tools/keventreader/server/eventgatherer.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "eventgatherer.h"
+#include "defines.h"
+#include <utils/Log.h>
+
+using namespace com::android::car::keventreader;
+
+EventGatherer::EventGatherer(int argc, const char** argv) {
+ for (auto i = 1; i < argc; ++i) {
+ auto dev = std::make_unique<InputSource>(argv[i]);
+ if (dev && *dev) {
+ ALOGD("opened input source file %s", argv[i]);
+ mFds.push_back({dev->descriptor(), POLLIN, 0});
+ mDevices.emplace(dev->descriptor(), std::move(dev));
+ } else {
+ ALOGW("failed to open input source file %s", argv[i]);
+ }
+ }
+}
+
+size_t EventGatherer::size() const {
+ return mDevices.size();
+}
+
+std::vector<com::android::car::keventreader::KeypressEvent> EventGatherer::read() {
+ constexpr int FOREVER = -1;
+ std::vector<com::android::car::keventreader::KeypressEvent> result;
+
+ int count = poll(&mFds[0], mFds.size(), FOREVER);
+ if (count < 0) {
+ ALOGE("poll failed: errno = %d", errno);
+ return result;
+ }
+
+ for (auto& fd : mFds) {
+ if (fd.revents != 0) {
+ if (fd.revents == POLLIN) {
+ if (auto dev = mDevices[fd.fd].get()) {
+ while(auto evt = dev->read()) {
+ result.push_back(*evt);
+ }
+ --count; // found one source of events we care abour
+ }
+ }
+ fd.revents = 0;
+ if (count == 0) break; // if we read all events, no point in continuing the scan
+ }
+ }
+
+ return result;
+}
diff --git a/tools/keventreader/server/eventgatherer.h b/tools/keventreader/server/eventgatherer.h
new file mode 100644
index 0000000..71b5f9a
--- /dev/null
+++ b/tools/keventreader/server/eventgatherer.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef CAR_KEVENTREADER_EVENTGATHERER
+#define CAR_KEVENTREADER_EVENTGATHERER
+
+#include "inputsource.h"
+#include "event.h"
+
+#include <map>
+#include <memory>
+#include <poll.h>
+#include <vector>
+
+namespace com::android::car::keventreader {
+ class EventGatherer {
+ public:
+ EventGatherer(int argc, const char** argv);
+
+ size_t size() const;
+
+ std::vector<com::android::car::keventreader::KeypressEvent> read();
+ private:
+ std::map<int, std::unique_ptr<InputSource>> mDevices;
+ std::vector<pollfd> mFds;
+ };
+}
+
+#endif //CAR_KEVENTREADER_EVENTGATHERER
diff --git a/tools/keventreader/server/eventprovider.cpp b/tools/keventreader/server/eventprovider.cpp
new file mode 100644
index 0000000..44ea266
--- /dev/null
+++ b/tools/keventreader/server/eventprovider.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "defines.h"
+#include "eventprovider.h"
+#include <algorithm>
+#include <utils/Log.h>
+
+using namespace com::android::car::keventreader;
+
+EventProviderImpl::EventProviderImpl(EventGatherer&& g) : mGatherer(std::move(g)) {}
+
+std::thread EventProviderImpl::startLoop() {
+ auto t = std::thread( [this] () -> void {
+ while(true) {
+ auto events = mGatherer.read();
+ {
+ std::scoped_lock lock(mMutex);
+ for (auto&& cb : mCallbacks) {
+ for (const auto& event : events) {
+ cb->onEvent(event);
+ }
+ }
+ }
+ }
+ });
+ return t;
+}
+
+Status EventProviderImpl::registerCallback(const sp<IEventCallback>& cb) {
+ std::scoped_lock lock(mMutex);
+ mCallbacks.push_back(cb);
+ return Status::ok();
+}
+
+Status EventProviderImpl::unregisterCallback(const sp<IEventCallback>& cb) {
+ std::scoped_lock lock(mMutex);
+ std::remove(mCallbacks.begin(), mCallbacks.end(), cb);
+ return Status::ok();
+}
diff --git a/tools/keventreader/server/eventprovider.h b/tools/keventreader/server/eventprovider.h
new file mode 100644
index 0000000..17a98b6
--- /dev/null
+++ b/tools/keventreader/server/eventprovider.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef CAR_KEVENTREADER_EVENTPROVIDER
+#define CAR_KEVENTREADER_EVENTPROVIDER
+
+#include "com/android/car/keventreader/IEventCallback.h"
+#include "com/android/car/keventreader/BnEventProvider.h"
+#include <binder/Binder.h>
+#include "eventgatherer.h"
+#include <mutex>
+#include <thread>
+#include <vector>
+
+using namespace android;
+using namespace android::binder;
+
+namespace com::android::car::keventreader {
+ class EventProviderImpl : public BnEventProvider {
+ public:
+ EventProviderImpl(EventGatherer&&);
+ std::thread startLoop();
+
+ virtual Status registerCallback(const sp<IEventCallback>& callback) override;
+ virtual Status unregisterCallback(const sp<IEventCallback>& callback) override;
+ private:
+ EventGatherer mGatherer;
+ std::mutex mMutex;
+ std::vector<sp<IEventCallback>> mCallbacks;
+ };
+}
+
+#endif
diff --git a/tools/keventreader/server/inputsource.cpp b/tools/keventreader/server/inputsource.cpp
new file mode 100644
index 0000000..07b3c90
--- /dev/null
+++ b/tools/keventreader/server/inputsource.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "inputsource.h"
+#include "defines.h"
+#include <utils/Log.h>
+
+#include <fcntl.h>
+#include <sstream>
+#include <string.h>
+#include <unistd.h>
+
+using namespace com::android::car::keventreader;
+
+InputSource::kevent::kevent() {
+ bzero(this, sizeof(*this));
+}
+
+bool InputSource::kevent::isKeypress() const {
+ return type == EV_KEY;
+}
+
+bool InputSource::kevent::isKeydown() const {
+ return isKeypress() && (value == 1);
+}
+
+bool InputSource::kevent::isKeyup() const {
+ return isKeypress() && (value == 0);
+}
+
+InputSource::InputSource(const char* file) : mFilePath(file), mDescriptor(open(file, O_RDONLY)) {}
+
+InputSource::operator bool() const {
+ return descriptor() >= 0;
+}
+
+int InputSource::descriptor() const {
+ return mDescriptor;
+}
+
+std::optional<com::android::car::keventreader::KeypressEvent> InputSource::read() const {
+ kevent evt;
+
+ auto cnt = ::read(mDescriptor, &evt, sizeof(evt));
+
+ // the kernel guarantees that we will always be able to read a whole number of events
+ if (cnt < static_cast<decltype(cnt)>(sizeof(evt))) {
+ return std::nullopt;
+ }
+ if (!evt.isKeypress()) {
+ return std::nullopt;
+ }
+
+ ALOGD("input source %s generated code %u (down = %s)",
+ mFilePath.c_str(), evt.code, evt.isKeydown() ? "true" : "false");
+ return com::android::car::keventreader::KeypressEvent(mFilePath, evt.code, evt.isKeydown());
+}
+
+InputSource::~InputSource() {
+ if (mDescriptor) ::close(mDescriptor);
+}
diff --git a/tools/keventreader/server/inputsource.h b/tools/keventreader/server/inputsource.h
new file mode 100644
index 0000000..282628e
--- /dev/null
+++ b/tools/keventreader/server/inputsource.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef CAR_KEVENTREADER_INPUTSOURCE
+#define CAR_KEVENTREADER_INPUTSOURCE
+
+#include <linux/input.h>
+#include <optional>
+#include <string>
+#include "event.h"
+
+namespace com::android::car::keventreader {
+ class InputSource {
+ public:
+ explicit InputSource(const char* file);
+
+ explicit operator bool() const;
+
+ int descriptor() const;
+
+ std::optional<com::android::car::keventreader::KeypressEvent> read() const;
+
+ virtual ~InputSource();
+ private:
+ struct kevent : public ::input_event {
+ kevent();
+ bool isKeypress() const;
+ bool isKeydown() const;
+ bool isKeyup() const;
+ };
+ static_assert(sizeof(kevent) == sizeof(::input_event), "do not add data to input_event");
+
+ std::string mFilePath;
+ int mDescriptor;
+ };
+}
+
+#endif //CAR_KEVENTREADER_INPUTSOURCE
diff --git a/tools/keventreader/server/keymap.cpp b/tools/keventreader/server/keymap.cpp
new file mode 100644
index 0000000..2e7c06d
--- /dev/null
+++ b/tools/keventreader/server/keymap.cpp
@@ -0,0 +1,595 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "keymap.h"
+#include <sstream>
+
+using namespace com::android::car::keventreader;
+
+Keymap& Keymap::get() {
+ static Keymap gKeymap;
+
+ return gKeymap;
+}
+
+Keymap::Keymap() {
+ fillMap();
+}
+
+std::string_view Keymap::getDisplayName(int keycode) {
+ auto iter = mKeyMap.find(keycode), end = mKeyMap.end();
+ if (iter == end) {
+ std::stringstream ss;
+ ss << "unknown " << keycode;
+ return ss.str();
+ }
+ return iter->second;
+}
+
+void Keymap::fillMap() {
+ mKeyMap.emplace(0,"RESERVED");
+ mKeyMap.emplace(1,"ESC");
+ mKeyMap.emplace(2,"1");
+ mKeyMap.emplace(3,"2");
+ mKeyMap.emplace(4,"3");
+ mKeyMap.emplace(5,"4");
+ mKeyMap.emplace(6,"5");
+ mKeyMap.emplace(7,"6");
+ mKeyMap.emplace(8,"7");
+ mKeyMap.emplace(9,"8");
+ mKeyMap.emplace(10,"9");
+ mKeyMap.emplace(11,"0");
+ mKeyMap.emplace(12,"MINUS");
+ mKeyMap.emplace(13,"EQUAL");
+ mKeyMap.emplace(14,"BACKSPACE");
+ mKeyMap.emplace(15,"TAB");
+ mKeyMap.emplace(16,"Q");
+ mKeyMap.emplace(17,"W");
+ mKeyMap.emplace(18,"E");
+ mKeyMap.emplace(19,"R");
+ mKeyMap.emplace(20,"T");
+ mKeyMap.emplace(21,"Y");
+ mKeyMap.emplace(22,"U");
+ mKeyMap.emplace(23,"I");
+ mKeyMap.emplace(24,"O");
+ mKeyMap.emplace(25,"P");
+ mKeyMap.emplace(26,"LEFTBRACE");
+ mKeyMap.emplace(27,"RIGHTBRACE");
+ mKeyMap.emplace(28,"ENTER");
+ mKeyMap.emplace(29,"LEFTCTRL");
+ mKeyMap.emplace(30,"A");
+ mKeyMap.emplace(31,"S");
+ mKeyMap.emplace(32,"D");
+ mKeyMap.emplace(33,"F");
+ mKeyMap.emplace(34,"G");
+ mKeyMap.emplace(35,"H");
+ mKeyMap.emplace(36,"J");
+ mKeyMap.emplace(37,"K");
+ mKeyMap.emplace(38,"L");
+ mKeyMap.emplace(39,"SEMICOLON");
+ mKeyMap.emplace(40,"APOSTROPHE");
+ mKeyMap.emplace(41,"GRAVE");
+ mKeyMap.emplace(42,"LEFTSHIFT");
+ mKeyMap.emplace(43,"BACKSLASH");
+ mKeyMap.emplace(44,"Z");
+ mKeyMap.emplace(45,"X");
+ mKeyMap.emplace(46,"C");
+ mKeyMap.emplace(47,"V");
+ mKeyMap.emplace(48,"B");
+ mKeyMap.emplace(49,"N");
+ mKeyMap.emplace(50,"M");
+ mKeyMap.emplace(51,"COMMA");
+ mKeyMap.emplace(52,"DOT");
+ mKeyMap.emplace(53,"SLASH");
+ mKeyMap.emplace(54,"RIGHTSHIFT");
+ mKeyMap.emplace(55,"KPASTERISK");
+ mKeyMap.emplace(56,"LEFTALT");
+ mKeyMap.emplace(57,"SPACE");
+ mKeyMap.emplace(58,"CAPSLOCK");
+ mKeyMap.emplace(59,"F1");
+ mKeyMap.emplace(60,"F2");
+ mKeyMap.emplace(61,"F3");
+ mKeyMap.emplace(62,"F4");
+ mKeyMap.emplace(63,"F5");
+ mKeyMap.emplace(64,"F6");
+ mKeyMap.emplace(65,"F7");
+ mKeyMap.emplace(66,"F8");
+ mKeyMap.emplace(67,"F9");
+ mKeyMap.emplace(68,"F10");
+ mKeyMap.emplace(69,"NUMLOCK");
+ mKeyMap.emplace(70,"SCROLLLOCK");
+ mKeyMap.emplace(71,"KP7");
+ mKeyMap.emplace(72,"KP8");
+ mKeyMap.emplace(73,"KP9");
+ mKeyMap.emplace(74,"KPMINUS");
+ mKeyMap.emplace(75,"KP4");
+ mKeyMap.emplace(76,"KP5");
+ mKeyMap.emplace(77,"KP6");
+ mKeyMap.emplace(78,"KPPLUS");
+ mKeyMap.emplace(79,"KP1");
+ mKeyMap.emplace(80,"KP2");
+ mKeyMap.emplace(81,"KP3");
+ mKeyMap.emplace(82,"KP0");
+ mKeyMap.emplace(83,"KPDOT");
+ mKeyMap.emplace(85,"ZENKAKUHANKAKU");
+ mKeyMap.emplace(86,"102ND");
+ mKeyMap.emplace(87,"F11");
+ mKeyMap.emplace(88,"F12");
+ mKeyMap.emplace(89,"RO");
+ mKeyMap.emplace(90,"KATAKANA");
+ mKeyMap.emplace(91,"HIRAGANA");
+ mKeyMap.emplace(92,"HENKAN");
+ mKeyMap.emplace(93,"KATAKANAHIRAGANA");
+ mKeyMap.emplace(94,"MUHENKAN");
+ mKeyMap.emplace(95,"KPJPCOMMA");
+ mKeyMap.emplace(96,"KPENTER");
+ mKeyMap.emplace(97,"RIGHTCTRL");
+ mKeyMap.emplace(98,"KPSLASH");
+ mKeyMap.emplace(99,"SYSRQ");
+ mKeyMap.emplace(100,"RIGHTALT");
+ mKeyMap.emplace(101,"LINEFEED");
+ mKeyMap.emplace(102,"HOME");
+ mKeyMap.emplace(103,"UP");
+ mKeyMap.emplace(104,"PAGEUP");
+ mKeyMap.emplace(105,"LEFT");
+ mKeyMap.emplace(106,"RIGHT");
+ mKeyMap.emplace(107,"END");
+ mKeyMap.emplace(108,"DOWN");
+ mKeyMap.emplace(109,"PAGEDOWN");
+ mKeyMap.emplace(110,"INSERT");
+ mKeyMap.emplace(111,"DELETE");
+ mKeyMap.emplace(112,"MACRO");
+ mKeyMap.emplace(113,"MUTE");
+ mKeyMap.emplace(114,"VOLUMEDOWN");
+ mKeyMap.emplace(115,"VOLUMEUP");
+ mKeyMap.emplace(116,"POWER");
+ mKeyMap.emplace(117,"KPEQUAL");
+ mKeyMap.emplace(118,"KPPLUSMINUS");
+ mKeyMap.emplace(119,"PAUSE");
+ mKeyMap.emplace(120,"SCALE");
+ mKeyMap.emplace(121,"KPCOMMA");
+ mKeyMap.emplace(122,"HANGEUL");
+ mKeyMap.emplace(123,"HANJA");
+ mKeyMap.emplace(124,"YEN");
+ mKeyMap.emplace(125,"LEFTMETA");
+ mKeyMap.emplace(126,"RIGHTMETA");
+ mKeyMap.emplace(127,"COMPOSE");
+ mKeyMap.emplace(128,"STOP");
+ mKeyMap.emplace(129,"AGAIN");
+ mKeyMap.emplace(130,"PROPS");
+ mKeyMap.emplace(131,"UNDO");
+ mKeyMap.emplace(132,"FRONT");
+ mKeyMap.emplace(133,"COPY");
+ mKeyMap.emplace(134,"OPEN");
+ mKeyMap.emplace(135,"PASTE");
+ mKeyMap.emplace(136,"FIND");
+ mKeyMap.emplace(137,"CUT");
+ mKeyMap.emplace(138,"HELP");
+ mKeyMap.emplace(139,"MENU");
+ mKeyMap.emplace(140,"CALC");
+ mKeyMap.emplace(141,"SETUP");
+ mKeyMap.emplace(142,"SLEEP");
+ mKeyMap.emplace(143,"WAKEUP");
+ mKeyMap.emplace(144,"FILE");
+ mKeyMap.emplace(145,"SENDFILE");
+ mKeyMap.emplace(146,"DELETEFILE");
+ mKeyMap.emplace(147,"XFER");
+ mKeyMap.emplace(148,"PROG1");
+ mKeyMap.emplace(149,"PROG2");
+ mKeyMap.emplace(150,"WWW");
+ mKeyMap.emplace(151,"MSDOS");
+ mKeyMap.emplace(152,"SCREENLOCK");
+ mKeyMap.emplace(153,"ROTATE_DISPLAY");
+ mKeyMap.emplace(154,"CYCLEWINDOWS");
+ mKeyMap.emplace(155,"MAIL");
+ mKeyMap.emplace(156,"BOOKMARKS");
+ mKeyMap.emplace(157,"COMPUTER");
+ mKeyMap.emplace(158,"BACK");
+ mKeyMap.emplace(159,"FORWARD");
+ mKeyMap.emplace(160,"CLOSECD");
+ mKeyMap.emplace(161,"EJECTCD");
+ mKeyMap.emplace(162,"EJECTCLOSECD");
+ mKeyMap.emplace(163,"NEXTSONG");
+ mKeyMap.emplace(164,"PLAYPAUSE");
+ mKeyMap.emplace(165,"PREVIOUSSONG");
+ mKeyMap.emplace(166,"STOPCD");
+ mKeyMap.emplace(167,"RECORD");
+ mKeyMap.emplace(168,"REWIND");
+ mKeyMap.emplace(169,"PHONE");
+ mKeyMap.emplace(170,"ISO");
+ mKeyMap.emplace(171,"CONFIG");
+ mKeyMap.emplace(172,"HOMEPAGE");
+ mKeyMap.emplace(173,"REFRESH");
+ mKeyMap.emplace(174,"EXIT");
+ mKeyMap.emplace(175,"MOVE");
+ mKeyMap.emplace(176,"EDIT");
+ mKeyMap.emplace(177,"SCROLLUP");
+ mKeyMap.emplace(178,"SCROLLDOWN");
+ mKeyMap.emplace(179,"KPLEFTPAREN");
+ mKeyMap.emplace(180,"KPRIGHTPAREN");
+ mKeyMap.emplace(181,"NEW");
+ mKeyMap.emplace(182,"REDO");
+ mKeyMap.emplace(183,"F13");
+ mKeyMap.emplace(184,"F14");
+ mKeyMap.emplace(185,"F15");
+ mKeyMap.emplace(186,"F16");
+ mKeyMap.emplace(187,"F17");
+ mKeyMap.emplace(188,"F18");
+ mKeyMap.emplace(189,"F19");
+ mKeyMap.emplace(190,"F20");
+ mKeyMap.emplace(191,"F21");
+ mKeyMap.emplace(192,"F22");
+ mKeyMap.emplace(193,"F23");
+ mKeyMap.emplace(194,"F24");
+ mKeyMap.emplace(200,"PLAYCD");
+ mKeyMap.emplace(201,"PAUSECD");
+ mKeyMap.emplace(202,"PROG3");
+ mKeyMap.emplace(203,"PROG4");
+ mKeyMap.emplace(204,"DASHBOARD");
+ mKeyMap.emplace(205,"SUSPEND");
+ mKeyMap.emplace(206,"CLOSE");
+ mKeyMap.emplace(207,"PLAY");
+ mKeyMap.emplace(208,"FASTFORWARD");
+ mKeyMap.emplace(209,"BASSBOOST");
+ mKeyMap.emplace(210,"PRINT");
+ mKeyMap.emplace(211,"HP");
+ mKeyMap.emplace(212,"CAMERA");
+ mKeyMap.emplace(213,"SOUND");
+ mKeyMap.emplace(214,"QUESTION");
+ mKeyMap.emplace(215,"EMAIL");
+ mKeyMap.emplace(216,"CHAT");
+ mKeyMap.emplace(217,"SEARCH");
+ mKeyMap.emplace(218,"CONNECT");
+ mKeyMap.emplace(219,"FINANCE");
+ mKeyMap.emplace(220,"SPORT");
+ mKeyMap.emplace(221,"SHOP");
+ mKeyMap.emplace(222,"ALTERASE");
+ mKeyMap.emplace(223,"CANCEL");
+ mKeyMap.emplace(224,"BRIGHTNESSDOWN");
+ mKeyMap.emplace(225,"BRIGHTNESSUP");
+ mKeyMap.emplace(226,"MEDIA");
+ mKeyMap.emplace(227,"SWITCHVIDEOMODE");
+ mKeyMap.emplace(228,"KBDILLUMTOGGLE");
+ mKeyMap.emplace(229,"KBDILLUMDOWN");
+ mKeyMap.emplace(230,"KBDILLUMUP");
+ mKeyMap.emplace(231,"SEND");
+ mKeyMap.emplace(232,"REPLY");
+ mKeyMap.emplace(233,"FORWARDMAIL");
+ mKeyMap.emplace(234,"SAVE");
+ mKeyMap.emplace(235,"DOCUMENTS");
+ mKeyMap.emplace(236,"BATTERY");
+ mKeyMap.emplace(237,"BLUETOOTH");
+ mKeyMap.emplace(238,"WLAN");
+ mKeyMap.emplace(239,"UWB");
+ mKeyMap.emplace(240,"UNKNOWN");
+ mKeyMap.emplace(241,"VIDEO_NEXT");
+ mKeyMap.emplace(242,"VIDEO_PREV");
+ mKeyMap.emplace(243,"BRIGHTNESS_CYCLE");
+ mKeyMap.emplace(244,"BRIGHTNESS_AUTO");
+ mKeyMap.emplace(245,"DISPLAY_OFF");
+ mKeyMap.emplace(246,"WWAN");
+ mKeyMap.emplace(247,"RFKILL");
+ mKeyMap.emplace(248,"MICMUTE");
+ mKeyMap.emplace(0x160,"OK");
+ mKeyMap.emplace(0x161,"SELECT");
+ mKeyMap.emplace(0x162,"GOTO");
+ mKeyMap.emplace(0x163,"CLEAR");
+ mKeyMap.emplace(0x164,"POWER2");
+ mKeyMap.emplace(0x165,"OPTION");
+ mKeyMap.emplace(0x166,"INFO");
+ mKeyMap.emplace(0x167,"TIME");
+ mKeyMap.emplace(0x168,"VENDOR");
+ mKeyMap.emplace(0x169,"ARCHIVE");
+ mKeyMap.emplace(0x16a,"PROGRAM");
+ mKeyMap.emplace(0x16b,"CHANNEL");
+ mKeyMap.emplace(0x16c,"FAVORITES");
+ mKeyMap.emplace(0x16d,"EPG");
+ mKeyMap.emplace(0x16e,"PVR");
+ mKeyMap.emplace(0x16f,"MHP");
+ mKeyMap.emplace(0x170,"LANGUAGE");
+ mKeyMap.emplace(0x171,"TITLE");
+ mKeyMap.emplace(0x172,"SUBTITLE");
+ mKeyMap.emplace(0x173,"ANGLE");
+ mKeyMap.emplace(0x174,"ZOOM");
+ mKeyMap.emplace(0x175,"MODE");
+ mKeyMap.emplace(0x176,"KEYBOARD");
+ mKeyMap.emplace(0x177,"SCREEN");
+ mKeyMap.emplace(0x178,"PC");
+ mKeyMap.emplace(0x179,"TV");
+ mKeyMap.emplace(0x17a,"TV2");
+ mKeyMap.emplace(0x17b,"VCR");
+ mKeyMap.emplace(0x17c,"VCR2");
+ mKeyMap.emplace(0x17d,"SAT");
+ mKeyMap.emplace(0x17e,"SAT2");
+ mKeyMap.emplace(0x17f,"CD");
+ mKeyMap.emplace(0x180,"TAPE");
+ mKeyMap.emplace(0x181,"RADIO");
+ mKeyMap.emplace(0x182,"TUNER");
+ mKeyMap.emplace(0x183,"PLAYER");
+ mKeyMap.emplace(0x184,"TEXT");
+ mKeyMap.emplace(0x185,"DVD");
+ mKeyMap.emplace(0x186,"AUX");
+ mKeyMap.emplace(0x187,"MP3");
+ mKeyMap.emplace(0x188,"AUDIO");
+ mKeyMap.emplace(0x189,"VIDEO");
+ mKeyMap.emplace(0x18a,"DIRECTORY");
+ mKeyMap.emplace(0x18b,"LIST");
+ mKeyMap.emplace(0x18c,"MEMO");
+ mKeyMap.emplace(0x18d,"CALENDAR");
+ mKeyMap.emplace(0x18e,"RED");
+ mKeyMap.emplace(0x18f,"GREEN");
+ mKeyMap.emplace(0x190,"YELLOW");
+ mKeyMap.emplace(0x191,"BLUE");
+ mKeyMap.emplace(0x192,"CHANNELUP");
+ mKeyMap.emplace(0x193,"CHANNELDOWN");
+ mKeyMap.emplace(0x194,"FIRST");
+ mKeyMap.emplace(0x195,"LAST");
+ mKeyMap.emplace(0x196,"AB");
+ mKeyMap.emplace(0x197,"NEXT");
+ mKeyMap.emplace(0x198,"RESTART");
+ mKeyMap.emplace(0x199,"SLOW");
+ mKeyMap.emplace(0x19a,"SHUFFLE");
+ mKeyMap.emplace(0x19b,"BREAK");
+ mKeyMap.emplace(0x19c,"PREVIOUS");
+ mKeyMap.emplace(0x19d,"DIGITS");
+ mKeyMap.emplace(0x19e,"TEEN");
+ mKeyMap.emplace(0x19f,"TWEN");
+ mKeyMap.emplace(0x1a0,"VIDEOPHONE");
+ mKeyMap.emplace(0x1a1,"GAMES");
+ mKeyMap.emplace(0x1a2,"ZOOMIN");
+ mKeyMap.emplace(0x1a3,"ZOOMOUT");
+ mKeyMap.emplace(0x1a4,"ZOOMRESET");
+ mKeyMap.emplace(0x1a5,"WORDPROCESSOR");
+ mKeyMap.emplace(0x1a6,"EDITOR");
+ mKeyMap.emplace(0x1a7,"SPREADSHEET");
+ mKeyMap.emplace(0x1a8,"GRAPHICSEDITOR");
+ mKeyMap.emplace(0x1a9,"PRESENTATION");
+ mKeyMap.emplace(0x1aa,"DATABASE");
+ mKeyMap.emplace(0x1ab,"NEWS");
+ mKeyMap.emplace(0x1ac,"VOICEMAIL");
+ mKeyMap.emplace(0x1ad,"ADDRESSBOOK");
+ mKeyMap.emplace(0x1ae,"MESSENGER");
+ mKeyMap.emplace(0x1af,"DISPLAYTOGGLE");
+ mKeyMap.emplace(0x1b0,"SPELLCHECK");
+ mKeyMap.emplace(0x1b1,"LOGOFF");
+ mKeyMap.emplace(0x1b2,"DOLLAR");
+ mKeyMap.emplace(0x1b3,"EURO");
+ mKeyMap.emplace(0x1b4,"FRAMEBACK");
+ mKeyMap.emplace(0x1b5,"FRAMEFORWARD");
+ mKeyMap.emplace(0x1b6,"CONTEXT_MENU");
+ mKeyMap.emplace(0x1b7,"MEDIA_REPEAT");
+ mKeyMap.emplace(0x1b8,"10CHANNELSUP");
+ mKeyMap.emplace(0x1b9,"10CHANNELSDOWN");
+ mKeyMap.emplace(0x1ba,"IMAGES");
+ mKeyMap.emplace(0x1c0,"DEL_EOL");
+ mKeyMap.emplace(0x1c1,"DEL_EOS");
+ mKeyMap.emplace(0x1c2,"INS_LINE");
+ mKeyMap.emplace(0x1c3,"DEL_LINE");
+ mKeyMap.emplace(0x1d0,"FN");
+ mKeyMap.emplace(0x1d1,"FN_ESC");
+ mKeyMap.emplace(0x1d2,"FN_F1");
+ mKeyMap.emplace(0x1d3,"FN_F2");
+ mKeyMap.emplace(0x1d4,"FN_F3");
+ mKeyMap.emplace(0x1d5,"FN_F4");
+ mKeyMap.emplace(0x1d6,"FN_F5");
+ mKeyMap.emplace(0x1d7,"FN_F6");
+ mKeyMap.emplace(0x1d8,"FN_F7");
+ mKeyMap.emplace(0x1d9,"FN_F8");
+ mKeyMap.emplace(0x1da,"FN_F9");
+ mKeyMap.emplace(0x1db,"FN_F10");
+ mKeyMap.emplace(0x1dc,"FN_F11");
+ mKeyMap.emplace(0x1dd,"FN_F12");
+ mKeyMap.emplace(0x1de,"FN_1");
+ mKeyMap.emplace(0x1df,"FN_2");
+ mKeyMap.emplace(0x1e0,"FN_D");
+ mKeyMap.emplace(0x1e1,"FN_E");
+ mKeyMap.emplace(0x1e2,"FN_F");
+ mKeyMap.emplace(0x1e3,"FN_S");
+ mKeyMap.emplace(0x1e4,"FN_B");
+ mKeyMap.emplace(0x1f1,"BRL_DOT1");
+ mKeyMap.emplace(0x1f2,"BRL_DOT2");
+ mKeyMap.emplace(0x1f3,"BRL_DOT3");
+ mKeyMap.emplace(0x1f4,"BRL_DOT4");
+ mKeyMap.emplace(0x1f5,"BRL_DOT5");
+ mKeyMap.emplace(0x1f6,"BRL_DOT6");
+ mKeyMap.emplace(0x1f7,"BRL_DOT7");
+ mKeyMap.emplace(0x1f8,"BRL_DOT8");
+ mKeyMap.emplace(0x1f9,"BRL_DOT9");
+ mKeyMap.emplace(0x1fa,"BRL_DOT10");
+ mKeyMap.emplace(0x200,"NUMERIC_0");
+ mKeyMap.emplace(0x201,"NUMERIC_1");
+ mKeyMap.emplace(0x202,"NUMERIC_2");
+ mKeyMap.emplace(0x203,"NUMERIC_3");
+ mKeyMap.emplace(0x204,"NUMERIC_4");
+ mKeyMap.emplace(0x205,"NUMERIC_5");
+ mKeyMap.emplace(0x206,"NUMERIC_6");
+ mKeyMap.emplace(0x207,"NUMERIC_7");
+ mKeyMap.emplace(0x208,"NUMERIC_8");
+ mKeyMap.emplace(0x209,"NUMERIC_9");
+ mKeyMap.emplace(0x20a,"NUMERIC_STAR");
+ mKeyMap.emplace(0x20b,"NUMERIC_POUND");
+ mKeyMap.emplace(0x20c,"NUMERIC_A");
+ mKeyMap.emplace(0x20d,"NUMERIC_B");
+ mKeyMap.emplace(0x20e,"NUMERIC_C");
+ mKeyMap.emplace(0x20f,"NUMERIC_D");
+ mKeyMap.emplace(0x210,"CAMERA_FOCUS");
+ mKeyMap.emplace(0x211,"WPS_BUTTON");
+ mKeyMap.emplace(0x212,"TOUCHPAD_TOGGLE");
+ mKeyMap.emplace(0x213,"TOUCHPAD_ON");
+ mKeyMap.emplace(0x214,"TOUCHPAD_OFF");
+ mKeyMap.emplace(0x215,"CAMERA_ZOOMIN");
+ mKeyMap.emplace(0x216,"CAMERA_ZOOMOUT");
+ mKeyMap.emplace(0x217,"CAMERA_UP");
+ mKeyMap.emplace(0x218,"CAMERA_DOWN");
+ mKeyMap.emplace(0x219,"CAMERA_LEFT");
+ mKeyMap.emplace(0x21a,"CAMERA_RIGHT");
+ mKeyMap.emplace(0x21b,"ATTENDANT_ON");
+ mKeyMap.emplace(0x21c,"ATTENDANT_OFF");
+ mKeyMap.emplace(0x21d,"ATTENDANT_TOGGLE");
+ mKeyMap.emplace(0x21e,"LIGHTS_TOGGLE");
+ mKeyMap.emplace(0x230,"ALS_TOGGLE");
+ mKeyMap.emplace(0x240,"BUTTONCONFIG");
+ mKeyMap.emplace(0x241,"TASKMANAGER");
+ mKeyMap.emplace(0x242,"JOURNAL");
+ mKeyMap.emplace(0x243,"CONTROLPANEL");
+ mKeyMap.emplace(0x244,"APPSELECT");
+ mKeyMap.emplace(0x245,"SCREENSAVER");
+ mKeyMap.emplace(0x246,"VOICECOMMAND");
+ mKeyMap.emplace(0x247,"ASSISTANT");
+ mKeyMap.emplace(0x250,"BRIGHTNESS_MIN");
+ mKeyMap.emplace(0x251,"BRIGHTNESS_MAX");
+ mKeyMap.emplace(0x260,"KBDINPUTASSIST_PREV");
+ mKeyMap.emplace(0x261,"KBDINPUTASSIST_NEXT");
+ mKeyMap.emplace(0x262,"KBDINPUTASSIST_PREVGROUP");
+ mKeyMap.emplace(0x263,"KBDINPUTASSIST_NEXTGROUP");
+ mKeyMap.emplace(0x264,"KBDINPUTASSIST_ACCEPT");
+ mKeyMap.emplace(0x265,"KBDINPUTASSIST_CANCEL");
+ mKeyMap.emplace(0x266,"RIGHT_UP");
+ mKeyMap.emplace(0x267,"RIGHT_DOWN");
+ mKeyMap.emplace(0x268,"LEFT_UP");
+ mKeyMap.emplace(0x269,"LEFT_DOWN");
+ mKeyMap.emplace(0x26a,"ROOT_MENU");
+ mKeyMap.emplace(0x26b,"MEDIA_TOP_MENU");
+ mKeyMap.emplace(0x26c,"NUMERIC_11");
+ mKeyMap.emplace(0x26d,"NUMERIC_12");
+ mKeyMap.emplace(0x26e,"AUDIO_DESC");
+ mKeyMap.emplace(0x26f,"3D_MODE");
+ mKeyMap.emplace(0x270,"NEXT_FAVORITE");
+ mKeyMap.emplace(0x271,"STOP_RECORD");
+ mKeyMap.emplace(0x272,"PAUSE_RECORD");
+ mKeyMap.emplace(0x273,"VOD");
+ mKeyMap.emplace(0x274,"UNMUTE");
+ mKeyMap.emplace(0x275,"FASTREVERSE");
+ mKeyMap.emplace(0x276,"SLOWREVERSE");
+ mKeyMap.emplace(0x277,"DATA");
+ mKeyMap.emplace(0x278,"ONSCREEN_KEYBOARD");
+ mKeyMap.emplace(113,"MIN_INTERESTING");
+ mKeyMap.emplace(0x2ff,"MAX");
+ mKeyMap.emplace(0x100,"MISC");
+ mKeyMap.emplace(0x100,"0");
+ mKeyMap.emplace(0x101,"1");
+ mKeyMap.emplace(0x102,"2");
+ mKeyMap.emplace(0x103,"3");
+ mKeyMap.emplace(0x104,"4");
+ mKeyMap.emplace(0x105,"5");
+ mKeyMap.emplace(0x106,"6");
+ mKeyMap.emplace(0x107,"7");
+ mKeyMap.emplace(0x108,"8");
+ mKeyMap.emplace(0x109,"9");
+ mKeyMap.emplace(0x110,"MOUSE");
+ mKeyMap.emplace(0x110,"LEFT");
+ mKeyMap.emplace(0x111,"RIGHT");
+ mKeyMap.emplace(0x112,"MIDDLE");
+ mKeyMap.emplace(0x113,"SIDE");
+ mKeyMap.emplace(0x114,"EXTRA");
+ mKeyMap.emplace(0x115,"FORWARD");
+ mKeyMap.emplace(0x116,"BACK");
+ mKeyMap.emplace(0x117,"TASK");
+ mKeyMap.emplace(0x120,"JOYSTICK");
+ mKeyMap.emplace(0x120,"TRIGGER");
+ mKeyMap.emplace(0x121,"THUMB");
+ mKeyMap.emplace(0x122,"THUMB2");
+ mKeyMap.emplace(0x123,"TOP");
+ mKeyMap.emplace(0x124,"TOP2");
+ mKeyMap.emplace(0x125,"PINKIE");
+ mKeyMap.emplace(0x126,"BASE");
+ mKeyMap.emplace(0x127,"BASE2");
+ mKeyMap.emplace(0x128,"BASE3");
+ mKeyMap.emplace(0x129,"BASE4");
+ mKeyMap.emplace(0x12a,"BASE5");
+ mKeyMap.emplace(0x12b,"BASE6");
+ mKeyMap.emplace(0x12f,"DEAD");
+ mKeyMap.emplace(0x130,"GAMEPAD");
+ mKeyMap.emplace(0x130,"SOUTH");
+ mKeyMap.emplace(0x131,"EAST");
+ mKeyMap.emplace(0x132,"C");
+ mKeyMap.emplace(0x133,"NORTH");
+ mKeyMap.emplace(0x134,"WEST");
+ mKeyMap.emplace(0x135,"Z");
+ mKeyMap.emplace(0x136,"TL");
+ mKeyMap.emplace(0x137,"TR");
+ mKeyMap.emplace(0x138,"TL2");
+ mKeyMap.emplace(0x139,"TR2");
+ mKeyMap.emplace(0x13a,"SELECT");
+ mKeyMap.emplace(0x13b,"START");
+ mKeyMap.emplace(0x13c,"MODE");
+ mKeyMap.emplace(0x13d,"THUMBL");
+ mKeyMap.emplace(0x13e,"THUMBR");
+ mKeyMap.emplace(0x140,"DIGI");
+ mKeyMap.emplace(0x140,"TOOL_PEN");
+ mKeyMap.emplace(0x141,"TOOL_RUBBER");
+ mKeyMap.emplace(0x142,"TOOL_BRUSH");
+ mKeyMap.emplace(0x143,"TOOL_PENCIL");
+ mKeyMap.emplace(0x144,"TOOL_AIRBRUSH");
+ mKeyMap.emplace(0x145,"TOOL_FINGER");
+ mKeyMap.emplace(0x146,"TOOL_MOUSE");
+ mKeyMap.emplace(0x147,"TOOL_LENS");
+ mKeyMap.emplace(0x148,"TOOL_QUINTTAP");
+ mKeyMap.emplace(0x149,"STYLUS3");
+ mKeyMap.emplace(0x14a,"TOUCH");
+ mKeyMap.emplace(0x14b,"STYLUS");
+ mKeyMap.emplace(0x14c,"STYLUS2");
+ mKeyMap.emplace(0x14d,"TOOL_DOUBLETAP");
+ mKeyMap.emplace(0x14e,"TOOL_TRIPLETAP");
+ mKeyMap.emplace(0x14f,"TOOL_QUADTAP");
+ mKeyMap.emplace(0x150,"WHEEL");
+ mKeyMap.emplace(0x150,"GEAR_DOWN");
+ mKeyMap.emplace(0x151,"GEAR_UP");
+ mKeyMap.emplace(0x220,"DPAD_UP");
+ mKeyMap.emplace(0x221,"DPAD_DOWN");
+ mKeyMap.emplace(0x222,"DPAD_LEFT");
+ mKeyMap.emplace(0x223,"DPAD_RIGHT");
+ mKeyMap.emplace(0x2c0,"TRIGGER_HAPPY");
+ mKeyMap.emplace(0x2c0,"TRIGGER_HAPPY1");
+ mKeyMap.emplace(0x2c1,"TRIGGER_HAPPY2");
+ mKeyMap.emplace(0x2c2,"TRIGGER_HAPPY3");
+ mKeyMap.emplace(0x2c3,"TRIGGER_HAPPY4");
+ mKeyMap.emplace(0x2c4,"TRIGGER_HAPPY5");
+ mKeyMap.emplace(0x2c5,"TRIGGER_HAPPY6");
+ mKeyMap.emplace(0x2c6,"TRIGGER_HAPPY7");
+ mKeyMap.emplace(0x2c7,"TRIGGER_HAPPY8");
+ mKeyMap.emplace(0x2c8,"TRIGGER_HAPPY9");
+ mKeyMap.emplace(0x2c9,"TRIGGER_HAPPY10");
+ mKeyMap.emplace(0x2ca,"TRIGGER_HAPPY11");
+ mKeyMap.emplace(0x2cb,"TRIGGER_HAPPY12");
+ mKeyMap.emplace(0x2cc,"TRIGGER_HAPPY13");
+ mKeyMap.emplace(0x2cd,"TRIGGER_HAPPY14");
+ mKeyMap.emplace(0x2ce,"TRIGGER_HAPPY15");
+ mKeyMap.emplace(0x2cf,"TRIGGER_HAPPY16");
+ mKeyMap.emplace(0x2d0,"TRIGGER_HAPPY17");
+ mKeyMap.emplace(0x2d1,"TRIGGER_HAPPY18");
+ mKeyMap.emplace(0x2d2,"TRIGGER_HAPPY19");
+ mKeyMap.emplace(0x2d3,"TRIGGER_HAPPY20");
+ mKeyMap.emplace(0x2d4,"TRIGGER_HAPPY21");
+ mKeyMap.emplace(0x2d5,"TRIGGER_HAPPY22");
+ mKeyMap.emplace(0x2d6,"TRIGGER_HAPPY23");
+ mKeyMap.emplace(0x2d7,"TRIGGER_HAPPY24");
+ mKeyMap.emplace(0x2d8,"TRIGGER_HAPPY25");
+ mKeyMap.emplace(0x2d9,"TRIGGER_HAPPY26");
+ mKeyMap.emplace(0x2da,"TRIGGER_HAPPY27");
+ mKeyMap.emplace(0x2db,"TRIGGER_HAPPY28");
+ mKeyMap.emplace(0x2dc,"TRIGGER_HAPPY29");
+ mKeyMap.emplace(0x2dd,"TRIGGER_HAPPY30");
+ mKeyMap.emplace(0x2de,"TRIGGER_HAPPY31");
+ mKeyMap.emplace(0x2df,"TRIGGER_HAPPY32");
+ mKeyMap.emplace(0x2e0,"TRIGGER_HAPPY33");
+ mKeyMap.emplace(0x2e1,"TRIGGER_HAPPY34");
+ mKeyMap.emplace(0x2e2,"TRIGGER_HAPPY35");
+ mKeyMap.emplace(0x2e3,"TRIGGER_HAPPY36");
+ mKeyMap.emplace(0x2e4,"TRIGGER_HAPPY37");
+ mKeyMap.emplace(0x2e5,"TRIGGER_HAPPY38");
+ mKeyMap.emplace(0x2e6,"TRIGGER_HAPPY39");
+ mKeyMap.emplace(0x2e7,"TRIGGER_HAPPY40");
+}
diff --git a/tools/keventreader/server/keymap.h b/tools/keventreader/server/keymap.h
new file mode 100644
index 0000000..28f68d6
--- /dev/null
+++ b/tools/keventreader/server/keymap.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef CAR_KEVENTREADER_KEYMAP
+#define CAR_KEVENTREADER_KEYMAP
+
+#include <string>
+#include <map>
+
+namespace com::android::car::keventreader {
+ // this class provides output that is based on the Linux keycodes table
+ class Keymap {
+ public:
+ static Keymap& get();
+
+ std::string_view getDisplayName(int keycode);
+
+ private:
+ std::map<int, const char*> mKeyMap;
+ void fillMap();
+ Keymap();
+ };
+}
+
+#endif //CAR_KEVENTREADER_KEYMAP
diff --git a/tools/keventreader/server/main.cpp b/tools/keventreader/server/main.cpp
new file mode 100644
index 0000000..fdf9a1c
--- /dev/null
+++ b/tools/keventreader/server/main.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "defines.h"
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <iostream>
+#include <signal.h>
+#include <utils/Log.h>
+#include "eventgatherer.h"
+#include "keymap.h"
+#include "eventprovider.h"
+
+
+using namespace android;
+using namespace com::android::car::keventreader;
+
+/**
+ * This tool expects to be able to read input events in the Linux kernel input_event format
+ * (see linux/input.h); that format is available by means of /dev/input/event* files (which are
+ * mapped 1-to-1 to input sources that provide keypresses as input (e.g. keyboards)
+ * The tool will hook up to each such file passed as input
+ */
+static const char* SYNTAX_INSTRUCTIONS =
+ "invalid command line arguments - provide one or more /dev/input/event files";
+
+static void error(int code) {
+ ALOGE("%s", SYNTAX_INSTRUCTIONS);
+ std::cerr << SYNTAX_INSTRUCTIONS << '\n';
+ exit(code);
+}
+
+int main(int argc, const char** argv) {
+ if (argc < 2 || argv[1] == nullptr || argv[1][0] == 0) {
+ error(1);
+ }
+
+ EventGatherer gatherer(argc, argv);
+ if (0 == gatherer.size()) {
+ error(2);
+ }
+
+ ALOGI("starting " LOG_TAG);
+ signal(SIGPIPE, SIG_IGN);
+
+ sp<ProcessState> processSelf(ProcessState::self());
+ sp<IServiceManager> serviceManager = defaultServiceManager();
+ auto service = std::make_unique<EventProviderImpl>(std::move(gatherer));
+ serviceManager->addService(String16(SERVICE_NAME), service.get());
+
+ processSelf->startThreadPool();
+
+ ALOGI(LOG_TAG " started");
+
+ auto svcthread = service->startLoop();
+ IPCThreadState::self()->joinThreadPool();
+
+ ALOGW(LOG_TAG " joined and going down");
+ exit(0);
+}