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