summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/Activity.java1
-rw-r--r--core/java/android/app/NativeActivity.java62
-rwxr-xr-xcore/java/android/view/KeyEvent.java36
-rw-r--r--core/jni/android_app_NativeActivity.cpp137
-rw-r--r--core/jni/android_view_KeyEvent.cpp23
-rw-r--r--include/ui/Input.h10
-rw-r--r--include/ui/InputTransport.h4
-rw-r--r--libs/ui/Input.cpp64
-rw-r--r--native/android/input.cpp9
9 files changed, 291 insertions, 55 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 9b9ae52..985f591 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -39,7 +39,6 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.text.Selection;
import android.text.SpannableStringBuilder;
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index 429d164..d43368b 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -6,9 +6,13 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
+import android.os.Looper;
+import android.os.MessageQueue;
import android.view.InputChannel;
import android.view.InputQueue;
+import android.view.KeyEvent;
import android.view.SurfaceHolder;
+import android.view.View;
import java.io.File;
@@ -22,7 +26,12 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback,
private int mNativeHandle;
- private native int loadNativeCode(String path);
+ private InputQueue mCurInputQueue;
+ private SurfaceHolder mCurSurfaceHolder;
+
+ private boolean mDestroyed;
+
+ private native int loadNativeCode(String path, MessageQueue queue);
private native void unloadNativeCode(int handle);
private native void onStartNative(int handle);
@@ -78,7 +87,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback,
throw new IllegalArgumentException("Unable to find native library: " + libname);
}
- mNativeHandle = loadNativeCode(path);
+ mNativeHandle = loadNativeCode(path, Looper.myQueue());
if (mNativeHandle == 0) {
throw new IllegalArgumentException("Unable to load native library: " + path);
}
@@ -87,6 +96,15 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback,
@Override
protected void onDestroy() {
+ mDestroyed = true;
+ if (mCurSurfaceHolder != null) {
+ onSurfaceDestroyedNative(mNativeHandle, mCurSurfaceHolder);
+ mCurSurfaceHolder = null;
+ }
+ if (mCurInputQueue != null) {
+ onInputChannelDestroyedNative(mNativeHandle, mCurInputQueue.getInputChannel());
+ mCurInputQueue = null;
+ }
unloadNativeCode(mNativeHandle);
super.onDestroy();
}
@@ -124,32 +142,58 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback,
@Override
public void onLowMemory() {
super.onLowMemory();
- onLowMemoryNative(mNativeHandle);
+ if (!mDestroyed) {
+ onLowMemoryNative(mNativeHandle);
+ }
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
- onWindowFocusChangedNative(mNativeHandle, hasFocus);
+ if (!mDestroyed) {
+ onWindowFocusChangedNative(mNativeHandle, hasFocus);
+ }
}
public void surfaceCreated(SurfaceHolder holder) {
- onSurfaceCreatedNative(mNativeHandle, holder);
+ if (!mDestroyed) {
+ mCurSurfaceHolder = holder;
+ onSurfaceCreatedNative(mNativeHandle, holder);
+ }
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- onSurfaceChangedNative(mNativeHandle, holder, format, width, height);
+ if (!mDestroyed) {
+ mCurSurfaceHolder = holder;
+ onSurfaceChangedNative(mNativeHandle, holder, format, width, height);
+ }
}
public void surfaceDestroyed(SurfaceHolder holder) {
- onSurfaceDestroyedNative(mNativeHandle, holder);
+ mCurSurfaceHolder = null;
+ if (!mDestroyed) {
+ onSurfaceDestroyedNative(mNativeHandle, holder);
+ }
}
public void onInputQueueCreated(InputQueue queue) {
- onInputChannelCreatedNative(mNativeHandle, queue.getInputChannel());
+ if (!mDestroyed) {
+ mCurInputQueue = queue;
+ onInputChannelCreatedNative(mNativeHandle, queue.getInputChannel());
+ }
}
public void onInputQueueDestroyed(InputQueue queue) {
- onInputChannelDestroyedNative(mNativeHandle, queue.getInputChannel());
+ mCurInputQueue = null;
+ if (!mDestroyed) {
+ onInputChannelDestroyedNative(mNativeHandle, queue.getInputChannel());
+ }
+ }
+
+ void dispatchUnhandledKeyEvent(KeyEvent event) {
+ View decor = getWindow().getDecorView();
+ if (decor != null) {
+ decor.dispatchKeyEvent(event);
+ }
}
}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index ae9746e..0bfb6d6 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -676,33 +676,12 @@ public class KeyEvent implements Parcelable {
* TODO: should the dpad keys be here? arguably, because they also shouldn't be menu shortcuts
*/
public final boolean isSystem() {
- switch (mKeyCode) {
- case KEYCODE_MENU:
- case KEYCODE_SOFT_RIGHT:
- case KEYCODE_HOME:
- case KEYCODE_BACK:
- case KEYCODE_CALL:
- case KEYCODE_ENDCALL:
- case KEYCODE_VOLUME_UP:
- case KEYCODE_VOLUME_DOWN:
- case KEYCODE_MUTE:
- case KEYCODE_POWER:
- case KEYCODE_HEADSETHOOK:
- case KEYCODE_MEDIA_PLAY_PAUSE:
- case KEYCODE_MEDIA_STOP:
- case KEYCODE_MEDIA_NEXT:
- case KEYCODE_MEDIA_PREVIOUS:
- case KEYCODE_MEDIA_REWIND:
- case KEYCODE_MEDIA_FAST_FORWARD:
- case KEYCODE_CAMERA:
- case KEYCODE_FOCUS:
- case KEYCODE_SEARCH:
- case KEYCODE_PICTSYMBOLS:
- case KEYCODE_SWITCH_CHARSET:
- return true;
- default:
- return false;
- }
+ return native_isSystemKey(mKeyCode);
+ }
+
+ /** @hide */
+ public final boolean hasDefaultAction() {
+ return native_hasDefaultAction(mKeyCode);
}
@@ -1226,4 +1205,7 @@ public class KeyEvent implements Parcelable {
mDownTime = in.readLong();
mEventTime = in.readLong();
}
+
+ private native boolean native_isSystemKey(int keyCode);
+ private native boolean native_hasDefaultAction(int keyCode);
}
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index a22b353..dd59d63 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -17,17 +17,63 @@
#define LOG_TAG "NativeActivity"
#include <utils/Log.h>
-#include "JNIHelp.h"
-#include "android_view_InputChannel.h"
+#include <poll.h>
+#include <dlfcn.h>
+
#include <android_runtime/AndroidRuntime.h>
#include <android/native_activity.h>
#include <ui/InputTransport.h>
+#include <utils/PollLoop.h>
-#include <dlfcn.h>
+#include "JNIHelp.h"
+#include "android_os_MessageQueue.h"
+#include "android_view_InputChannel.h"
+#include "android_view_KeyEvent.h"
namespace android
{
+static struct {
+ jclass clazz;
+
+ jmethodID dispatchUnhandledKeyEvent;
+} gNativeActivityClassInfo;
+
+struct MyInputQueue : AInputQueue {
+ explicit MyInputQueue(const android::sp<android::InputChannel>& channel, int workWrite)
+ : AInputQueue(channel), mWorkWrite(workWrite) {
+ }
+
+ virtual void doDefaultKey(android::KeyEvent* keyEvent) {
+ mLock.lock();
+ LOGI("Default key: pending=%d write=%d\n", mPendingKeys.size(), mWorkWrite);
+ if (mPendingKeys.size() <= 0 && mWorkWrite >= 0) {
+ int8_t cmd = 1;
+ write(mWorkWrite, &cmd, sizeof(cmd));
+ }
+ mPendingKeys.add(keyEvent);
+ mLock.unlock();
+ }
+
+ KeyEvent* getNextEvent() {
+ KeyEvent* event = NULL;
+
+ mLock.lock();
+ if (mPendingKeys.size() > 0) {
+ event = mPendingKeys[0];
+ mPendingKeys.removeAt(0);
+ }
+ mLock.unlock();
+
+ return event;
+ }
+
+ int mWorkWrite;
+
+ Mutex mLock;
+ Vector<KeyEvent*> mPendingKeys;
+};
+
struct NativeCode {
NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) {
memset(&activity, sizeof(activity), 0);
@@ -37,14 +83,26 @@ struct NativeCode {
surface = NULL;
inputChannel = NULL;
nativeInputQueue = NULL;
+ mainWorkRead = mainWorkWrite = -1;
}
~NativeCode() {
+ if (activity.env != NULL && activity.clazz != NULL) {
+ activity.env->DeleteGlobalRef(activity.clazz);
+ }
+ if (pollLoop != NULL && mainWorkRead >= 0) {
+ pollLoop->removeCallback(mainWorkRead);
+ }
+ if (nativeInputQueue != NULL) {
+ nativeInputQueue->mWorkWrite = -1;
+ }
setSurface(NULL);
setInputChannel(NULL);
if (callbacks.onDestroy != NULL) {
callbacks.onDestroy(&activity);
}
+ if (mainWorkRead >= 0) close(mainWorkRead);
+ if (mainWorkWrite >= 0) close(mainWorkWrite);
if (dlhandle != NULL) {
dlclose(dlhandle);
}
@@ -73,7 +131,7 @@ struct NativeCode {
sp<InputChannel> ic =
android_view_InputChannel_getInputChannel(activity.env, _channel);
if (ic != NULL) {
- nativeInputQueue = new AInputQueue(ic);
+ nativeInputQueue = new MyInputQueue(ic, mainWorkWrite);
if (nativeInputQueue->getConsumer().initialize() != android::OK) {
delete nativeInputQueue;
nativeInputQueue = NULL;
@@ -94,11 +152,36 @@ struct NativeCode {
jobject surface;
jobject inputChannel;
- struct AInputQueue* nativeInputQueue;
+ struct MyInputQueue* nativeInputQueue;
+
+ // These are used to wake up the main thread to process work.
+ int mainWorkRead;
+ int mainWorkWrite;
+ sp<PollLoop> pollLoop;
};
+static bool mainWorkCallback(int fd, int events, void* data) {
+ NativeCode* code = (NativeCode*)data;
+ if ((events & POLLIN) != 0) {
+ KeyEvent* keyEvent;
+ while ((keyEvent=code->nativeInputQueue->getNextEvent()) != NULL) {
+ jobject inputEventObj = android_view_KeyEvent_fromNative(
+ code->activity.env, keyEvent);
+ code->activity.env->CallVoidMethod(code->activity.clazz,
+ gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
+ int32_t res = code->nativeInputQueue->getConsumer().sendFinishedSignal();
+ if (res != OK) {
+ LOGW("Failed to send finished signal on channel '%s'. status=%d",
+ code->nativeInputQueue->getConsumer().getChannel()->getName().string(), res);
+ }
+ }
+ }
+
+ return true;
+}
+
static jint
-loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path)
+loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQueue)
{
const char* pathStr = env->GetStringUTFChars(path, NULL);
NativeCode* code = NULL;
@@ -115,6 +198,24 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path)
delete code;
return 0;
}
+
+ code->pollLoop = android_os_MessageQueue_getPollLoop(env, messageQueue);
+ if (code->pollLoop == NULL) {
+ LOGW("Unable to retrieve MessageQueue's PollLoop");
+ delete code;
+ return 0;
+ }
+
+ int msgpipe[2];
+ if (pipe(msgpipe)) {
+ LOGW("could not create pipe: %s", strerror(errno));
+ delete code;
+ return 0;
+ }
+ code->mainWorkRead = msgpipe[0];
+ code->mainWorkWrite = msgpipe[1];
+ code->pollLoop->setCallback(code->mainWorkRead, POLLIN, mainWorkCallback, code);
+
code->activity.callbacks = &code->callbacks;
if (env->GetJavaVM(&code->activity.vm) < 0) {
LOGW("NativeActivity GetJavaVM failed");
@@ -122,7 +223,7 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path)
return 0;
}
code->activity.env = env;
- code->activity.clazz = clazz;
+ code->activity.clazz = env->NewGlobalRef(clazz);
code->createActivityFunc(&code->activity, NULL, 0);
}
@@ -288,7 +389,7 @@ onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject
}
static const JNINativeMethod g_methods[] = {
- { "loadNativeCode", "(Ljava/lang/String;)I", (void*)loadNativeCode_native },
+ { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;)I", (void*)loadNativeCode_native },
{ "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native },
{ "onStartNative", "(I)V", (void*)onStart_native },
{ "onResumeNative", "(I)V", (void*)onResume_native },
@@ -306,15 +407,25 @@ static const JNINativeMethod g_methods[] = {
static const char* const kNativeActivityPathName = "android/app/NativeActivity";
+#define FIND_CLASS(var, className) \
+ var = env->FindClass(className); \
+ LOG_FATAL_IF(! var, "Unable to find class " className); \
+ var = jclass(env->NewGlobalRef(var));
+
+#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
+ var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find method" methodName);
+
int register_android_app_NativeActivity(JNIEnv* env)
{
//LOGD("register_android_app_NativeActivity");
- jclass clazz;
-
- clazz = env->FindClass(kNativeActivityPathName);
- LOG_FATAL_IF(clazz == NULL, "Unable to find class android.app.NativeActivity");
-
+ FIND_CLASS(gNativeActivityClassInfo.clazz, kNativeActivityPathName);
+
+ GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent,
+ gNativeActivityClassInfo.clazz,
+ "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)V");
+
return AndroidRuntime::registerNativeMethods(
env, kNativeActivityPathName,
g_methods, NELEM(g_methods));
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
index df3b952..8f648f4 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -76,8 +76,23 @@ void android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj, int32_t natur
milliseconds_to_nanoseconds(eventTime));
}
+static jboolean native_isSystemKey(JNIEnv* env, jobject clazz, jint keyCode) {
+ return KeyEvent::isSystemKey(keyCode);
+}
+
+static jboolean native_hasDefaultAction(JNIEnv* env, jobject clazz, jint keyCode) {
+ return KeyEvent::hasDefaultAction(keyCode);
+}
+
// ----------------------------------------------------------------------------
+static const JNINativeMethod g_methods[] = {
+ { "native_isSystemKey", "(I)Z", (void*)native_isSystemKey },
+ { "native_hasDefaultAction", "(I)Z", (void*)native_hasDefaultAction },
+};
+
+static const char* const kKeyEventPathName = "android/view/KeyEvent";
+
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
LOG_FATAL_IF(! var, "Unable to find class " className); \
@@ -92,8 +107,8 @@ void android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj, int32_t natur
LOG_FATAL_IF(! var, "Unable to find field " fieldName);
int register_android_view_KeyEvent(JNIEnv* env) {
- FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent");
-
+ FIND_CLASS(gKeyEventClassInfo.clazz, kKeyEventPathName);
+
GET_METHOD_ID(gKeyEventClassInfo.ctor, gKeyEventClassInfo.clazz,
"<init>", "(JJIIIIIII)V");
@@ -118,7 +133,9 @@ int register_android_view_KeyEvent(JNIEnv* env) {
GET_FIELD_ID(gKeyEventClassInfo.mCharacters, gKeyEventClassInfo.clazz,
"mCharacters", "Ljava/lang/String;");
- return 0;
+ return AndroidRuntime::registerNativeMethods(
+ env, kKeyEventPathName,
+ g_methods, NELEM(g_methods));
}
} // namespace android
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 8890789..214f587 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -145,7 +145,7 @@ public:
inline int32_t getDeviceId() const { return mDeviceId; }
inline int32_t getNature() const { return mNature; }
-
+
protected:
void initialize(int32_t deviceId, int32_t nature);
@@ -179,6 +179,14 @@ public:
inline nsecs_t getEventTime() const { return mEventTime; }
+ // Return true if this event may have a default action implementation.
+ static bool hasDefaultAction(int32_t keyCode);
+ bool hasDefaultAction() const;
+
+ // Return true if this event represents a system key.
+ static bool isSystemKey(int32_t keyCode);
+ bool isSystemKey() const;
+
void initialize(
int32_t deviceId,
int32_t nature,
diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h
index 4e76051..2dfe2a8 100644
--- a/include/ui/InputTransport.h
+++ b/include/ui/InputTransport.h
@@ -339,12 +339,14 @@ public:
explicit AInputQueue(const android::sp<android::InputChannel>& channel);
/* Destroys the consumer and releases its input channel. */
- ~AInputQueue();
+ virtual ~AInputQueue();
inline android::InputConsumer& getConsumer() { return mConsumer; }
android::status_t consume(android::InputEvent** event);
+ virtual void doDefaultKey(android::KeyEvent* keyEvent) = 0;
+
private:
android::InputConsumer mConsumer;
android::PreallocatedInputEventFactory mInputEventFactory;
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index 4121b5a..a64251f 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -20,6 +20,70 @@ void InputEvent::initialize(int32_t deviceId, int32_t nature) {
// class KeyEvent
+bool KeyEvent::hasDefaultAction(int32_t keyCode) {
+ switch (keyCode) {
+ case KEYCODE_HOME:
+ case KEYCODE_BACK:
+ case KEYCODE_CALL:
+ case KEYCODE_ENDCALL:
+ case KEYCODE_VOLUME_UP:
+ case KEYCODE_VOLUME_DOWN:
+ case KEYCODE_POWER:
+ case KEYCODE_CAMERA:
+ case KEYCODE_HEADSETHOOK:
+ case KEYCODE_MENU:
+ case KEYCODE_NOTIFICATION:
+ case KEYCODE_FOCUS:
+ case KEYCODE_SEARCH:
+ case KEYCODE_MEDIA_PLAY_PAUSE:
+ case KEYCODE_MEDIA_STOP:
+ case KEYCODE_MEDIA_NEXT:
+ case KEYCODE_MEDIA_PREVIOUS:
+ case KEYCODE_MEDIA_REWIND:
+ case KEYCODE_MEDIA_FAST_FORWARD:
+ case KEYCODE_MUTE:
+ return true;
+ }
+
+ return false;
+}
+
+bool KeyEvent::hasDefaultAction() const {
+ return hasDefaultAction(getKeyCode());
+}
+
+bool KeyEvent::isSystemKey(int32_t keyCode) {
+ switch (keyCode) {
+ case KEYCODE_MENU:
+ case KEYCODE_SOFT_RIGHT:
+ case KEYCODE_HOME:
+ case KEYCODE_BACK:
+ case KEYCODE_CALL:
+ case KEYCODE_ENDCALL:
+ case KEYCODE_VOLUME_UP:
+ case KEYCODE_VOLUME_DOWN:
+ case KEYCODE_MUTE:
+ case KEYCODE_POWER:
+ case KEYCODE_HEADSETHOOK:
+ case KEYCODE_MEDIA_PLAY_PAUSE:
+ case KEYCODE_MEDIA_STOP:
+ case KEYCODE_MEDIA_NEXT:
+ case KEYCODE_MEDIA_PREVIOUS:
+ case KEYCODE_MEDIA_REWIND:
+ case KEYCODE_MEDIA_FAST_FORWARD:
+ case KEYCODE_CAMERA:
+ case KEYCODE_FOCUS:
+ case KEYCODE_SEARCH:
+ return true;
+ }
+
+ return false;
+}
+
+bool KeyEvent::isSystemKey() const {
+ return isSystemKey(getKeyCode());
+}
+
void KeyEvent::initialize(
int32_t deviceId,
int32_t nature,
diff --git a/native/android/input.cpp b/native/android/input.cpp
index e0544a1..8498840 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -225,6 +225,15 @@ int32_t AInputQueue_getEvent(AInputQueue* queue, AInputEvent** outEvent) {
void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event,
int handled) {
+ if (!handled && ((InputEvent*)event)->getType() == INPUT_EVENT_TYPE_KEY
+ && ((KeyEvent*)event)->hasDefaultAction()) {
+ // The app didn't handle this, but it may have a default action
+ // associated with it. We need to hand this back to Java to be
+ // executed.
+ queue->doDefaultKey((KeyEvent*)event);
+ return;
+ }
+
int32_t res = queue->getConsumer().sendFinishedSignal();
if (res != android::OK) {
LOGW("Failed to send finished signal on channel '%s'. status=%d",