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
29#include "core_jni_helpers.h"
30
31namespace android {
32
33static struct {
34    jclass clazz;
35    jmethodID ctor;
36} gKeyCharacterMapClassInfo;
37
38static struct {
39    jclass clazz;
40} gKeyEventClassInfo;
41
42static struct {
43    jfieldID keyCode;
44    jfieldID metaState;
45} gFallbackActionClassInfo;
46
47
48class NativeKeyCharacterMap {
49public:
50    NativeKeyCharacterMap(int32_t deviceId, const sp<KeyCharacterMap>& map) :
51        mDeviceId(deviceId), mMap(map) {
52    }
53
54    ~NativeKeyCharacterMap() {
55    }
56
57    inline int32_t getDeviceId() const {
58        return mDeviceId;
59    }
60
61    inline const sp<KeyCharacterMap>& getMap() const {
62        return mMap;
63    }
64
65private:
66    int32_t mDeviceId;
67    sp<KeyCharacterMap> mMap;
68};
69
70
71jobject android_view_KeyCharacterMap_create(JNIEnv* env, int32_t deviceId,
72        const sp<KeyCharacterMap>& kcm) {
73    NativeKeyCharacterMap* map = new NativeKeyCharacterMap(deviceId,
74            kcm.get() ? kcm : KeyCharacterMap::empty());
75    if (!map) {
76        return NULL;
77    }
78
79    return env->NewObject(gKeyCharacterMapClassInfo.clazz, gKeyCharacterMapClassInfo.ctor,
80            reinterpret_cast<jlong>(map));
81}
82
83static jlong nativeReadFromParcel(JNIEnv *env, jobject clazz, jobject parcelObj) {
84    Parcel* parcel = parcelForJavaObject(env, parcelObj);
85    if (!parcel) {
86        return 0;
87    }
88
89    int32_t deviceId = parcel->readInt32();
90    if (parcel->errorCheck()) {
91        return 0;
92    }
93
94    sp<KeyCharacterMap> kcm = KeyCharacterMap::readFromParcel(parcel);
95    if (!kcm.get()) {
96        return 0;
97    }
98
99    NativeKeyCharacterMap* map = new NativeKeyCharacterMap(deviceId, kcm);
100    return reinterpret_cast<jlong>(map);
101}
102
103static void nativeWriteToParcel(JNIEnv* env, jobject clazz, jlong ptr, jobject parcelObj) {
104    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
105    Parcel* parcel = parcelForJavaObject(env, parcelObj);
106    if (parcel) {
107        parcel->writeInt32(map->getDeviceId());
108        map->getMap()->writeToParcel(parcel);
109    }
110}
111
112static void nativeDispose(JNIEnv *env, jobject clazz, jlong ptr) {
113    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
114    delete map;
115}
116
117static jchar nativeGetCharacter(JNIEnv *env, jobject clazz, jlong ptr,
118        jint keyCode, jint metaState) {
119    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
120    return map->getMap()->getCharacter(keyCode, metaState);
121}
122
123static jboolean nativeGetFallbackAction(JNIEnv *env, jobject clazz, jlong ptr, jint keyCode,
124        jint metaState, jobject fallbackActionObj) {
125    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
126    KeyCharacterMap::FallbackAction fallbackAction;
127
128    bool result = map->getMap()->getFallbackAction(keyCode, metaState, &fallbackAction);
129    if (result) {
130        env->SetIntField(fallbackActionObj, gFallbackActionClassInfo.keyCode,
131                fallbackAction.keyCode);
132        env->SetIntField(fallbackActionObj, gFallbackActionClassInfo.metaState,
133                fallbackAction.metaState);
134    }
135    return result;
136}
137
138static jchar nativeGetNumber(JNIEnv *env, jobject clazz, jlong ptr, jint keyCode) {
139    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
140    return map->getMap()->getNumber(keyCode);
141}
142
143static jchar nativeGetMatch(JNIEnv *env, jobject clazz, jlong ptr, jint keyCode,
144        jcharArray charsArray, jint metaState) {
145    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
146
147    jsize numChars = env->GetArrayLength(charsArray);
148    jchar* chars = static_cast<jchar*>(env->GetPrimitiveArrayCritical(charsArray, NULL));
149    if (!chars) {
150        return 0;
151    }
152
153    char16_t result = map->getMap()->getMatch(
154        keyCode, reinterpret_cast<char16_t*>(chars), size_t(numChars),
155        metaState);
156
157    env->ReleasePrimitiveArrayCritical(charsArray, chars, JNI_ABORT);
158    return result;
159}
160
161static jchar nativeGetDisplayLabel(JNIEnv *env, jobject clazz, jlong ptr, jint keyCode) {
162    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
163    return map->getMap()->getDisplayLabel(keyCode);
164}
165
166static jint nativeGetKeyboardType(JNIEnv *env, jobject clazz, jlong ptr) {
167    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
168    return map->getMap()->getKeyboardType();
169}
170
171static jobjectArray nativeGetEvents(JNIEnv *env, jobject clazz, jlong ptr,
172        jcharArray charsArray) {
173    NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
174
175    jchar* chars = env->GetCharArrayElements(charsArray, NULL);
176    if (!chars) {
177        return NULL;
178    }
179    jsize numChars = env->GetArrayLength(charsArray);
180
181    Vector<KeyEvent> events;
182    jobjectArray result = NULL;
183    if (map->getMap()->getEvents(map->getDeviceId(),
184                                 reinterpret_cast<char16_t*>(chars),
185                                 size_t(numChars), events)) {
186        result = env->NewObjectArray(jsize(events.size()), gKeyEventClassInfo.clazz, NULL);
187        if (result) {
188            for (size_t i = 0; i < events.size(); i++) {
189                jobject keyEventObj = android_view_KeyEvent_fromNative(env, &events.itemAt(i));
190                if (!keyEventObj) break; // threw OOM exception
191                env->SetObjectArrayElement(result, jsize(i), keyEventObj);
192                env->DeleteLocalRef(keyEventObj);
193            }
194        }
195    }
196
197    env->ReleaseCharArrayElements(charsArray, chars, JNI_ABORT);
198    return result;
199}
200
201
202/*
203 * JNI registration.
204 */
205
206static JNINativeMethod g_methods[] = {
207    /* name, signature, funcPtr */
208    { "nativeReadFromParcel", "(Landroid/os/Parcel;)J",
209            (void*)nativeReadFromParcel },
210    { "nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
211            (void*)nativeWriteToParcel },
212    { "nativeDispose", "(J)V",
213            (void*)nativeDispose },
214    { "nativeGetCharacter", "(JII)C",
215            (void*)nativeGetCharacter },
216    { "nativeGetFallbackAction", "(JIILandroid/view/KeyCharacterMap$FallbackAction;)Z",
217            (void*)nativeGetFallbackAction },
218    { "nativeGetNumber", "(JI)C",
219            (void*)nativeGetNumber },
220    { "nativeGetMatch", "(JI[CI)C",
221            (void*)nativeGetMatch },
222    { "nativeGetDisplayLabel", "(JI)C",
223            (void*)nativeGetDisplayLabel },
224    { "nativeGetKeyboardType", "(J)I",
225            (void*)nativeGetKeyboardType },
226    { "nativeGetEvents", "(J[C)[Landroid/view/KeyEvent;",
227            (void*)nativeGetEvents },
228};
229
230int register_android_view_KeyCharacterMap(JNIEnv* env)
231{
232    gKeyCharacterMapClassInfo.clazz = FindClassOrDie(env, "android/view/KeyCharacterMap");
233    gKeyCharacterMapClassInfo.clazz = MakeGlobalRefOrDie(env, gKeyCharacterMapClassInfo.clazz);
234
235    gKeyCharacterMapClassInfo.ctor = GetMethodIDOrDie(env, gKeyCharacterMapClassInfo.clazz,
236            "<init>", "(J)V");
237
238    gKeyEventClassInfo.clazz = FindClassOrDie(env, "android/view/KeyEvent");
239    gKeyEventClassInfo.clazz = MakeGlobalRefOrDie(env, gKeyEventClassInfo.clazz);
240
241    jclass clazz = FindClassOrDie(env, "android/view/KeyCharacterMap$FallbackAction");
242
243    gFallbackActionClassInfo.keyCode = GetFieldIDOrDie(env, clazz, "keyCode", "I");
244    gFallbackActionClassInfo.metaState = GetFieldIDOrDie(env, clazz, "metaState", "I");
245
246    return RegisterMethodsOrDie(env, "android/view/KeyCharacterMap", g_methods, NELEM(g_methods));
247}
248
249}; // namespace android
250