Add new native Looper API.

This allows us to avoid exposing the file descriptor of
the event queue; instead, you attach an event queue to
a looper.  This will also should allow native apps to be
written without the need for a separate thread, by attaching
the event queue to the main thread's looper and scheduling
their own messages there.

Change-Id: I38489282635895ae2cbfacb88599c1b1cad9b239
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index 161161c..d72dda7 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -6,7 +6,9 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.graphics.PixelFormat;
+import android.os.Build;
 import android.os.Bundle;
+import android.os.Environment;
 import android.os.Looper;
 import android.os.MessageQueue;
 import android.view.InputChannel;
@@ -33,7 +35,8 @@
     
     private boolean mDestroyed;
     
-    private native int loadNativeCode(String path, MessageQueue queue);
+    private native int loadNativeCode(String path, MessageQueue queue,
+            String internalDataPath, String externalDataPath, int sdkVersion);
     private native void unloadNativeCode(int handle);
     
     private native void onStartNative(int handle);
@@ -90,7 +93,11 @@
             throw new IllegalArgumentException("Unable to find native library: " + libname);
         }
         
-        mNativeHandle = loadNativeCode(path, Looper.myQueue());
+        mNativeHandle = loadNativeCode(path, Looper.myQueue(),
+                 getFilesDir().toString(),
+                 Environment.getExternalStorageAppFilesDirectory(ai.packageName).toString(),
+                 Build.VERSION.SDK_INT);
+        
         if (mNativeHandle == 0) {
             throw new IllegalArgumentException("Unable to load native library: " + path);
         }
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index dab1dba..af61b80 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -166,6 +166,9 @@
     void* dlhandle;
     ANativeActivity_createFunc* createActivityFunc;
     
+    String8 internalDataPath;
+    String8 externalDataPath;
+    
     sp<ANativeWindow> nativeWindow;
     jobject inputChannel;
     struct MyInputQueue* nativeInputQueue;
@@ -204,7 +207,8 @@
 // ------------------------------------------------------------------------
 
 static jint
-loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQueue)
+loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQueue,
+        jstring internalDataDir, jstring externalDataDir, int sdkVersion)
 {
     const char* pathStr = env->GetStringUTFChars(path, NULL);
     NativeCode* code = NULL;
@@ -247,6 +251,19 @@
         }
         code->activity.env = env;
         code->activity.clazz = env->NewGlobalRef(clazz);
+        
+        const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL);
+        code->internalDataPath = dirStr;
+        code->activity.internalDataPath = code->internalDataPath.string();
+        env->ReleaseStringUTFChars(path, dirStr);
+    
+        dirStr = env->GetStringUTFChars(externalDataDir, NULL);
+        code->externalDataPath = dirStr;
+        code->activity.externalDataPath = code->externalDataPath.string();
+        env->ReleaseStringUTFChars(path, dirStr);
+    
+        code->activity.sdkVersion = sdkVersion;
+        
         code->createActivityFunc(&code->activity, NULL, 0);
     }
     
