Fix issue #3117918: No way to finish a native activity

Change-Id: Ic53e712f7ab5412d72a31b96ecba252344b91644
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 6aa77f6..63d3578 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -35,8 +35,8 @@
 #include "android_view_InputChannel.h"
 #include "android_view_KeyEvent.h"
 
-//#define LOG_TRACE(...)
-#define LOG_TRACE(...) LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define LOG_TRACE(...)
+//#define LOG_TRACE(...) LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
 
 namespace android
 {
@@ -46,6 +46,7 @@
 
     jmethodID dispatchUnhandledKeyEvent;
     jmethodID preDispatchKeyEvent;
+    jmethodID finish;
     jmethodID setWindowFlags;
     jmethodID setWindowFormat;
     jmethodID showIme;
@@ -62,6 +63,7 @@
 
 enum {
     CMD_DEF_KEY = 1,
+    CMD_FINISH,
     CMD_SET_WINDOW_FORMAT,
     CMD_SET_WINDOW_FLAGS,
     CMD_SHOW_SOFT_INPUT,
@@ -512,6 +514,11 @@
     sp<Looper> looper;
 };
 
+void android_NativeActivity_finish(ANativeActivity* activity) {
+    NativeCode* code = static_cast<NativeCode*>(activity);
+    write_work(code->mainWorkWrite, CMD_FINISH, 0);
+}
+
 void android_NativeActivity_setWindowFormat(
         ANativeActivity* activity, int32_t format) {
     NativeCode* code = static_cast<NativeCode*>(activity);
@@ -538,6 +545,16 @@
 
 // ------------------------------------------------------------------------
 
+static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+   if (env->ExceptionCheck()) {
+       LOGE("An exception was thrown by callback '%s'.", methodName);
+       LOGE_EX(env);
+       env->ExceptionClear();
+       return true;
+   }
+   return false;
+}
+
 /*
  * Callback for handling native events on the application's main thread.
  */
@@ -562,6 +579,7 @@
                         code->env, keyEvent);
                 code->env->CallVoidMethod(code->clazz,
                         gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
+                checkAndClearExceptionFromCallback(code->env, "dispatchUnhandledKeyEvent");
                 code->nativeInputQueue->finishEvent(keyEvent, true);
             }
             int seq;
@@ -570,23 +588,32 @@
                         code->env, keyEvent);
                 code->env->CallVoidMethod(code->clazz,
                         gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq);
+                checkAndClearExceptionFromCallback(code->env, "preDispatchKeyEvent");
             }
         } break;
+        case CMD_FINISH: {
+            code->env->CallVoidMethod(code->clazz, gNativeActivityClassInfo.finish);
+            checkAndClearExceptionFromCallback(code->env, "finish");
+        } break;
         case CMD_SET_WINDOW_FORMAT: {
             code->env->CallVoidMethod(code->clazz,
                     gNativeActivityClassInfo.setWindowFormat, work.arg1);
+            checkAndClearExceptionFromCallback(code->env, "setWindowFormat");
         } break;
         case CMD_SET_WINDOW_FLAGS: {
             code->env->CallVoidMethod(code->clazz,
                     gNativeActivityClassInfo.setWindowFlags, work.arg1, work.arg2);
+            checkAndClearExceptionFromCallback(code->env, "setWindowFlags");
         } break;
         case CMD_SHOW_SOFT_INPUT: {
             code->env->CallVoidMethod(code->clazz,
                     gNativeActivityClassInfo.showIme, work.arg1);
+            checkAndClearExceptionFromCallback(code->env, "showIme");
         } break;
         case CMD_HIDE_SOFT_INPUT: {
             code->env->CallVoidMethod(code->clazz,
                     gNativeActivityClassInfo.hideIme, work.arg1);
+            checkAndClearExceptionFromCallback(code->env, "hideIme");
         } break;
         default:
             LOGW("Unknown work command: %d", work.cmd);
@@ -1018,6 +1045,9 @@
             gNativeActivityClassInfo.clazz,
             "preDispatchKeyEvent", "(Landroid/view/KeyEvent;I)V");
 
+    GET_METHOD_ID(gNativeActivityClassInfo.finish,
+            gNativeActivityClassInfo.clazz,
+            "finish", "()V");
     GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags,
             gNativeActivityClassInfo.clazz,
             "setWindowFlags", "(II)V");
diff --git a/include/android_runtime/android_app_NativeActivity.h b/include/android_runtime/android_app_NativeActivity.h
index b49e02a..5dbec59 100644
--- a/include/android_runtime/android_app_NativeActivity.h
+++ b/include/android_runtime/android_app_NativeActivity.h
@@ -26,6 +26,9 @@
 
 namespace android {
 
+extern void android_NativeActivity_finish(
+        ANativeActivity* activity);
+
 extern void android_NativeActivity_setWindowFormat(
         ANativeActivity* activity, int32_t format);
 
diff --git a/native/android/native_activity.cpp b/native/android/native_activity.cpp
index 0c6823a..056255f 100644
--- a/native/android/native_activity.cpp
+++ b/native/android/native_activity.cpp
@@ -21,6 +21,10 @@
 
 using namespace android;
 
+void ANativeActivity_finish(ANativeActivity* activity) {
+    android_NativeActivity_finish(activity);
+}
+
 void ANativeActivity_setWindowFormat(ANativeActivity* activity, int32_t format) {
 	android_NativeActivity_setWindowFormat(activity, format);
 }
diff --git a/native/include/android/native_activity.h b/native/include/android/native_activity.h
index d74e1ce..a8f11c9 100644
--- a/native/include/android/native_activity.h
+++ b/native/include/android/native_activity.h
@@ -227,6 +227,12 @@
  */
 extern ANativeActivity_createFunc ANativeActivity_onCreate;
 
+/**
+ * Finish the given activity.  Its finish() method will be called, causing it
+ * to be stopped and destroyed.
+ */
+void ANativeActivity_finish(ANativeActivity* activity);
+
 void ANativeActivity_setWindowFormat(ANativeActivity* activity, int32_t format);
 
 void ANativeActivity_setWindowFlags(ANativeActivity* activity,