Use thread with looper and native message loop for vsync service.

This allows not to switch thread when communicating with the
choreographer.

This CL introduces NativeHandlerThread. This is a HandlerThread
(associated to an android Looper) that is also associated to a native
message loop to be able to receive mojo messages.

R=ppi@chromium.org
BUG=https://github.com/domokit/mojo/issues/535

Review URL: https://codereview.chromium.org/1456913002 .
diff --git a/services/vsync/src/org/chromium/mojo/vsync/VSyncProviderImpl.java b/services/vsync/src/org/chromium/mojo/vsync/VSyncProviderImpl.java
index adca2bc..3c8cc11 100644
--- a/services/vsync/src/org/chromium/mojo/vsync/VSyncProviderImpl.java
+++ b/services/vsync/src/org/chromium/mojo/vsync/VSyncProviderImpl.java
@@ -4,42 +4,21 @@
 
 package org.chromium.mojo.vsync;
 
-import android.os.Handler;
-import android.os.Looper;
 import android.view.Choreographer;
 
-import org.chromium.mojo.system.Core;
 import org.chromium.mojo.system.MojoException;
-import org.chromium.mojo.system.RunLoop;
 import org.chromium.mojom.vsync.VSyncProvider;
 
 /**
  * Android implementation of VSyncProvider.
  */
 public class VSyncProviderImpl implements VSyncProvider, Choreographer.FrameCallback {
-    private final RunLoop mRunLoop;
-    private Choreographer mChoreographer;
+    private final Choreographer mChoreographer;
     private AwaitVSyncResponse mCallback;
     private Binding mBinding = null;
 
-    public VSyncProviderImpl(Core core, Looper looper) {
-        mRunLoop = core.getCurrentRunLoop();
-        // The choreographer must be initialized on a thread with a looper.
-        new Handler(looper).post(new Runnable() {
-            @Override
-            public void run() {
-                final Choreographer choreographer = Choreographer.getInstance();
-                mRunLoop.postDelayedTask(new Runnable() {
-                    @Override
-                    public void run() {
-                        mChoreographer = choreographer;
-                        if (mCallback != null) {
-                            mChoreographer.postFrameCallback(VSyncProviderImpl.this);
-                        }
-                    }
-                }, 0);
-            }
-        });
+    public VSyncProviderImpl() {
+        mChoreographer = Choreographer.getInstance();
     }
 
     public void setBinding(Binding binding) {
@@ -62,20 +41,12 @@
             return;
         }
         mCallback = callback;
-        if (mChoreographer != null) {
-            // Posting from another thread is allowed on a choreographer.
-            mChoreographer.postFrameCallback(this);
-        }
+        mChoreographer.postFrameCallback(this);
     }
 
     @Override
     public void doFrame(final long frameTimeNanos) {
-        mRunLoop.postDelayedTask(new Runnable() {
-            @Override
-            public void run() {
-                mCallback.call(frameTimeNanos / 1000);
-                mCallback = null;
-            }
-        }, 0);
+        mCallback.call(frameTimeNanos / 1000);
+        mCallback = null;
     }
 }
diff --git a/shell/BUILD.gn b/shell/BUILD.gn
index 981df80..16d100f 100644
--- a/shell/BUILD.gn
+++ b/shell/BUILD.gn
@@ -227,6 +227,8 @@
       "android/android_handler_loader.h",
       "android/java_application_loader.cc",
       "android/java_application_loader.h",
+      "android/native_handler_thread.cc",
+      "android/native_handler_thread.h",
       "android/native_viewport_application_loader.cc",
       "android/native_viewport_application_loader.h",
       "android/ui_application_loader_android.cc",
@@ -291,6 +293,7 @@
       "android/apk/src/org/chromium/mojo/shell/AndroidHandler.java",
       "android/apk/src/org/chromium/mojo/shell/Bootstrap.java",
       "android/apk/src/org/chromium/mojo/shell/JavaApplicationRegistry.java",
+      "android/apk/src/org/chromium/mojo/shell/NativeHandlerThread.java",
       "android/apk/src/org/chromium/mojo/shell/ShellService.java",
       "android/tests/src/org/chromium/mojo/shell/ShellTestBase.java",
     ]
@@ -341,6 +344,7 @@
       "android/apk/src/org/chromium/mojo/shell/KeyboardFactory.java",
       "android/apk/src/org/chromium/mojo/shell/MojoShellActivity.java",
       "android/apk/src/org/chromium/mojo/shell/MojoShellApplication.java",
