1/*
2 * Copyright 2006, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15*/
16
17#include <android_runtime/AndroidRuntime.h>
18
19#include <input/KeyCharacterMap.h>
20#include <input/Input.h>
21#include <binder/Parcel.h>
22
23#include <nativehelper/jni.h>
24#include <nativehelper/JNIHelp.h>
25
26#include "android_os_Parcel.h"
27#include "android_view_KeyEvent.h"
28
29namespace android {
30
31static struct {
32    jclass clazz;
33    jmethodID ctor;
34} gKeyCharacterMapClassInfo;
35
36static struct {
37    jclass clazz;
38} gKeyEventClassInfo;
39
40static struct {
41    jfieldID keyCode;
42    jfieldID metaState;
43} gFallbackActionClassInfo;
44
45
46class NativeKeyCharacterMap {
47public:
48    NativeKeyCharacterMap(int32_t deviceId, const sp<KeyCharacterMap>& map) :
49        mDeviceId(deviceId), mMap(map) {
50    }
51
52    ~NativeKeyCharacterMap() {
53    }
54
55    inline int32_t getDeviceId() const {
56        return mDeviceId;
57    }
58
59    inline const sp<KeyCharacterMap>& getMap() const {
60        return mMap;
61    }
62
63private:
64    int32_t mDeviceId;
65    sp<KeyCharacterMap> mMap;
66};
67
68
69jobject android_view_KeyCharacterMap_create(JNIEnv* env, int32_t deviceId,
70        const sp<KeyCharacterMap>& kcm) {
71    NativeKeyCharacterMap* map = new NativeKeyCharacterMap(deviceId,
72            kcm.get() ? kcm : KeyCharacterMap::empty());
73    if (!map) {
74        return NULL;
75    }
76
77    return env->NewObject(gKeyCharacterMapClassInfo.clazz, gKeyCharacterMapClassInfo.ctor,
78            reinterpret_cast<jlong>(map));
79}
80
81static jlong nativeReadFromParcel(JNIEnv *env, jobject clazz, jobject parcelObj) {
82    Parcel* parcel = parcelForJavaObject(env, parcelObj);
83    if (!parcel) {
84        return 0;
85    }
86
87    int32_t deviceId = parcel->readInt32();
88    if (parcel->errorCheck()) {
89        return 0;
90    }
91
92    sp<KeyCharacterMap> kcm = KeyCharacterMap::readFromParcel(parcel);
93    if (!kcm.get()) {
94        return 0;
95    }
96
97    NativeKeyCharacterMap* map = new NativeKeyCharacterMap(deviceId, kcm);
98    return reinterpret_cast<jlong>(map);
99}
100
101static void nativeWriteToParcel(JNIEnv* env, jobject clazz, jlong ptr, jobject parcelObj) {
102    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
103    Parcel* parcel = parcelForJavaObject(env, parcelObj);
104    if (parcel) {
105        parcel->writeInt32(map->getDeviceId());
106        map->getMap()->writeToParcel(parcel);
107    }
108}
109
110static void nativeDispose(JNIEnv *env, jobject clazz, jlong ptr) {
111    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
112    delete map;
113}
114
115static jchar nativeGetCharacter(JNIEnv *env, jobject clazz, jlong ptr,
116        jint keyCode, jint metaState) {
117    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
118    return map->getMap()->getCharacter(keyCode, metaState);
119}
120
121static jboolean nativeGetFallbackAction(JNIEnv *env, jobject clazz, jlong ptr, jint keyCode,
122        jint metaState, jobject fallbackActionObj) {
123    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
124    KeyCharacterMap::FallbackAction fallbackAction;
125
126    bool result = map->getMap()->getFallbackAction(keyCode, metaState, &fallbackAction);
127    if (result) {
128        env->SetIntField(fallbackActionObj, gFallbackActionClassInfo.keyCode,
129                fallbackAction.keyCode);
130        env->SetIntField(fallbackActionObj, gFallbackActionClassInfo.metaState,
131                fallbackAction.metaState);
132    }
133    return result;
134}
135
136static jchar nativeGetNumber(JNIEnv *env, jobject clazz, jlong ptr, jint keyCode) {
137    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
138    return map->getMap()->getNumber(keyCode);
139}
140
141static jchar nativeGetMatch(JNIEnv *env, jobject clazz, jlong ptr, jint keyCode,
142        jcharArray charsArray, jint metaState) {
143    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
144
145    jsize numChars = env->GetArrayLength(charsArray);
146    jchar* chars = static_cast<jchar*>(env->GetPrimitiveArrayCritical(charsArray, NULL));
147    if (!chars) {
148        return 0;
149    }
150
151    char16_t result = map->getMap()->getMatch(keyCode, chars, size_t(numChars), metaState);
152
153    env->ReleasePrimitiveArrayCritical(charsArray, chars, JNI_ABORT);
154    return result;
155}
156
157static jchar nativeGetDisplayLabel(JNIEnv *env, jobject clazz, jlong ptr, jint keyCode) {
158    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
159    return map->getMap()->getDisplayLabel(keyCode);
160}
161
162static jint nativeGetKeyboardType(JNIEnv *env, jobject clazz, jlong ptr) {
163    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
164    return map->getMap()->getKeyboardType();
165}
166
167static jobjectArray nativeGetEvents(JNIEnv *env, jobject clazz, jlong ptr,
168        jcharArray charsArray) {
169    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
170
171    jchar* chars = env->GetCharArrayElements(charsArray, NULL);
172    if (!chars) {
173        return NULL;
174    }
175    jsize numChars = env->GetArrayLength(charsArray);
176
177    Vector<KeyEvent> events;
178    jobjectArray result = NULL;
179    if (map->getMap()->getEvents(map->getDeviceId(), chars, size_t(numChars), events)) {
180        result = env->NewObjectArray(jsize(events.size()), gKeyEventClassInfo.clazz, NULL);
181        if (result) {
182            for (size_t i = 0; i < events.size(); i++) {
183                jobject keyEventObj = android_view_KeyEvent_fromNative(env, &events.itemAt(i));
184                if (!keyEventObj) break; // threw OOM exception
185                env->SetObjectArrayElement(result, jsize(i), keyEventObj);
186                env->DeleteLocalRef(keyEventObj);
187            }
188        }
189    }
190
191    env->ReleaseCharArrayElements(charsArray, chars, JNI_ABORT);
192    return result;
193}
194
195
196/*
197 * JNI registration.
198 */
199
200static JNINativeMethod g_methods[] = {
201    /* name, signature, funcPtr */
202    { "nativeReadFromParcel", "(Landroid/os/Parcel;)J",
203            (void*)nativeReadFromParcel },
204    { "nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
205            (void*)nativeWriteToParcel },
206    { "nativeDispose", "(J)V",
207            (void*)nativeDispose },
208    { "nativeGetCharacter", "(JII)C",
209            (void*)nativeGetCharacter },
210    { "nativeGetFallbackAction", "(JIILandroid/view/KeyCharacterMap$FallbackAction;)Z",
211            (void*)nativeGetFallbackAction },
212    { "nativeGetNumber", "(JI)C",
213            (void*)nativeGetNumber },
214    { "nativeGetMatch", "(JI[CI)C",
215            (void*)nativeGetMatch },
216    { "nativeGetDisplayLabel", "(JI)C",
217            (void*)nativeGetDisplayLabel },
218    { "nativeGetKeyboardType", "(J)I",
219            (void*)nativeGetKeyboardType },
220    { "nativeGetEvents", "(J[C)[Landroid/view/KeyEvent;",
221            (void*)nativeGetEvents },
222};
223
224#define FIND_CLASS(var, className) \
225        var = env->FindClass(className); \
226        LOG_FATAL_IF(! var, "Unable to find class " className);
227
228#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
229        var = env->GetMethodID(clazz, methodName, methodDescriptor); \
230        LOG_FATAL_IF(! var, "Unable to find method " methodName);
231
232#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
233        var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
234        LOG_FATAL_IF(! var, "Unable to find field " fieldName);
235
236int register_android_view_KeyCharacterMap(JNIEnv* env)
237{
238    FIND_CLASS(gKeyCharacterMapClassInfo.clazz, "android/view/KeyCharacterMap");
239    gKeyCharacterMapClassInfo.clazz = jclass(env->NewGlobalRef(gKeyCharacterMapClassInfo.clazz));
240
241    GET_METHOD_ID(gKeyCharacterMapClassInfo.ctor, gKeyCharacterMapClassInfo.clazz,
242            "<init>", "(J)V");
243
244    FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent");
245    gKeyEventClassInfo.clazz = jclass(env->NewGlobalRef(gKeyEventClassInfo.clazz));
246
247    jclass clazz;
248    FIND_CLASS(clazz, "android/view/KeyCharacterMap$FallbackAction");
249
250    GET_FIELD_ID(gFallbackActionClassInfo.keyCode, clazz,
251            "keyCode", "I");
252
253    GET_FIELD_ID(gFallbackActionClassInfo.metaState, clazz,
254            "metaState", "I");
255
256    return AndroidRuntime::registerNativeMethods(env,
257            "android/view/KeyCharacterMap", g_methods, NELEM(g_methods));
258}
259
260}; // namespace android
261