• 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/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