+      "android/apk/src/org/chromium/mojo/shell/NativeHandlerThread.java",
       "android/apk/src/org/chromium/mojo/shell/NativeViewportSupportApplicationDelegate.java",
       "android/apk/src/org/chromium/mojo/shell/NfcApplicationDelegate.java",
       "android/apk/src/org/chromium/mojo/shell/NfcDbManager.java",
diff --git a/shell/android/android_handler.h b/shell/android/android_handler.h
index 468d824..12bba72 100644
--- a/shell/android/android_handler.h
+++ b/shell/android/android_handler.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MOJO_SHELL_ANDROID_CONTENT_HANDLER_H_
-#define MOJO_SHELL_ANDROID_CONTENT_HANDLER_H_
+#ifndef SHELL_ANDROID_ANDROID_HANDLER_H_
+#define SHELL_ANDROID_ANDROID_HANDLER_H_
 
 #include <jni.h>
 
@@ -53,4 +53,4 @@
 
 }  // namespace shell
 
-#endif  // MOJO_SHELL_ANDROID_CONTENT_HANDLER_H_
+#endif  // SHELL_ANDROID_ANDROID_HANDLER_H_
diff --git a/shell/android/apk/src/org/chromium/mojo/shell/JavaApplicationRegistry.java b/shell/android/apk/src/org/chromium/mojo/shell/JavaApplicationRegistry.java
index 67241c1..49b5ff3 100644
--- a/shell/android/apk/src/org/chromium/mojo/shell/JavaApplicationRegistry.java
+++ b/shell/android/apk/src/org/chromium/mojo/shell/JavaApplicationRegistry.java
@@ -33,7 +33,7 @@
 public class JavaApplicationRegistry {
     private final Map<String, ApplicationDelegate> mApplicationDelegateMap = new HashMap<>();
     // Thread with a Looper required to get callbacks from the android framework..
-    private final HandlerThread mHandlerThread = new HandlerThread("FrameworkThread");
+    private final HandlerThread mHandlerThread = new NativeHandlerThread("FrameworkThread");
 
     private static final class ApplicationRunnable implements Runnable {
         private final ApplicationDelegate mApplicationDelegate;
diff --git a/shell/android/apk/src/org/chromium/mojo/shell/NativeHandlerThread.java b/shell/android/apk/src/org/chromium/mojo/shell/NativeHandlerThread.java
new file mode 100644
index 0000000..334372e
--- /dev/null
+++ b/shell/android/apk/src/org/chromium/mojo/shell/NativeHandlerThread.java
@@ -0,0 +1,54 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.shell;
+
+import android.os.HandlerThread;
+
+import org.chromium.base.JNINamespace;
+
+/**
+ * Handler thread associated with a native message loop.
+ */
+@JNINamespace("shell")
+public class NativeHandlerThread extends HandlerThread {
+    // The native message loop.
+    private long mNativeMessageLoop = 0;
+
+    /**
+     * Constructor.
+     */
+    public NativeHandlerThread(String name) {
+        super(name);
+    }
+
+    /**
+     * @see HandlerThread#run()
+     */
+    @Override
+    public void run() {
+        try {
+            super.run();
+        } finally {
+            if (mNativeMessageLoop != 0) {
+                nativeDeleteMessageLoop(mNativeMessageLoop);
+            }
+        }
+    }
+
+    /**
+     * @see HandlerThread#onLooperPrepared()
+     */
+    @Override
+    protected void onLooperPrepared() {
+        if (mNativeMessageLoop == 0) {
+            mNativeMessageLoop = nativeCreateMessageLoop();
+        }
+        super.onLooperPrepared();
+    }
+
+    native long nativeCreateMessageLoop();
+
+    native void nativeDeleteMessageLoop(long messageLoop);
+}
diff --git a/shell/android/apk/src/org/chromium/mojo/shell/VsyncFactory.java b/shell/android/apk/src/org/chromium/mojo/shell/VsyncFactory.java
index b159ce2..c050af3 100644
--- a/shell/android/apk/src/org/chromium/mojo/shell/VsyncFactory.java
+++ b/shell/android/apk/src/org/chromium/mojo/shell/VsyncFactory.java
@@ -4,11 +4,11 @@
 
 package org.chromium.mojo.shell;
 
+import android.os.Handler;
 import android.os.Looper;
 
 import org.chromium.mojo.application.ServiceFactoryBinder;
 import org.chromium.mojo.bindings.InterfaceRequest;
-import org.chromium.mojo.system.impl.CoreImpl;
 import org.chromium.mojo.vsync.VSyncProviderImpl;
 import org.chromium.mojom.vsync.VSyncProvider;
 
@@ -16,16 +16,21 @@
  * A ServiceFactoryBinder for the vsync service.
  */
 final class VsyncFactory implements ServiceFactoryBinder<VSyncProvider> {
-    private final Looper mLooper;
+    private final Handler mHandler;
 
     public VsyncFactory(Looper looper) {
-        mLooper = looper;
+        mHandler = new Handler(looper);
     }
 
     @Override
-    public void bind(InterfaceRequest<VSyncProvider> request) {
-        VSyncProviderImpl implementation = new VSyncProviderImpl(CoreImpl.getInstance(), mLooper);
-        implementation.setBinding(VSyncProvider.MANAGER.bind(implementation, request));
+    public void bind(final InterfaceRequest<VSyncProvider> request) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                VSyncProviderImpl implementation = new VSyncProviderImpl();
+                implementation.setBinding(VSyncProvider.MANAGER.bind(implementation, request));
+            }
+        });
     }
 
     @Override
