am 3c80a4a0: Implement default key handling for native code.

Merge commit '3c80a4a044865bdf1289c7896baffa1c082d835c' into gingerbread-plus-aosp

* commit '3c80a4a044865bdf1289c7896baffa1c082d835c':
  Implement default key handling for native code.
This commit is contained in:
Dianne Hackborn
2010-06-30 15:32:54 -07:00
committed by Android Git Automerger
9 changed files with 291 additions and 55 deletions

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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);
}

View File

@@ -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));

View File

@@ -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

View File

@@ -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,

View File

@@ -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;

View File

@@ -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,

View File

@@ -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",