@@ -420,7 +437,8 @@
 }
 
 static const JNINativeMethod g_methods[] = {
-    { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;)I", (void*)loadNativeCode_native },
+    { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;I)I",
+            (void*)loadNativeCode_native },
     { "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native },
     { "onStartNative", "(I)V", (void*)onStart_native },
     { "onResumeNative", "(I)V", (void*)onResume_native },
diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp
index 030d6c7..961f806 100644
--- a/core/jni/android_os_MessageQueue.cpp
+++ b/core/jni/android_os_MessageQueue.cpp
@@ -51,7 +51,11 @@
 // ----------------------------------------------------------------------------
 
 NativeMessageQueue::NativeMessageQueue() {
-    mPollLoop = new PollLoop();
+    mPollLoop = PollLoop::getForThread();
+    if (mPollLoop == NULL) {
+        mPollLoop = new PollLoop();
+        PollLoop::setForThread(mPollLoop);
+    }
 }
 
 NativeMessageQueue::~NativeMessageQueue() {
diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h
index 2dfe2a8..11714d5 100644
--- a/include/ui/InputTransport.h
+++ b/include/ui/InputTransport.h
@@ -33,6 +33,7 @@
 #include <semaphore.h>
 #include <ui/Input.h>
 #include <utils/Errors.h>
+#include <utils/PollLoop.h>
 #include <utils/Timers.h>
 #include <utils/RefBase.h>
 #include <utils/String8.h>
@@ -345,11 +346,15 @@
     
     android::status_t consume(android::InputEvent** event);
     
+    void setPollLoop(const android::sp<android::PollLoop>& pollLoop) { mPollLoop = pollLoop; }
+    const android::sp<android::PollLoop> getPollLoop() const { return mPollLoop; }
+    
     virtual void doDefaultKey(android::KeyEvent* keyEvent) = 0;
     
 private:
     android::InputConsumer mConsumer;
     android::PreallocatedInputEventFactory mInputEventFactory;
+    android::sp<android::PollLoop> mPollLoop;
 };
 
 #endif // _UI_INPUT_TRANSPORT_H
diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h
index a95fb17..b3651ca 100644
--- a/include/utils/PollLoop.h
+++ b/include/utils/PollLoop.h
@@ -22,12 +22,22 @@
 
 #include <sys/poll.h>
 
+#include <android/looper.h>
+
+struct ALooper : public android::RefBase {
+protected:
+    virtual ~ALooper() { }
+
+public:
+    ALooper() { }
+};
+
 namespace android {
 
 /**
  * A basic file descriptor polling loop based on poll() with callbacks.
  */
-class PollLoop : public RefBase {
+class PollLoop : public ALooper {
 protected:
     virtual ~PollLoop();
 
@@ -83,6 +93,11 @@
     void setCallback(int fd, int events, Callback callback, void* data = NULL);
 
     /**
+     * Like setCallback(), but for the NDK callback function.
+     */
+    void setLooperCallback(int fd, int events, ALooper_callbackFunc* callback, void* data);
+    
+    /**
      * Removes the callback for a file descriptor, if one exists.
      *
      * When this method returns, it is safe to close the file descriptor since the poll loop
@@ -100,9 +115,22 @@
      */
     bool removeCallback(int fd);
 
+    /**
+     * Set the given PollLoop to be associated with the
+     * calling thread.  There must be a 1:1 relationship between
+     * PollLoop and thread.
+     */
+    static void setForThread(const sp<PollLoop>& pollLoop);
+    
+    /**
+     * Return the PollLoop associated with the calling thread.
+     */
+    static sp<PollLoop> getForThread();
+    
 private:
     struct RequestedCallback {
         Callback callback;
+        ALooper_callbackFunc* looperCallback;
         void* data;
     };
 
@@ -110,6 +138,7 @@
         int fd;
         int events;
         Callback callback;
+        ALooper_callbackFunc* looperCallback;
         void* data;
     };
 
@@ -130,8 +159,11 @@
     void openWakePipe();
     void closeWakePipe();
 
+    void setCallbackCommon(int fd, int events, Callback callback,
+            ALooper_callbackFunc* looperCallback, void* data);
     ssize_t getRequestIndexLocked(int fd);
     void wakeAndLock();
+    static void threadDestructor(void *st);
 };
 
 } // namespace android
diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp
index 20a4d13..58fe141 100644
--- a/libs/utils/PollLoop.cpp
+++ b/libs/utils/PollLoop.cpp
@@ -21,6 +21,10 @@
 
 namespace android {
 
+static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER;
+static bool gHaveTLS = false;
+static pthread_key_t gTLS = 0;
+
 PollLoop::PollLoop() :
         mPolling(false), mWaiters(0) {
     openWakePipe();
@@ -30,6 +34,41 @@
     closeWakePipe();
 }
 
+void PollLoop::threadDestructor(void *st) {
+    PollLoop* const self = static_cast<PollLoop*>(st);
+    if (self != NULL) {
+        self->decStrong((void*)threadDestructor);
+    }
+}
+
+void PollLoop::setForThread(const sp<PollLoop>& pollLoop) {
+    sp<PollLoop> old = getForThread();
+    
+    if (pollLoop != NULL) {
+        pollLoop->incStrong((void*)threadDestructor);
+    }
+    
+    pthread_setspecific(gTLS, pollLoop.get());
+    
+    if (old != NULL) {
+        old->decStrong((void*)threadDestructor);
+    }
+}
+    
+sp<PollLoop> PollLoop::getForThread() {
+    if (!gHaveTLS) {
+        pthread_mutex_lock(&gTLSMutex);
+        if (pthread_key_create(&gTLS, threadDestructor) != 0) {
+            pthread_mutex_unlock(&gTLSMutex);
+            return NULL;
+        }
+        gHaveTLS = true;
+        pthread_mutex_unlock(&gTLSMutex);
+    }
+    
+    return (PollLoop*)pthread_getspecific(gTLS);
+}
+
 void PollLoop::openWakePipe() {
     int wakeFds[2];
     int result = pipe(wakeFds);
@@ -54,6 +93,7 @@
 
     RequestedCallback requestedCallback;
     requestedCallback.callback = NULL;
+    requestedCallback.looperCallback = NULL;
     requestedCallback.data = NULL;
     mRequestedCallbacks.insertAt(requestedCallback, 0);
 }
@@ -123,12 +163,14 @@
         if (revents) {
             const RequestedCallback& requestedCallback = mRequestedCallbacks.itemAt(i);
             Callback callback = requestedCallback.callback;
+            ALooper_callbackFunc* looperCallback = requestedCallback.looperCallback;
 
-            if (callback) {
+            if (callback || looperCallback) {
                 PendingCallback pendingCallback;
                 pendingCallback.fd = requestedFd.fd;
                 pendingCallback.events = requestedFd.revents;
                 pendingCallback.callback = callback;
+                pendingCallback.looperCallback = looperCallback;
                 pendingCallback.data = requestedCallback.data;
                 mPendingCallbacks.push(pendingCallback);
             } else {
@@ -172,8 +214,14 @@
             LOGD("%p ~ pollOnce - invoking callback for fd %d", this, pendingCallback.fd);
 #endif
 
-            bool keep = pendingCallback.callback(pendingCallback.fd, pendingCallback.events,
-                    pendingCallback.data);
+            bool keep = true;
+            if (pendingCallback.callback != NULL) {
+                keep = pendingCallback.callback(pendingCallback.fd, pendingCallback.events,
+                        pendingCallback.data);
+            } else {
+                keep = pendingCallback.looperCallback(pendingCallback.fd, pendingCallback.events,
+                        pendingCallback.data) != 0;
+            }
             if (! keep) {
                 removeCallback(pendingCallback.fd);
             }
@@ -200,11 +248,22 @@
 }
 
 void PollLoop::setCallback(int fd, int events, Callback callback, void* data) {
+    setCallbackCommon(fd, events, callback, NULL, data);
+}
+
+void PollLoop::setLooperCallback(int fd, int events, ALooper_callbackFunc* callback,
+        void* data) {
+    setCallbackCommon(fd, events, NULL, callback, data);
+}
+
+void PollLoop::setCallbackCommon(int fd, int events, Callback callback,
+        ALooper_callbackFunc* looperCallback, void* data) {
+
 #if DEBUG_CALLBACKS
     LOGD("%p ~ setCallback - fd=%d, events=%d", this, fd, events);
 #endif
 
-    if (! events || ! callback) {
+    if (! events || (! callback && ! looperCallback)) {
         LOGE("Invalid attempt to set a callback with no selected poll events or no callback.");
         removeCallback(fd);
         return;
@@ -218,6 +277,7 @@
 
     RequestedCallback requestedCallback;
     requestedCallback.callback = callback;
+    requestedCallback.looperCallback = looperCallback;
     requestedCallback.data = data;
 
     ssize_t index = getRequestIndexLocked(fd);
diff --git a/native/android/Android.mk b/native/android/Android.mk
index fe8ed00..376c64f 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -8,6 +8,7 @@
 LOCAL_SRC_FILES:= \
     activity.cpp \
     input.cpp \
+    looper.cpp \
     native_window.cpp
 
 LOCAL_SHARED_LIBRARIES := \
diff --git a/native/android/input.cpp b/native/android/input.cpp
index 8498840..015a1ce 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -20,6 +20,7 @@
 #include <android/input.h>
 #include <ui/Input.h>
 #include <ui/InputTransport.h>
+#include <utils/PollLoop.h>
 
 #include <poll.h>
 
@@ -184,8 +185,16 @@
             pointer_index, history_index);
 }
 
-int AInputQueue_getFd(AInputQueue* queue) {
-    return queue->getConsumer().getChannel()->getReceivePipeFd();
+void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper,
+        ALooper_callbackFunc callback, void* data) {
+    queue->setPollLoop(static_cast<android::PollLoop*>(looper));
+    ALooper_setCallback(looper, queue->getConsumer().getChannel()->getReceivePipeFd(),
+            POLLIN, callback, data);
+}
+
+void AInputQueue_detachLooper(AInputQueue* queue) {
+    queue->getPollLoop()->removeCallback(
+            queue->getConsumer().getChannel()->getReceivePipeFd());
 }
 
 int AInputQueue_hasEvents(AInputQueue* queue) {
diff --git a/native/android/looper.cpp b/native/android/looper.cpp
new file mode 100644
index 0000000..6e78bbd
--- /dev/null
+++ b/native/android/looper.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "ALooper"
+#include <utils/Log.h>
+
+#include <android/looper.h>
+#include <utils/PollLoop.h>
+
+using android::PollLoop;
+using android::sp;
+
+ALooper* ALooper_forThread() {
+    return PollLoop::getForThread().get();
+}
+
+ALooper* ALooper_prepare() {
+    sp<PollLoop> loop = PollLoop::getForThread();
+    if (loop == NULL) {
+        loop = new PollLoop();
+        PollLoop::setForThread(loop);
+    }
+    return loop.get();
+}
+
+int32_t ALooper_pollOnce(int timeoutMillis) {
+    sp<PollLoop> loop = PollLoop::getForThread();
+    if (loop == NULL) {
+        LOGW("ALooper_pollOnce: No looper for this thread!");
+        return -1;
+    }
+    return loop->pollOnce(timeoutMillis) ? 1 : 0;
+}
+
+void ALooper_acquire(ALooper* looper) {
+    static_cast<PollLoop*>(looper)->incStrong((void*)ALooper_acquire);
+}
+
+void ALooper_release(ALooper* looper) {
+    static_cast<PollLoop*>(looper)->decStrong((void*)ALooper_acquire);
+}
+
+void ALooper_setCallback(ALooper* looper, int fd, int events,
+        ALooper_callbackFunc* callback, void* data) {
+    static_cast<PollLoop*>(looper)->setLooperCallback(fd, events, callback, data);
+}
+
+int32_t ALooper_removeCallback(ALooper* looper, int fd) {
+    return static_cast<PollLoop*>(looper)->removeCallback(fd) ? 1 : 0;
+}
diff --git a/native/include/android/input.h b/native/include/android/input.h
index 7617662..75be85a 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -42,6 +42,7 @@
 
 #include <sys/types.h>
 #include <android/keycodes.h>
+#include <android/looper.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -533,12 +534,15 @@
 typedef struct AInputQueue AInputQueue;
 
 /*
- * Return a file descriptor for the queue, which you
- * can use to determine if there are events available.  This
- * is typically used with select() or poll() to multiplex
- * with other kinds of events.
+ * Add this input queue to a looper for processing.
  */
-int AInputQueue_getFd(AInputQueue* queue);
+void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper,
+        ALooper_callbackFunc callback, void* data);
+
+/*
+ * Remove the input queue from the looper it is currently attached to.
+ */
+void AInputQueue_detachLooper(AInputQueue* queue);
 
 /*
  * Returns true if there are one or more events available in the
diff --git a/native/include/android/looper.h b/native/include/android/looper.h
new file mode 100644
index 0000000..90a8983
--- /dev/null
+++ b/native/include/android/looper.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+
+#ifndef ANDROID_LOOPER_H
+#define ANDROID_LOOPER_H
+
+#include <poll.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ALooper;
+typedef struct ALooper ALooper;
+
+typedef int ALooper_callbackFunc(int fd, int events, void* data);
+
+ALooper* ALooper_forThread();
+
+ALooper* ALooper_prepare();
+
+int32_t ALooper_pollOnce(int timeoutMillis);
+
+void ALooper_acquire(ALooper* looper);
+
+void ALooper_release(ALooper* looper);
+
+void ALooper_setCallback(ALooper* looper, int fd, int events,
+        ALooper_callbackFunc* callback, void* data);
+
+int32_t ALooper_removeCallback(ALooper* looper, int fd);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_NATIVE_WINDOW_H
diff --git a/native/include/android/native_activity.h b/native/include/android/native_activity.h
index bf5c641..a31c5af 100644
--- a/native/include/android/native_activity.h
+++ b/native/include/android/native_activity.h
@@ -64,6 +64,21 @@
     jobject clazz;
 
     /**
+     * Path to this application's internal data directory.
+     */
+    const char* internalDataPath;
+    
+    /**
+     * Path to this application's external (removable/mountable) data directory.
+     */
+    const char* externalDataPath;
+    
+    /**
+     * The platform's SDK version code.
+     */
+    int32_t sdkVersion;
+    
+    /**
      * This is the native instance of the application.  It is not used by
      * the framework, but can be set by the application to its own instance
      * state.