diff --git a/shell/android/library_loader.cc b/shell/android/library_loader.cc
index 4c4f38a..7a4f870 100644
--- a/shell/android/library_loader.cc
+++ b/shell/android/library_loader.cc
@@ -13,6 +13,7 @@
 #include "shell/android/android_handler.h"
 #include "shell/android/java_application_loader.h"
 #include "shell/android/main.h"
+#include "shell/android/native_handler_thread.h"
 
 namespace {
 
@@ -21,10 +22,11 @@
     {"Core", mojo::android::RegisterCoreImpl},
     {"BaseRunLoop", mojo::android::RegisterBaseRunLoop},
     {"AndroidHandler", shell::RegisterAndroidHandlerJni},
+    {"JavaApplicationLoader", shell::JavaApplicationLoader::RegisterJni},
+    {"NativeHandlerThread", shell::RegisterNativeHandlerThreadJni},
     {"PlatformViewportAndroid",
      native_viewport::PlatformViewportAndroid::Register},
     {"ShellService", shell::RegisterShellService},
-    {"JavaApplicationLoader", shell::JavaApplicationLoader::RegisterJni},
 };
 
 bool RegisterJNI(JNIEnv* env) {
diff --git a/shell/android/native_handler_thread.cc b/shell/android/native_handler_thread.cc
new file mode 100644
index 0000000..9d1df59
--- /dev/null
+++ b/shell/android/native_handler_thread.cc
@@ -0,0 +1,28 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/android/native_handler_thread.h"
+
+#include "base/message_loop/message_loop.h"
+#include "jni/NativeHandlerThread_jni.h"
+
+namespace shell {
+
+jlong CreateMessageLoop(JNIEnv* env, jobject jcaller) {
+  scoped_ptr<base::MessageLoop> loop(new base::MessageLoopForUI);
+  base::MessageLoopForUI::current()->Start();
+  return reinterpret_cast<intptr_t>(loop.release());
+}
+
+void DeleteMessageLoop(JNIEnv* env, jobject jcaller, jlong message_loop) {
+  scoped_ptr<base::MessageLoop> loop(
+      reinterpret_cast<base::MessageLoop*>(message_loop));
+  loop->QuitNow();
+}
+
+bool RegisterNativeHandlerThreadJni(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace shell
diff --git a/shell/android/native_handler_thread.h b/shell/android/native_handler_thread.h
new file mode 100644
index 0000000..a953a9c
--- /dev/null
+++ b/shell/android/native_handler_thread.h
@@ -0,0 +1,16 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_ANDROID_NATIVE_HANDLER_THREAD_H_
+#define SHELL_ANDROID_NATIVE_HANDLER_THREAD_H_
+
+#include <jni.h>
+
+namespace shell {
+
+bool RegisterNativeHandlerThreadJni(JNIEnv* env);
+
+}  // namespace shell
+
+#endif  // SHELL_ANDROID_NATIVE_HANDLER_THREAD_H_