First stab at attaching native event dispatching.

Provides the basic infrastructure for a
NativeActivity's native code to get an object representing
its event stream that can be used to read input events.

Still work to do, probably some API changes, and reasonable
default key handling (so that for example back will still
work).

Change-Id: I6db891bc35dc9683181d7708eaed552b955a077e
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index fd20b71..973ad60 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -6,6 +6,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
+import android.view.InputChannel;
+import android.view.InputConsumer;
 import android.view.SurfaceHolder;
 
 import java.io.File;
@@ -14,7 +16,8 @@
  * Convenience for implementing an activity that will be implemented
  * purely in native code.  That is, a game (or game-like thing).
  */
-public class NativeActivity extends Activity implements SurfaceHolder.Callback {
+public class NativeActivity extends Activity implements SurfaceHolder.Callback,
+        InputConsumer.Callback {
     public static final String META_DATA_LIB_NAME = "android.app.lib_name";
     
     private int mNativeHandle;
@@ -33,6 +36,8 @@
     private native void onSurfaceChangedNative(int handle, SurfaceHolder holder,
             int format, int width, int height);
     private native void onSurfaceDestroyedNative(int handle, SurfaceHolder holder);
+    private native void onInputChannelCreatedNative(int handle, InputChannel channel);
+    private native void onInputChannelDestroyedNative(int handle, InputChannel channel);
     
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -40,6 +45,7 @@
         ActivityInfo ai;
         
         getWindow().takeSurface(this);
+        getWindow().takeInputChannel(this);
         
         try {
             ai = getPackageManager().getActivityInfo(
@@ -138,4 +144,12 @@
     public void surfaceDestroyed(SurfaceHolder holder) {
         onSurfaceDestroyedNative(mNativeHandle, holder);
     }
+    
+    public void onInputConsumerCreated(InputConsumer consumer) {
+        onInputChannelCreatedNative(mNativeHandle, consumer.getInputChannel());
+    }
+    
+    public void onInputConsumerDestroyed(InputConsumer consumer) {
+        onInputChannelDestroyedNative(mNativeHandle, consumer.getInputChannel());
+    }
 }
diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java
index e5ebc69..e24c3c9 100644
--- a/core/java/android/view/InputChannel.java
+++ b/core/java/android/view/InputChannel.java
@@ -22,8 +22,9 @@
 
 /**
  * An input channel specifies the file descriptors used to send input events to
- * a window in another process.  It is Parcelable so that it can be transmitted
- * to the ViewRoot through a Binder transaction as part of registering the Window.
+ * a window in another process.  It is Parcelable so that it can be sent
+ * to the process that is to receive events.  Only one thread should be reading
+ * from an InputChannel at a time.
  * @hide
  */
 public final class InputChannel implements Parcelable {
diff --git a/core/java/android/view/InputConsumer.java b/core/java/android/view/InputConsumer.java
new file mode 100644
index 0000000..63b26c6
--- /dev/null
+++ b/core/java/android/view/InputConsumer.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2010 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 android.view;
+
+/**
+ * Handle for consuming raw input events.
+ */
+public class InputConsumer {
+    public static interface Callback {
+        void onInputConsumerCreated(InputConsumer consumer);
+        void onInputConsumerDestroyed(InputConsumer consumer);
+    }
+
+    final InputChannel mChannel;
+    
+    /** @hide */
+    public InputConsumer(InputChannel channel) {
+        mChannel = channel;
+    }
+    
+    /** @hide */
+    public InputChannel getInputChannel() {
+        return mChannel;
+    }
+}
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index a41c690d..8984b74 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -154,7 +154,9 @@
 
     final View.AttachInfo mAttachInfo;
     InputChannel mInputChannel;
-
+    InputConsumer.Callback mInputConsumerCallback;
+    InputConsumer mInputConsumer;
+    
     final Rect mTempRect; // used in the transaction to not thrash the heap.
     final Rect mVisRect; // used to retrieve visible rect of focused view.
 
@@ -555,8 +557,17 @@
                 }
 
                 if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
-                    InputQueue.registerInputChannel(mInputChannel, mInputHandler,
-                            Looper.myQueue());
+                    if (view instanceof RootViewSurfaceTaker) {
+                        mInputConsumerCallback =
+                            ((RootViewSurfaceTaker)view).willYouTakeTheInputConsumer();
+                    }
+                    if (mInputConsumerCallback != null) {
+                        mInputConsumer = new InputConsumer(mInputChannel);
+                        mInputConsumerCallback.onInputConsumerCreated(mInputConsumer);
+                    } else {
+                        InputQueue.registerInputChannel(mInputChannel, mInputHandler,
+                                Looper.myQueue());
+                    }
                 }
                 
                 view.assignParent(this);
@@ -1736,7 +1747,12 @@
 
         if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
             if (mInputChannel != null) {
-                InputQueue.unregisterInputChannel(mInputChannel);
+                if (mInputConsumerCallback != null) {
+                    mInputConsumerCallback.onInputConsumerDestroyed(mInputConsumer);
+                    mInputConsumerCallback = null;
+                } else {
+                    InputQueue.unregisterInputChannel(mInputChannel);
+                }
                 mInputChannel.dispose();
                 mInputChannel = null;
             }
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 234deba..b00d33d 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -481,6 +481,13 @@
     public abstract void takeSurface(SurfaceHolder.Callback callback);
     
     /**
+     * Take ownership of this window's InputChannel.  The window will no
+     * longer read and dispatch input events from the channel; it is your
+     * responsibility to do so.
+     */
+    public abstract void takeInputChannel(InputConsumer.Callback callback);
+    
+    /**
      * Return whether this window is being displayed with a floating style
      * (based on the {@link android.R.attr#windowIsFloating} attribute in
      * the style/theme).
diff --git a/core/java/com/android/internal/view/RootViewSurfaceTaker.java b/core/java/com/android/internal/view/RootViewSurfaceTaker.java
index fcb1645..991266a 100644
--- a/core/java/com/android/internal/view/RootViewSurfaceTaker.java
+++ b/core/java/com/android/internal/view/RootViewSurfaceTaker.java
@@ -1,5 +1,6 @@
 package com.android.internal.view;
 
+import android.view.InputConsumer;
 import android.view.SurfaceHolder;
 
 /** hahahah */
@@ -8,4 +9,5 @@
     void setSurfaceType(int type);
     void setSurfaceFormat(int format);
     void setSurfaceKeepScreenOn(boolean keepOn);
+    InputConsumer.Callback willYouTakeTheInputConsumer();
 }
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index f2ab134..5e5e47e 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -18,8 +18,10 @@
 #include <utils/Log.h>
 
 #include "JNIHelp.h"
+#include "android_view_InputChannel.h"
 #include <android_runtime/AndroidRuntime.h>
 #include <android/native_activity.h>
+#include <ui/InputTransport.h>
 
 #include <dlfcn.h>
 
@@ -33,9 +35,13 @@
         dlhandle = _dlhandle;
         createActivityFunc = _createFunc;
         surface = NULL;
+        inputChannel = NULL;
+        nativeInputQueue = NULL;
     }
     
     ~NativeCode() {
+        setSurface(NULL);
+        setInputChannel(NULL);
         if (callbacks.onDestroy != NULL) {
             callbacks.onDestroy(&activity);
         }
@@ -55,6 +61,31 @@
         }
     }
     
