EventHub: Add support for excluding devices from being opened by as a keyboard.

This will be used to avoid unnecessarily listening to data from sensors
that function as event devices.

Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index d9c0af2..d62fd7d 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -75,7 +75,11 @@
     
     status_t scancodeToKeycode(int32_t deviceId, int scancode,
             int32_t* outKeycode, uint32_t* outFlags) const;
-    
+
+    // exclude a particular device from opening
+    // this can be used to ignore input devices for sensors
+    void addExcludedDevice(const char* deviceName);
+
     // special type codes when devices are added/removed.
     enum {
         DEVICE_ADDED = 0x10000000,
@@ -88,10 +92,9 @@
     virtual bool getEvent(int32_t* outDeviceId, int32_t* outType,
             int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
             int32_t* outValue, nsecs_t* outWhen);
-    
+
 protected:
     virtual ~EventHub();
-    virtual void onFirstRef();
     
 private:
     bool openPlatformInput(void);
@@ -139,7 +142,10 @@
     device_t        **mDevices;
     struct pollfd   *mFDs;
     int             mFDCount;
-    
+
+    bool            mOpened;
+    List<String8>   mExcludedDevices;
+
     // device ids that report particular switches.
 #ifdef EV_SW
     int32_t         mSwitches[SW_MAX+1];
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index a72f055..402bce2 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -22,7 +22,6 @@
 #include <utils/Log.h>
 #include <utils/Timers.h>
 #include <utils/threads.h>
-#include <utils/List.h>
 #include <utils/Errors.h>
 
 #include <stdlib.h>
@@ -84,7 +83,7 @@
     : mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(0)
     , mDevicesById(0), mNumDevicesById(0)
     , mOpeningDevices(0), mClosingDevices(0)
-    , mDevices(0), mFDs(0), mFDCount(0)
+    , mDevices(0), mFDs(0), mFDCount(0), mOpened(false)
 {
     acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
 #ifdef EV_SW
@@ -101,11 +100,6 @@
     // we should free stuff here...
 }
 
-void EventHub::onFirstRef()
-{
-    mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
-}
-
 status_t EventHub::errorCheck() const
 {
     return mError;
@@ -269,6 +263,12 @@
     return NAME_NOT_FOUND;
 }
 
+void EventHub::addExcludedDevice(const char* deviceName)
+{
+    String8 name(deviceName);
+    mExcludedDevices.push_back(name);
+}
+
 EventHub::device_t* EventHub::getDevice(int32_t deviceId) const
 {
     if (deviceId == 0) deviceId = mFirstKeyboardId;
@@ -306,7 +306,12 @@
 
     // Note that we only allow one caller to getEvent(), so don't need
     // to do locking here...  only when adding/removing devices.
-    
+
+    if (!mOpened) {
+        mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
+        mOpened = true;
+    }
+
     while(1) {
 
         // First, report any devices that had last been added/removed.
@@ -513,6 +518,19 @@
         idstr[0] = '\0';
     }
 
+    // check to see if the device is on our excluded list
+    List<String8>::iterator iter = mExcludedDevices.begin();
+    List<String8>::iterator end = mExcludedDevices.end();
+    for ( ; iter != end; iter++) {
+        const char* name = *iter;
+        if (strcmp(name, idstr) == 0) {
+            LOGD("ignoring event id %s driver %s\n", deviceName, name);
+            close(fd);
+            fd = -1;
+            return -1;
+        }
+    }
+
     int devid = 0;
     while (devid < mNumDevicesById) {
         if (mDevicesById[devid].device == NULL) {
@@ -763,6 +781,7 @@
     int event_pos = 0;
     struct inotify_event *event;
 
+LOGD("EventHub::read_notify nfd: %d\n", nfd);
     res = read(nfd, event_buf, sizeof(event_buf));
     if(res < (int)sizeof(*event)) {
         if(errno == EINTR)
diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java
index fd6b813..77051bd 100644
--- a/services/java/com/android/server/KeyInputQueue.java
+++ b/services/java/com/android/server/KeyInputQueue.java
@@ -18,11 +18,13 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
+import android.os.Environment;
 import android.os.LatencyTimer;
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.Xml;
 import android.view.Display;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -30,9 +32,16 @@
 import android.view.Surface;
 import android.view.WindowManagerPolicy;
 
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.BufferedReader;
+import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
@@ -42,6 +51,8 @@
 
     static final boolean DEBUG_VIRTUAL_KEYS = false;
     
+    private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
+
     final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
     final ArrayList<VirtualKey> mVirtualKeys = new ArrayList<VirtualKey>();
     
@@ -177,12 +188,8 @@
             hitBottom = miny + ((bottom*maxy-miny)/dh);
         }
     }
-    
-    KeyInputQueue(Context context) {
-        if (MEASURE_LATENCY) {
-            lt = new LatencyTimer(100, 1000);
-        }
 
+    private void readVirtualKeys() {
         try {
             FileInputStream fis = new FileInputStream(
                     "/sys/board_properties/virtualkeys.synaptics-rmi-touchscreen");
@@ -223,6 +230,47 @@
         } catch (IOException e) {
             Log.w(TAG, "Error reading virtual keys", e);
         }
+    }
+
+    private void readExcludedDevices() {
+        // Read partner-provided list of excluded input devices
+        XmlPullParser parser = null;
+        // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
+        File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
+        FileReader confreader = null;
+        try {
+            confreader = new FileReader(confFile);
+            parser = Xml.newPullParser();
+            parser.setInput(confreader);
+            XmlUtils.beginDocument(parser, "devices");
+
+            while (true) {
+                XmlUtils.nextElement(parser);
+                if (!"device".equals(parser.getName())) {
+                    break;
+                }
+                String name = parser.getAttributeValue(null, "name");
+                if (name != null) {
+                    Log.d(TAG, "addExcludedDevice " + name);
+                    addExcludedDevice(name);
+                }
+            }
+        } catch (FileNotFoundException e) {
+            // It's ok if the file does not exist.
+        } catch (Exception e) {
+            Log.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
+        } finally {
+            try { if (confreader != null) confreader.close(); } catch (IOException e) { }
+        }
+    }
+
+    KeyInputQueue(Context context) {
+        if (MEASURE_LATENCY) {
+            lt = new LatencyTimer(100, 1000);
+        }
+
+        readVirtualKeys();
+        readExcludedDevices();
         
         PowerManager pm = (PowerManager)context.getSystemService(
                                                         Context.POWER_SERVICE);
@@ -280,6 +328,7 @@
     
     public static native String getDeviceName(int deviceId);
     public static native int getDeviceClasses(int deviceId);
+    public static native void addExcludedDevice(String deviceName);
     public static native boolean getAbsoluteInfo(int deviceId, int axis,
             InputDevice.AbsoluteInfo outInfo);
     public static native int getSwitchState(int sw);
@@ -305,6 +354,7 @@
     
     Thread mThread = new Thread("InputDeviceReader") {
         public void run() {
+            Log.d(TAG, "InputDeviceReader.run()");
             android.os.Process.setThreadPriority(
                     android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
             
diff --git a/services/jni/com_android_server_KeyInputQueue.cpp b/services/jni/com_android_server_KeyInputQueue.cpp
index afca1f7..f27596c 100644
--- a/services/jni/com_android_server_KeyInputQueue.cpp
+++ b/services/jni/com_android_server_KeyInputQueue.cpp
@@ -110,6 +110,23 @@
     return NULL;
 }
 
+static void
+android_server_KeyInputQueue_addExcludedDevice(JNIEnv* env, jobject clazz,
+                                              jstring deviceName)
+{
+    gLock.lock();
+    sp<EventHub> hub = gHub;
+    if (hub == NULL) {
+        hub = new EventHub;
+        gHub = hub;
+    }
+    gLock.unlock();
+
+    const char* nameStr = env->GetStringUTFChars(deviceName, NULL);
+    gHub->addExcludedDevice(nameStr);
+    env->ReleaseStringUTFChars(deviceName, nameStr);
+}
+
 static jboolean
 android_server_KeyInputQueue_getAbsoluteInfo(JNIEnv* env, jobject clazz,
                                              jint deviceId, jint axis,
@@ -255,6 +272,8 @@
         (void*) android_server_KeyInputQueue_getDeviceClasses },
     { "getDeviceName", "(I)Ljava/lang/String;",
         (void*) android_server_KeyInputQueue_getDeviceName },
+    { "addExcludedDevice", "(Ljava/lang/String;)V",
+        (void*) android_server_KeyInputQueue_addExcludedDevice },
     { "getAbsoluteInfo", "(IILcom/android/server/InputDevice$AbsoluteInfo;)Z",
         (void*) android_server_KeyInputQueue_getAbsoluteInfo },
     { "getSwitchState", "(I)I",