1 /*
2 * Copyright (C) 2010 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 #define LOG_TAG "NativeActivity"
18 #include <utils/Log.h>
19
20 #include <poll.h>
21 #include <dlfcn.h>
22 #include <fcntl.h>
23
24 #include <android_runtime/AndroidRuntime.h>
25 #include <android_runtime/android_view_Surface.h>
26 #include <android_runtime/android_app_NativeActivity.h>
27 #include <android_runtime/android_util_AssetManager.h>
28 #include <surfaceflinger/Surface.h>
29 #include <ui/egl/android_natives.h>
30 #include <ui/InputTransport.h>
31 #include <utils/Looper.h>
32
33 #include "JNIHelp.h"
34 #include "android_os_MessageQueue.h"
35 #include "android_view_InputChannel.h"
36 #include "android_view_KeyEvent.h"
37
38 #define LOG_TRACE(...)
39 //#define LOG_TRACE(...) LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
40
41 namespace android
42 {
43
44 static struct {
45 jmethodID dispatchUnhandledKeyEvent;
46 jmethodID preDispatchKeyEvent;
47 jmethodID finish;
48 jmethodID setWindowFlags;
49 jmethodID setWindowFormat;
50 jmethodID showIme;
51 jmethodID hideIme;
52 } gNativeActivityClassInfo;
53
54 // ------------------------------------------------------------------------
55
56 struct ActivityWork {
57 int32_t cmd;
58 int32_t arg1;
59 int32_t arg2;
60 };
61
62 enum {
63 CMD_DEF_KEY = 1,
64 CMD_FINISH,
65 CMD_SET_WINDOW_FORMAT,
66 CMD_SET_WINDOW_FLAGS,
67 CMD_SHOW_SOFT_INPUT,
68 CMD_HIDE_SOFT_INPUT,
69 };
70
write_work(int fd,int32_t cmd,int32_t arg1=0,int32_t arg2=0)71 static void write_work(int fd, int32_t cmd, int32_t arg1=0, int32_t arg2=0) {
72 ActivityWork work;
73 work.cmd = cmd;
74 work.arg1 = arg1;
75 work.arg2 = arg2;
76
77 LOG_TRACE("write_work: cmd=%d", cmd);
78
79 restart:
80 int res = write(fd, &work, sizeof(work));
81 if (res < 0 && errno == EINTR) {
82 goto restart;
83 }
84
85 if (res == sizeof(work)) return;
86
87 if (res < 0) LOGW("Failed writing to work fd: %s", strerror(errno));
88 else LOGW("Truncated writing to work fd: %d", res);
89 }
90
read_work(int fd,ActivityWork * outWork)91 static bool read_work(int fd, ActivityWork* outWork) {
92 int res = read(fd, outWork, sizeof(ActivityWork));
93 // no need to worry about EINTR, poll loop will just come back again.
94 if (res == sizeof(ActivityWork)) return true;
95
96 if (res < 0) LOGW("Failed reading work fd: %s", strerror(errno));
97 else LOGW("Truncated reading work fd: %d", res);
98 return false;
99 }
100
101 // ------------------------------------------------------------------------
102
103 } // namespace android
104
105 using namespace android;
106
AInputQueue(const sp<InputChannel> & channel,int workWrite)107 AInputQueue::AInputQueue(const sp<InputChannel>& channel, int workWrite) :
108 mWorkWrite(workWrite), mConsumer(channel), mSeq(0) {
109 int msgpipe[2];
110 if (pipe(msgpipe)) {
111 LOGW("could not create pipe: %s", strerror(errno));
112 mDispatchKeyRead = mDispatchKeyWrite = -1;
113 } else {
114 mDispatchKeyRead = msgpipe[0];
115 mDispatchKeyWrite = msgpipe[1];
116 int result = fcntl(mDispatchKeyRead, F_SETFL, O_NONBLOCK);
117 SLOGW_IF(result != 0, "Could not make AInputQueue read pipe "
118 "non-blocking: %s", strerror(errno));
119 result = fcntl(mDispatchKeyWrite, F_SETFL, O_NONBLOCK);
120 SLOGW_IF(result != 0, "Could not make AInputQueue write pipe "
121 "non-blocking: %s", strerror(errno));
122 }
123 }
124
~AInputQueue()125 AInputQueue::~AInputQueue() {
126 close(mDispatchKeyRead);
127 close(mDispatchKeyWrite);
128 }
129
attachLooper(ALooper * looper,int ident,ALooper_callbackFunc callback,void * data)130 void AInputQueue::attachLooper(ALooper* looper, int ident,
131 ALooper_callbackFunc callback, void* data) {
132 mLooper = static_cast<android::Looper*>(looper);
133 mLooper->addFd(mConsumer.getChannel()->getReceivePipeFd(),
134 ident, ALOOPER_EVENT_INPUT, callback, data);
135 mLooper->addFd(mDispatchKeyRead,
136 ident, ALOOPER_EVENT_INPUT, callback, data);
137 }
138
detachLooper()139 void AInputQueue::detachLooper() {
140 mLooper->removeFd(mConsumer.getChannel()->getReceivePipeFd());
141 mLooper->removeFd(mDispatchKeyRead);
142 }
143
hasEvents()144 int32_t AInputQueue::hasEvents() {
145 struct pollfd pfd[2];
146
147 pfd[0].fd = mConsumer.getChannel()->getReceivePipeFd();
148 pfd[0].events = POLLIN;
149 pfd[0].revents = 0;
150 pfd[1].fd = mDispatchKeyRead;
151 pfd[1].events = POLLIN;
152 pfd[1].revents = 0;
153
154 int nfd = poll(pfd, 2, 0);
155 if (nfd <= 0) return 0;
156 return ((pfd[0].revents & POLLIN) || (pfd[1].revents & POLLIN)) ? 1 : -1;
157 }
158
getEvent(AInputEvent ** outEvent)159 int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
160 *outEvent = NULL;
161
162 bool finishNow = false;
163
164 char byteread;
165 ssize_t nRead = read(mDispatchKeyRead, &byteread, 1);
166 if (nRead == 1) {
167 mLock.lock();
168 if (mDispatchingKeys.size() > 0) {
169 KeyEvent* kevent = mDispatchingKeys[0];
170 *outEvent = kevent;
171 mDispatchingKeys.removeAt(0);
172 in_flight_event inflight;
173 inflight.event = kevent;
174 inflight.seq = -1;
175 inflight.doFinish = false;
176 mInFlightEvents.push(inflight);
177 }
178 if (mFinishPreDispatches.size() > 0) {
179 finish_pre_dispatch finish(mFinishPreDispatches[0]);
180 mFinishPreDispatches.removeAt(0);
181 const size_t N = mInFlightEvents.size();
182 for (size_t i=0; i<N; i++) {
183 const in_flight_event& inflight(mInFlightEvents[i]);
184 if (inflight.seq == finish.seq) {
185 *outEvent = inflight.event;
186 finishNow = finish.handled;
187 }
188 }
189 if (*outEvent == NULL) {
190 LOGW("getEvent couldn't find inflight for seq %d", finish.seq);
191 }
192 }
193 mLock.unlock();
194
195 if (finishNow) {
196 finishEvent(*outEvent, true, false);
197 *outEvent = NULL;
198 return -1;
199 } else if (*outEvent != NULL) {
200 return 0;
201 }
202 }
203
204 int32_t res = mConsumer.receiveDispatchSignal();
205 if (res != android::OK) {
206 LOGE("channel '%s' ~ Failed to receive dispatch signal. status=%d",
207 mConsumer.getChannel()->getName().string(), res);
208 return -1;
209 }
210
211 InputEvent* myEvent = NULL;
212 res = mConsumer.consume(this, &myEvent);
213 if (res != android::OK) {
214 LOGW("channel '%s' ~ Failed to consume input event. status=%d",
215 mConsumer.getChannel()->getName().string(), res);
216 mConsumer.sendFinishedSignal(false);
217 return -1;
218 }
219
220 in_flight_event inflight;
221 inflight.event = myEvent;
222 inflight.seq = -1;
223 inflight.doFinish = true;
224 mInFlightEvents.push(inflight);
225
226 *outEvent = myEvent;
227 return 0;
228 }
229
preDispatchEvent(AInputEvent * event)230 bool AInputQueue::preDispatchEvent(AInputEvent* event) {
231 if (((InputEvent*)event)->getType() != AINPUT_EVENT_TYPE_KEY) {
232 // The IME only cares about key events.
233 return false;
234 }
235
236 // For now we only send system keys to the IME... this avoids having
237 // critical keys like DPAD go through this path. We really need to have
238 // the IME report which keys it wants.
239 if (!((KeyEvent*)event)->isSystemKey()) {
240 return false;
241 }
242
243 return preDispatchKey((KeyEvent*)event);
244 }
245
finishEvent(AInputEvent * event,bool handled,bool didDefaultHandling)246 void AInputQueue::finishEvent(AInputEvent* event, bool handled, bool didDefaultHandling) {
247 LOG_TRACE("finishEvent: %p handled=%d, didDefaultHandling=%d", event,
248 handled ? 1 : 0, didDefaultHandling ? 1 : 0);
249
250 if (!handled && !didDefaultHandling
251 && ((InputEvent*)event)->getType() == AINPUT_EVENT_TYPE_KEY
252 && ((KeyEvent*)event)->hasDefaultAction()) {
253 // The app didn't handle this, but it may have a default action
254 // associated with it. We need to hand this back to Java to be
255 // executed.
256 doUnhandledKey((KeyEvent*)event);
257 return;
258 }
259
260 mLock.lock();
261 const size_t N = mInFlightEvents.size();
262 for (size_t i=0; i<N; i++) {
263 const in_flight_event& inflight(mInFlightEvents[i]);
264 if (inflight.event == event) {
265 if (inflight.doFinish) {
266 int32_t res = mConsumer.sendFinishedSignal(handled);
267 if (res != android::OK) {
268 LOGW("Failed to send finished signal on channel '%s'. status=%d",
269 mConsumer.getChannel()->getName().string(), res);
270 }
271 }
272 if (static_cast<InputEvent*>(event)->getType() == AINPUT_EVENT_TYPE_KEY) {
273 mAvailKeyEvents.push(static_cast<KeyEvent*>(event));
274 } else {
275 mAvailMotionEvents.push(static_cast<MotionEvent*>(event));
276 }
277 mInFlightEvents.removeAt(i);
278 mLock.unlock();
279 return;
280 }
281 }
282 mLock.unlock();
283
284 LOGW("finishEvent called for unknown event: %p", event);
285 }
286
dispatchEvent(android::KeyEvent * event)287 void AInputQueue::dispatchEvent(android::KeyEvent* event) {
288 mLock.lock();
289 LOG_TRACE("dispatchEvent: dispatching=%d write=%d\n", mDispatchingKeys.size(),
290 mDispatchKeyWrite);
291 mDispatchingKeys.add(event);
292 wakeupDispatch();
293 mLock.unlock();
294 }
295
finishPreDispatch(int seq,bool handled)296 void AInputQueue::finishPreDispatch(int seq, bool handled) {
297 mLock.lock();
298 LOG_TRACE("finishPreDispatch: seq=%d handled=%d\n", seq, handled ? 1 : 0);
299 finish_pre_dispatch finish;
300 finish.seq = seq;
301 finish.handled = handled;
302 mFinishPreDispatches.add(finish);
303 wakeupDispatch();
304 mLock.unlock();
305 }
306
consumeUnhandledEvent()307 KeyEvent* AInputQueue::consumeUnhandledEvent() {
308 KeyEvent* event = NULL;
309
310 mLock.lock();
311 if (mUnhandledKeys.size() > 0) {
312 event = mUnhandledKeys[0];
313 mUnhandledKeys.removeAt(0);
314 }
315 mLock.unlock();
316
317 LOG_TRACE("consumeUnhandledEvent: KeyEvent=%p", event);
318
319 return event;
320 }
321
consumePreDispatchingEvent(int * outSeq)322 KeyEvent* AInputQueue::consumePreDispatchingEvent(int* outSeq) {
323 KeyEvent* event = NULL;
324
325 mLock.lock();
326 if (mPreDispatchingKeys.size() > 0) {
327 const in_flight_event& inflight(mPreDispatchingKeys[0]);
328 event = static_cast<KeyEvent*>(inflight.event);
329 *outSeq = inflight.seq;
330 mPreDispatchingKeys.removeAt(0);
331 }
332 mLock.unlock();
333
334 LOG_TRACE("consumePreDispatchingEvent: KeyEvent=%p", event);
335
336 return event;
337 }
338
createKeyEvent()339 KeyEvent* AInputQueue::createKeyEvent() {
340 mLock.lock();
341 KeyEvent* event;
342 if (mAvailKeyEvents.size() <= 0) {
343 event = new KeyEvent();
344 } else {
345 event = mAvailKeyEvents.top();
346 mAvailKeyEvents.pop();
347 }
348 mLock.unlock();
349 return event;
350 }
351
createMotionEvent()352 MotionEvent* AInputQueue::createMotionEvent() {
353 mLock.lock();
354 MotionEvent* event;
355 if (mAvailMotionEvents.size() <= 0) {
356 event = new MotionEvent();
357 } else {
358 event = mAvailMotionEvents.top();
359 mAvailMotionEvents.pop();
360 }
361 mLock.unlock();
362 return event;
363 }
364
doUnhandledKey(KeyEvent * keyEvent)365 void AInputQueue::doUnhandledKey(KeyEvent* keyEvent) {
366 mLock.lock();
367 LOG_TRACE("Unhandled key: pending=%d write=%d\n", mUnhandledKeys.size(), mWorkWrite);
368 if (mUnhandledKeys.size() <= 0 && mWorkWrite >= 0) {
369 write_work(mWorkWrite, CMD_DEF_KEY);
370 }
371 mUnhandledKeys.add(keyEvent);
372 mLock.unlock();
373 }
374
preDispatchKey(KeyEvent * keyEvent)375 bool AInputQueue::preDispatchKey(KeyEvent* keyEvent) {
376 mLock.lock();
377 LOG_TRACE("preDispatch key: pending=%d write=%d\n", mPreDispatchingKeys.size(), mWorkWrite);
378 const size_t N = mInFlightEvents.size();
379 for (size_t i=0; i<N; i++) {
380 in_flight_event& inflight(mInFlightEvents.editItemAt(i));
381 if (inflight.event == keyEvent) {
382 if (inflight.seq >= 0) {
383 // This event has already been pre-dispatched!
384 LOG_TRACE("Event already pre-dispatched!");
385 mLock.unlock();
386 return false;
387 }
388 mSeq++;
389 if (mSeq < 0) mSeq = 1;
390 inflight.seq = mSeq;
391
392 if (mPreDispatchingKeys.size() <= 0 && mWorkWrite >= 0) {
393 write_work(mWorkWrite, CMD_DEF_KEY);
394 }
395 mPreDispatchingKeys.add(inflight);
396 mLock.unlock();
397 return true;
398 }
399 }
400
401 LOGW("preDispatchKey called for unknown event: %p", keyEvent);
402 return false;
403 }
404
wakeupDispatch()405 void AInputQueue::wakeupDispatch() {
406 restart:
407 char dummy = 0;
408 int res = write(mDispatchKeyWrite, &dummy, sizeof(dummy));
409 if (res < 0 && errno == EINTR) {
410 goto restart;
411 }
412
413 if (res == sizeof(dummy)) return;
414
415 if (res < 0) LOGW("Failed writing to dispatch fd: %s", strerror(errno));
416 else LOGW("Truncated writing to dispatch fd: %d", res);
417 }
418
419 namespace android {
420
421 // ------------------------------------------------------------------------
422
423 /*
424 * Native state for interacting with the NativeActivity class.
425 */
426 struct NativeCode : public ANativeActivity {
NativeCodeandroid::NativeCode427 NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) {
428 memset((ANativeActivity*)this, 0, sizeof(ANativeActivity));
429 memset(&callbacks, 0, sizeof(callbacks));
430 dlhandle = _dlhandle;
431 createActivityFunc = _createFunc;
432 nativeWindow = NULL;
433 inputChannel = NULL;
434 nativeInputQueue = NULL;
435 mainWorkRead = mainWorkWrite = -1;
436 }
437
~NativeCodeandroid::NativeCode438 ~NativeCode() {
439 if (callbacks.onDestroy != NULL) {
440 callbacks.onDestroy(this);
441 }
442 if (env != NULL && clazz != NULL) {
443 env->DeleteGlobalRef(clazz);
444 }
445 if (looper != NULL && mainWorkRead >= 0) {
446 looper->removeFd(mainWorkRead);
447 }
448 if (nativeInputQueue != NULL) {
449 nativeInputQueue->mWorkWrite = -1;
450 }
451 setSurface(NULL);
452 setInputChannel(NULL);
453 if (mainWorkRead >= 0) close(mainWorkRead);
454 if (mainWorkWrite >= 0) close(mainWorkWrite);
455 if (dlhandle != NULL) {
456 // for now don't unload... we probably should clean this
457 // up and only keep one open dlhandle per proc, since there
458 // is really no benefit to unloading the code.
459 //dlclose(dlhandle);
460 }
461 }
462
setSurfaceandroid::NativeCode463 void setSurface(jobject _surface) {
464 if (_surface != NULL) {
465 nativeWindow = android_Surface_getNativeWindow(env, _surface);
466 } else {
467 nativeWindow = NULL;
468 }
469 }
470
setInputChannelandroid::NativeCode471 status_t setInputChannel(jobject _channel) {
472 if (inputChannel != NULL) {
473 delete nativeInputQueue;
474 env->DeleteGlobalRef(inputChannel);
475 }
476 inputChannel = NULL;
477 nativeInputQueue = NULL;
478 if (_channel != NULL) {
479 inputChannel = env->NewGlobalRef(_channel);
480 sp<InputChannel> ic =
481 android_view_InputChannel_getInputChannel(env, _channel);
482 if (ic != NULL) {
483 nativeInputQueue = new AInputQueue(ic, mainWorkWrite);
484 if (nativeInputQueue->getConsumer().initialize() != android::OK) {
485 delete nativeInputQueue;
486 nativeInputQueue = NULL;
487 return UNKNOWN_ERROR;
488 }
489 } else {
490 return UNKNOWN_ERROR;
491 }
492 }
493 return OK;
494 }
495
496 ANativeActivityCallbacks callbacks;
497
498 void* dlhandle;
499 ANativeActivity_createFunc* createActivityFunc;
500
501 String8 internalDataPathObj;
502 String8 externalDataPathObj;
503 String8 obbPathObj;
504
505 sp<ANativeWindow> nativeWindow;
506 int32_t lastWindowWidth;
507 int32_t lastWindowHeight;
508
509 jobject inputChannel;
510 struct AInputQueue* nativeInputQueue;
511
512 // These are used to wake up the main thread to process work.
513 int mainWorkRead;
514 int mainWorkWrite;
515 sp<Looper> looper;
516 };
517
android_NativeActivity_finish(ANativeActivity * activity)518 void android_NativeActivity_finish(ANativeActivity* activity) {
519 NativeCode* code = static_cast<NativeCode*>(activity);
520 write_work(code->mainWorkWrite, CMD_FINISH, 0);
521 }
522
android_NativeActivity_setWindowFormat(ANativeActivity * activity,int32_t format)523 void android_NativeActivity_setWindowFormat(
524 ANativeActivity* activity, int32_t format) {
525 NativeCode* code = static_cast<NativeCode*>(activity);
526 write_work(code->mainWorkWrite, CMD_SET_WINDOW_FORMAT, format);
527 }
528
android_NativeActivity_setWindowFlags(ANativeActivity * activity,int32_t values,int32_t mask)529 void android_NativeActivity_setWindowFlags(
530 ANativeActivity* activity, int32_t values, int32_t mask) {
531 NativeCode* code = static_cast<NativeCode*>(activity);
532 write_work(code->mainWorkWrite, CMD_SET_WINDOW_FLAGS, values, mask);
533 }
534
android_NativeActivity_showSoftInput(ANativeActivity * activity,int32_t flags)535 void android_NativeActivity_showSoftInput(
536 ANativeActivity* activity, int32_t flags) {
537 NativeCode* code = static_cast<NativeCode*>(activity);
538 write_work(code->mainWorkWrite, CMD_SHOW_SOFT_INPUT, flags);
539 }
540
android_NativeActivity_hideSoftInput(ANativeActivity * activity,int32_t flags)541 void android_NativeActivity_hideSoftInput(
542 ANativeActivity* activity, int32_t flags) {
543 NativeCode* code = static_cast<NativeCode*>(activity);
544 write_work(code->mainWorkWrite, CMD_HIDE_SOFT_INPUT, flags);
545 }
546
547 // ------------------------------------------------------------------------
548
checkAndClearExceptionFromCallback(JNIEnv * env,const char * methodName)549 static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
550 if (env->ExceptionCheck()) {
551 LOGE("An exception was thrown by callback '%s'.", methodName);
552 LOGE_EX(env);
553 env->ExceptionClear();
554 return true;
555 }
556 return false;
557 }
558
559 /*
560 * Callback for handling native events on the application's main thread.
561 */
mainWorkCallback(int fd,int events,void * data)562 static int mainWorkCallback(int fd, int events, void* data) {
563 NativeCode* code = (NativeCode*)data;
564 if ((events & POLLIN) == 0) {
565 return 1;
566 }
567
568 ActivityWork work;
569 if (!read_work(code->mainWorkRead, &work)) {
570 return 1;
571 }
572
573 LOG_TRACE("mainWorkCallback: cmd=%d", work.cmd);
574
575 switch (work.cmd) {
576 case CMD_DEF_KEY: {
577 KeyEvent* keyEvent;
578 while ((keyEvent=code->nativeInputQueue->consumeUnhandledEvent()) != NULL) {
579 jobject inputEventObj = android_view_KeyEvent_fromNative(
580 code->env, keyEvent);
581 jboolean handled;
582 if (inputEventObj) {
583 handled = code->env->CallBooleanMethod(code->clazz,
584 gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
585 checkAndClearExceptionFromCallback(code->env, "dispatchUnhandledKeyEvent");
586 code->env->DeleteLocalRef(inputEventObj);
587 } else {
588 LOGE("Failed to obtain key event for dispatchUnhandledKeyEvent.");
589 handled = false;
590 }
591 code->nativeInputQueue->finishEvent(keyEvent, handled, true);
592 }
593 int seq;
594 while ((keyEvent=code->nativeInputQueue->consumePreDispatchingEvent(&seq)) != NULL) {
595 jobject inputEventObj = android_view_KeyEvent_fromNative(
596 code->env, keyEvent);
597 if (inputEventObj) {
598 code->env->CallVoidMethod(code->clazz,
599 gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq);
600 checkAndClearExceptionFromCallback(code->env, "preDispatchKeyEvent");
601 code->env->DeleteLocalRef(inputEventObj);
602 } else {
603 LOGE("Failed to obtain key event for preDispatchKeyEvent.");
604 }
605 }
606 } break;
607 case CMD_FINISH: {
608 code->env->CallVoidMethod(code->clazz, gNativeActivityClassInfo.finish);
609 checkAndClearExceptionFromCallback(code->env, "finish");
610 } break;
611 case CMD_SET_WINDOW_FORMAT: {
612 code->env->CallVoidMethod(code->clazz,
613 gNativeActivityClassInfo.setWindowFormat, work.arg1);
614 checkAndClearExceptionFromCallback(code->env, "setWindowFormat");
615 } break;
616 case CMD_SET_WINDOW_FLAGS: {
617 code->env->CallVoidMethod(code->clazz,
618 gNativeActivityClassInfo.setWindowFlags, work.arg1, work.arg2);
619 checkAndClearExceptionFromCallback(code->env, "setWindowFlags");
620 } break;
621 case CMD_SHOW_SOFT_INPUT: {
622 code->env->CallVoidMethod(code->clazz,
623 gNativeActivityClassInfo.showIme, work.arg1);
624 checkAndClearExceptionFromCallback(code->env, "showIme");
625 } break;
626 case CMD_HIDE_SOFT_INPUT: {
627 code->env->CallVoidMethod(code->clazz,
628 gNativeActivityClassInfo.hideIme, work.arg1);
629 checkAndClearExceptionFromCallback(code->env, "hideIme");
630 } break;
631 default:
632 LOGW("Unknown work command: %d", work.cmd);
633 break;
634 }
635
636 return 1;
637 }
638
639 // ------------------------------------------------------------------------
640
641 static jint
loadNativeCode_native(JNIEnv * env,jobject clazz,jstring path,jstring funcName,jobject messageQueue,jstring internalDataDir,jstring obbDir,jstring externalDataDir,int sdkVersion,jobject jAssetMgr,jbyteArray savedState)642 loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName,
643 jobject messageQueue, jstring internalDataDir, jstring obbDir,
644 jstring externalDataDir, int sdkVersion,
645 jobject jAssetMgr, jbyteArray savedState)
646 {
647 LOG_TRACE("loadNativeCode_native");
648
649 const char* pathStr = env->GetStringUTFChars(path, NULL);
650 NativeCode* code = NULL;
651
652 void* handle = dlopen(pathStr, RTLD_LAZY);
653
654 env->ReleaseStringUTFChars(path, pathStr);
655
656 if (handle != NULL) {
657 const char* funcStr = env->GetStringUTFChars(funcName, NULL);
658 code = new NativeCode(handle, (ANativeActivity_createFunc*)
659 dlsym(handle, funcStr));
660 env->ReleaseStringUTFChars(funcName, funcStr);
661
662 if (code->createActivityFunc == NULL) {
663 LOGW("ANativeActivity_onCreate not found");
664 delete code;
665 return 0;
666 }
667
668 code->looper = android_os_MessageQueue_getLooper(env, messageQueue);
669 if (code->looper == NULL) {
670 LOGW("Unable to retrieve MessageQueue's Looper");
671 delete code;
672 return 0;
673 }
674
675 int msgpipe[2];
676 if (pipe(msgpipe)) {
677 LOGW("could not create pipe: %s", strerror(errno));
678 delete code;
679 return 0;
680 }
681 code->mainWorkRead = msgpipe[0];
682 code->mainWorkWrite = msgpipe[1];
683 int result = fcntl(code->mainWorkRead, F_SETFL, O_NONBLOCK);
684 SLOGW_IF(result != 0, "Could not make main work read pipe "
685 "non-blocking: %s", strerror(errno));
686 result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK);
687 SLOGW_IF(result != 0, "Could not make main work write pipe "
688 "non-blocking: %s", strerror(errno));
689 code->looper->addFd(code->mainWorkRead, 0, ALOOPER_EVENT_INPUT, mainWorkCallback, code);
690
691 code->ANativeActivity::callbacks = &code->callbacks;
692 if (env->GetJavaVM(&code->vm) < 0) {
693 LOGW("NativeActivity GetJavaVM failed");
694 delete code;
695 return 0;
696 }
697 code->env = env;
698 code->clazz = env->NewGlobalRef(clazz);
699
700 const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL);
701 code->internalDataPathObj = dirStr;
702 code->internalDataPath = code->internalDataPathObj.string();
703 env->ReleaseStringUTFChars(internalDataDir, dirStr);
704
705 dirStr = env->GetStringUTFChars(externalDataDir, NULL);
706 code->externalDataPathObj = dirStr;
707 code->externalDataPath = code->externalDataPathObj.string();
708 env->ReleaseStringUTFChars(externalDataDir, dirStr);
709
710 code->sdkVersion = sdkVersion;
711
712 code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
713
714 dirStr = env->GetStringUTFChars(obbDir, NULL);
715 code->obbPathObj = dirStr;
716 code->obbPath = code->obbPathObj.string();
717 env->ReleaseStringUTFChars(obbDir, dirStr);
718
719 jbyte* rawSavedState = NULL;
720 jsize rawSavedSize = 0;
721 if (savedState != NULL) {
722 rawSavedState = env->GetByteArrayElements(savedState, NULL);
723 rawSavedSize = env->GetArrayLength(savedState);
724 }
725
726 code->createActivityFunc(code, rawSavedState, rawSavedSize);
727
728 if (rawSavedState != NULL) {
729 env->ReleaseByteArrayElements(savedState, rawSavedState, 0);
730 }
731 }
732
733 return (jint)code;
734 }
735
736 static void
unloadNativeCode_native(JNIEnv * env,jobject clazz,jint handle)737 unloadNativeCode_native(JNIEnv* env, jobject clazz, jint handle)
738 {
739 LOG_TRACE("unloadNativeCode_native");
740 if (handle != 0) {
741 NativeCode* code = (NativeCode*)handle;
742 delete code;
743 }
744 }
745
746 static void
onStart_native(JNIEnv * env,jobject clazz,jint handle)747 onStart_native(JNIEnv* env, jobject clazz, jint handle)
748 {
749 LOG_TRACE("onStart_native");
750 if (handle != 0) {
751 NativeCode* code = (NativeCode*)handle;
752 if (code->callbacks.onStart != NULL) {
753 code->callbacks.onStart(code);
754 }
755 }
756 }
757
758 static void
onResume_native(JNIEnv * env,jobject clazz,jint handle)759 onResume_native(JNIEnv* env, jobject clazz, jint handle)
760 {
761 LOG_TRACE("onResume_native");
762 if (handle != 0) {
763 NativeCode* code = (NativeCode*)handle;
764 if (code->callbacks.onResume != NULL) {
765 code->callbacks.onResume(code);
766 }
767 }
768 }
769
770 static jbyteArray
onSaveInstanceState_native(JNIEnv * env,jobject clazz,jint handle)771 onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle)
772 {
773 LOG_TRACE("onSaveInstanceState_native");
774
775 jbyteArray array = NULL;
776
777 if (handle != 0) {
778 NativeCode* code = (NativeCode*)handle;
779 if (code->callbacks.onSaveInstanceState != NULL) {
780 size_t len = 0;
781 jbyte* state = (jbyte*)code->callbacks.onSaveInstanceState(code, &len);
782 if (len > 0) {
783 array = env->NewByteArray(len);
784 if (array != NULL) {
785 env->SetByteArrayRegion(array, 0, len, state);
786 }
787 }
788 if (state != NULL) {
789 free(state);
790 }
791 }
792 }
793
794 return array;
795 }
796
797 static void
onPause_native(JNIEnv * env,jobject clazz,jint handle)798 onPause_native(JNIEnv* env, jobject clazz, jint handle)
799 {
800 LOG_TRACE("onPause_native");
801 if (handle != 0) {
802 NativeCode* code = (NativeCode*)handle;
803 if (code->callbacks.onPause != NULL) {
804 code->callbacks.onPause(code);
805 }
806 }
807 }
808
809 static void
onStop_native(JNIEnv * env,jobject clazz,jint handle)810 onStop_native(JNIEnv* env, jobject clazz, jint handle)
811 {
812 LOG_TRACE("onStop_native");
813 if (handle != 0) {
814 NativeCode* code = (NativeCode*)handle;
815 if (code->callbacks.onStop != NULL) {
816 code->callbacks.onStop(code);
817 }
818 }
819 }
820
821 static void
onConfigurationChanged_native(JNIEnv * env,jobject clazz,jint handle)822 onConfigurationChanged_native(JNIEnv* env, jobject clazz, jint handle)
823 {
824 LOG_TRACE("onConfigurationChanged_native");
825 if (handle != 0) {
826 NativeCode* code = (NativeCode*)handle;
827 if (code->callbacks.onConfigurationChanged != NULL) {
828 code->callbacks.onConfigurationChanged(code);
829 }
830 }
831 }
832
833 static void
onLowMemory_native(JNIEnv * env,jobject clazz,jint handle)834 onLowMemory_native(JNIEnv* env, jobject clazz, jint handle)
835 {
836 LOG_TRACE("onLowMemory_native");
837 if (handle != 0) {
838 NativeCode* code = (NativeCode*)handle;
839 if (code->callbacks.onLowMemory != NULL) {
840 code->callbacks.onLowMemory(code);
841 }
842 }
843 }
844
845 static void
onWindowFocusChanged_native(JNIEnv * env,jobject clazz,jint handle,jboolean focused)846 onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jint handle, jboolean focused)
847 {
848 LOG_TRACE("onWindowFocusChanged_native");
849 if (handle != 0) {
850 NativeCode* code = (NativeCode*)handle;
851 if (code->callbacks.onWindowFocusChanged != NULL) {
852 code->callbacks.onWindowFocusChanged(code, focused ? 1 : 0);
853 }
854 }
855 }
856
857 static void
onSurfaceCreated_native(JNIEnv * env,jobject clazz,jint handle,jobject surface)858 onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface)
859 {
860 LOG_TRACE("onSurfaceCreated_native");
861 if (handle != 0) {
862 NativeCode* code = (NativeCode*)handle;
863 code->setSurface(surface);
864 if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) {
865 code->callbacks.onNativeWindowCreated(code,
866 code->nativeWindow.get());
867 }
868 }
869 }
870
getWindowProp(ANativeWindow * window,int what)871 static int32_t getWindowProp(ANativeWindow* window, int what) {
872 int value;
873 int res = window->query(window, what, &value);
874 return res < 0 ? res : value;
875 }
876
877 static void
onSurfaceChanged_native(JNIEnv * env,jobject clazz,jint handle,jobject surface,jint format,jint width,jint height)878 onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface,
879 jint format, jint width, jint height)
880 {
881 LOG_TRACE("onSurfaceChanged_native");
882 if (handle != 0) {
883 NativeCode* code = (NativeCode*)handle;
884 sp<ANativeWindow> oldNativeWindow = code->nativeWindow;
885 code->setSurface(surface);
886 if (oldNativeWindow != code->nativeWindow) {
887 if (oldNativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
888 code->callbacks.onNativeWindowDestroyed(code,
889 oldNativeWindow.get());
890 }
891 if (code->nativeWindow != NULL) {
892 if (code->callbacks.onNativeWindowCreated != NULL) {
893 code->callbacks.onNativeWindowCreated(code,
894 code->nativeWindow.get());
895 }
896 code->lastWindowWidth = getWindowProp(code->nativeWindow.get(),
897 NATIVE_WINDOW_WIDTH);
898 code->lastWindowHeight = getWindowProp(code->nativeWindow.get(),
899 NATIVE_WINDOW_HEIGHT);
900 }
901 } else {
902 // Maybe it resized?
903 int32_t newWidth = getWindowProp(code->nativeWindow.get(),
904 NATIVE_WINDOW_WIDTH);
905 int32_t newHeight = getWindowProp(code->nativeWindow.get(),
906 NATIVE_WINDOW_HEIGHT);
907 if (newWidth != code->lastWindowWidth
908 || newHeight != code->lastWindowHeight) {
909 if (code->callbacks.onNativeWindowResized != NULL) {
910 code->callbacks.onNativeWindowResized(code,
911 code->nativeWindow.get());
912 }
913 }
914 }
915 }
916 }
917
918 static void
onSurfaceRedrawNeeded_native(JNIEnv * env,jobject clazz,jint handle)919 onSurfaceRedrawNeeded_native(JNIEnv* env, jobject clazz, jint handle)
920 {
921 LOG_TRACE("onSurfaceRedrawNeeded_native");
922 if (handle != 0) {
923 NativeCode* code = (NativeCode*)handle;
924 if (code->nativeWindow != NULL && code->callbacks.onNativeWindowRedrawNeeded != NULL) {
925 code->callbacks.onNativeWindowRedrawNeeded(code, code->nativeWindow.get());
926 }
927 }
928 }
929
930 static void
onSurfaceDestroyed_native(JNIEnv * env,jobject clazz,jint handle,jobject surface)931 onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surface)
932 {
933 LOG_TRACE("onSurfaceDestroyed_native");
934 if (handle != 0) {
935 NativeCode* code = (NativeCode*)handle;
936 if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
937 code->callbacks.onNativeWindowDestroyed(code,
938 code->nativeWindow.get());
939 }
940 code->setSurface(NULL);
941 }
942 }
943
944 static void
onInputChannelCreated_native(JNIEnv * env,jobject clazz,jint handle,jobject channel)945 onInputChannelCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject channel)
946 {
947 LOG_TRACE("onInputChannelCreated_native");
948 if (handle != 0) {
949 NativeCode* code = (NativeCode*)handle;
950 status_t err = code->setInputChannel(channel);
951 if (err != OK) {
952 jniThrowException(env, "java/lang/IllegalStateException",
953 "Error setting input channel");
954 return;
955 }
956 if (code->callbacks.onInputQueueCreated != NULL) {
957 code->callbacks.onInputQueueCreated(code,
958 code->nativeInputQueue);
959 }
960 }
961 }
962
963 static void
onInputChannelDestroyed_native(JNIEnv * env,jobject clazz,jint handle,jobject channel)964 onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject channel)
965 {
966 LOG_TRACE("onInputChannelDestroyed_native");
967 if (handle != 0) {
968 NativeCode* code = (NativeCode*)handle;
969 if (code->nativeInputQueue != NULL
970 && code->callbacks.onInputQueueDestroyed != NULL) {
971 code->callbacks.onInputQueueDestroyed(code,
972 code->nativeInputQueue);
973 }
974 code->setInputChannel(NULL);
975 }
976 }
977
978 static void
onContentRectChanged_native(JNIEnv * env,jobject clazz,jint handle,jint x,jint y,jint w,jint h)979 onContentRectChanged_native(JNIEnv* env, jobject clazz, jint handle,
980 jint x, jint y, jint w, jint h)
981 {
982 LOG_TRACE("onContentRectChanged_native");
983 if (handle != 0) {
984 NativeCode* code = (NativeCode*)handle;
985 if (code->callbacks.onContentRectChanged != NULL) {
986 ARect rect;
987 rect.left = x;
988 rect.top = y;
989 rect.right = x+w;
990 rect.bottom = y+h;
991 code->callbacks.onContentRectChanged(code, &rect);
992 }
993 }
994 }
995
996 static void
dispatchKeyEvent_native(JNIEnv * env,jobject clazz,jint handle,jobject eventObj)997 dispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, jobject eventObj)
998 {
999 LOG_TRACE("dispatchKeyEvent_native");
1000 if (handle != 0) {
1001 NativeCode* code = (NativeCode*)handle;
1002 if (code->nativeInputQueue != NULL) {
1003 KeyEvent* event = code->nativeInputQueue->createKeyEvent();
1004 status_t status = android_view_KeyEvent_toNative(env, eventObj, event);
1005 if (status) {
1006 delete event;
1007 jniThrowRuntimeException(env, "Could not read contents of KeyEvent object.");
1008 return;
1009 }
1010 code->nativeInputQueue->dispatchEvent(event);
1011 }
1012 }
1013 }
1014
1015 static void
finishPreDispatchKeyEvent_native(JNIEnv * env,jobject clazz,jint handle,jint seq,jboolean handled)1016 finishPreDispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle,
1017 jint seq, jboolean handled)
1018 {
1019 LOG_TRACE("finishPreDispatchKeyEvent_native");
1020 if (handle != 0) {
1021 NativeCode* code = (NativeCode*)handle;
1022 if (code->nativeInputQueue != NULL) {
1023 code->nativeInputQueue->finishPreDispatch(seq, handled ? true : false);
1024 }
1025 }
1026 }
1027
1028 static const JNINativeMethod g_methods[] = {
1029 { "loadNativeCode", "(Ljava/lang/String;Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;[B)I",
1030 (void*)loadNativeCode_native },
1031 { "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native },
1032 { "onStartNative", "(I)V", (void*)onStart_native },
1033 { "onResumeNative", "(I)V", (void*)onResume_native },
1034 { "onSaveInstanceStateNative", "(I)[B", (void*)onSaveInstanceState_native },
1035 { "onPauseNative", "(I)V", (void*)onPause_native },
1036 { "onStopNative", "(I)V", (void*)onStop_native },
1037 { "onConfigurationChangedNative", "(I)V", (void*)onConfigurationChanged_native },
1038 { "onLowMemoryNative", "(I)V", (void*)onLowMemory_native },
1039 { "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native },
1040 { "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native },
1041 { "onSurfaceChangedNative", "(ILandroid/view/Surface;III)V", (void*)onSurfaceChanged_native },
1042 { "onSurfaceRedrawNeededNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceRedrawNeeded_native },
1043 { "onSurfaceDestroyedNative", "(I)V", (void*)onSurfaceDestroyed_native },
1044 { "onInputChannelCreatedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelCreated_native },
1045 { "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native },
1046 { "onContentRectChangedNative", "(IIIII)V", (void*)onContentRectChanged_native },
1047 { "dispatchKeyEventNative", "(ILandroid/view/KeyEvent;)V", (void*)dispatchKeyEvent_native },
1048 { "finishPreDispatchKeyEventNative", "(IIZ)V", (void*)finishPreDispatchKeyEvent_native },
1049 };
1050
1051 static const char* const kNativeActivityPathName = "android/app/NativeActivity";
1052
1053 #define FIND_CLASS(var, className) \
1054 var = env->FindClass(className); \
1055 LOG_FATAL_IF(! var, "Unable to find class %s", className);
1056
1057 #define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
1058 var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
1059 LOG_FATAL_IF(! var, "Unable to find method" methodName);
1060
register_android_app_NativeActivity(JNIEnv * env)1061 int register_android_app_NativeActivity(JNIEnv* env)
1062 {
1063 //LOGD("register_android_app_NativeActivity");
1064 jclass clazz;
1065 FIND_CLASS(clazz, kNativeActivityPathName);
1066
1067 GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent,
1068 clazz,
1069 "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)Z");
1070 GET_METHOD_ID(gNativeActivityClassInfo.preDispatchKeyEvent,
1071 clazz,
1072 "preDispatchKeyEvent", "(Landroid/view/KeyEvent;I)V");
1073
1074 GET_METHOD_ID(gNativeActivityClassInfo.finish,
1075 clazz,
1076 "finish", "()V");
1077 GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags,
1078 clazz,
1079 "setWindowFlags", "(II)V");
1080 GET_METHOD_ID(gNativeActivityClassInfo.setWindowFormat,
1081 clazz,
1082 "setWindowFormat", "(I)V");
1083 GET_METHOD_ID(gNativeActivityClassInfo.showIme,
1084 clazz,
1085 "showIme", "(I)V");
1086 GET_METHOD_ID(gNativeActivityClassInfo.hideIme,
1087 clazz,
1088 "hideIme", "(I)V");
1089
1090 return AndroidRuntime::registerNativeMethods(
1091 env, kNativeActivityPathName,
1092 g_methods, NELEM(g_methods));
1093 }
1094
1095 } // namespace android
1096