+    status_t setInputChannel(jobject _channel) {
+        if (inputChannel != NULL) {
+            delete nativeInputQueue;
+            activity.env->DeleteGlobalRef(inputChannel);
+        }
+        inputChannel = NULL;
+        nativeInputQueue = NULL;
+        if (_channel != NULL) {
+            inputChannel = activity.env->NewGlobalRef(_channel);
+            sp<InputChannel> ic =
+                    android_view_InputChannel_getInputChannel(activity.env, _channel);
+            if (ic != NULL) {
+                nativeInputQueue = new input_queue_t(ic);
+                if (nativeInputQueue->getConsumer().initialize() != android::OK) {
+                    delete nativeInputQueue;
+                    nativeInputQueue = NULL;
+                    return UNKNOWN_ERROR;
+                }
+            } else {
+                return UNKNOWN_ERROR;
+            }
+        }
+        return OK;
+    }
+    
     android_activity_t activity;
     android_activity_callbacks_t callbacks;
     
@@ -62,6 +93,8 @@
     android_activity_create_t* createActivityFunc;
     
     jobject surface;
+    jobject inputChannel;
+    struct input_queue_t* nativeInputQueue;
 };
 
 static jint
@@ -217,6 +250,38 @@
     }
 }
 
+static void
+onInputChannelCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject channel)
+{
+    if (handle != 0) {
+        NativeCode* code = (NativeCode*)handle;
+        status_t err = code->setInputChannel(channel);
+        if (err != OK) {
+            jniThrowException(env, "java/lang/IllegalStateException",
+                    "Error setting input channel");
+            return;
+        }
+        if (code->callbacks.onInputQueueCreated != NULL) {
+            code->callbacks.onInputQueueCreated(&code->activity,
+                    code->nativeInputQueue);
+        }
+    }
+}
+
+static void
+onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject channel)
+{
+    if (handle != 0) {
+        NativeCode* code = (NativeCode*)handle;
+        if (code->nativeInputQueue != NULL
+                && code->callbacks.onInputQueueDestroyed != NULL) {
+            code->callbacks.onInputQueueDestroyed(&code->activity,
+                    code->nativeInputQueue);
+        }
+        code->setInputChannel(NULL);
+    }
+}
+
 static const JNINativeMethod g_methods[] = {
     { "loadNativeCode", "(Ljava/lang/String;)I", (void*)loadNativeCode_native },
     { "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native },
@@ -230,6 +295,8 @@
     { "onSurfaceCreatedNative", "(ILandroid/view/SurfaceHolder;)V", (void*)onSurfaceCreated_native },
     { "onSurfaceChangedNative", "(ILandroid/view/SurfaceHolder;III)V", (void*)onSurfaceChanged_native },
     { "onSurfaceDestroyedNative", "(ILandroid/view/SurfaceHolder;)V", (void*)onSurfaceDestroyed_native },
+    { "onInputChannelCreatedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelCreated_native },
+    { "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native },
 };
 
 static const char* const kNativeActivityPathName = "android/app/NativeActivity";
@@ -248,4 +315,4 @@
         g_methods, NELEM(g_methods));
 }
 
-}
+} // namespace android
diff --git a/core/jni/android_view_InputChannel.h b/core/jni/android_view_InputChannel.h
index ac1defb..fa2d282 100644
--- a/core/jni/android_view_InputChannel.h
+++ b/core/jni/android_view_InputChannel.h
@@ -19,9 +19,9 @@
 
 #include "jni.h"
 
-namespace android {
+#include <ui/InputTransport.h>
 
-class InputChannel;
+namespace android {
 
 typedef void (*InputChannelObjDisposeCallback)(JNIEnv* env, jobject inputChannelObj,
         const sp<InputChannel>& inputChannel, void* data);