• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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