Add mojo service to get notified of vsync.
R=ppi@chromium.org
BUG=Fixes https://github.com/domokit/mojo/issues/535
Review URL: https://codereview.chromium.org/1458453003 .
diff --git a/mojo/services/vsync/interfaces/BUILD.gn b/mojo/services/vsync/interfaces/BUILD.gn
new file mode 100644
index 0000000..087262b
--- /dev/null
+++ b/mojo/services/vsync/interfaces/BUILD.gn
@@ -0,0 +1,12 @@
+# 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.
+
+import("//build/module_args/mojo.gni")
+import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
+
+mojom("interfaces") {
+ sources = [
+ "vsync.mojom",
+ ]
+}
diff --git a/mojo/services/vsync/interfaces/vsync.mojom b/mojo/services/vsync/interfaces/vsync.mojom
new file mode 100644
index 0000000..5cc66a1
--- /dev/null
+++ b/mojo/services/vsync/interfaces/vsync.mojom
@@ -0,0 +1,13 @@
+// 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.
+
+[DartPackage="mojo_services"]
+module vsync;
+
+interface VSyncProvider {
+ // Waits for the next vsync and returns its timestamp once it happens. The
+ // timestamps can only be compared with other calls of this method.
+ // Only one callback can be parked at a given time.
+ AwaitVSync() => (int64 time_stamp);
+};
diff --git a/services/vsync/BUILD.gn b/services/vsync/BUILD.gn
new file mode 100644
index 0000000..fa46639
--- /dev/null
+++ b/services/vsync/BUILD.gn
@@ -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.
+
+import("//build/config/android/config.gni")
+import("//build/config/android/rules.gni")
+
+android_library("vsync") {
+ java_files = [ "src/org/chromium/mojo/vsync/VSyncProviderImpl.java" ]
+
+ deps = [
+ "//mojo/public/java:bindings",
+ "//mojo/public/java:system",
+ "//mojo/services/vsync/interfaces:interfaces_java",
+ ]
+}
diff --git a/services/vsync/src/org/chromium/mojo/vsync/VSyncProviderImpl.java b/services/vsync/src/org/chromium/mojo/vsync/VSyncProviderImpl.java
new file mode 100644
index 0000000..adca2bc
--- /dev/null
+++ b/services/vsync/src/org/chromium/mojo/vsync/VSyncProviderImpl.java
@@ -0,0 +1,81 @@
+// 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.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 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 void setBinding(Binding binding) {
+ if (mBinding != null) {
+ mBinding.unbind().close();
+ }
+ mBinding = binding;
+ }
+
+ @Override
+ public void close() {}
+
+ @Override
+ public void onConnectionError(MojoException e) {}
+
+ @Override
+ public void awaitVSync(final AwaitVSyncResponse callback) {
+ if (mCallback != null) {
+ setBinding(null);
+ return;
+ }
+ mCallback = callback;
+ if (mChoreographer != null) {
+ // Posting from another thread is allowed on a choreographer.
+ 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);
+ }
+}
diff --git a/shell/BUILD.gn b/shell/BUILD.gn
index a09eb67..981df80 100644
--- a/shell/BUILD.gn
+++ b/shell/BUILD.gn
@@ -351,6 +351,7 @@
"android/apk/src/org/chromium/mojo/shell/SharingApplicationDelegate.java",
"android/apk/src/org/chromium/mojo/shell/ShellService.java",
"android/apk/src/org/chromium/mojo/shell/ViewportActivity.java",
+ "android/apk/src/org/chromium/mojo/shell/VsyncFactory.java",
]
deps = [
@@ -364,6 +365,7 @@
"//mojo/services/input/interfaces:interfaces_java",
"//mojo/services/keyboard/interfaces:interfaces_java",
"//mojo/services/nfc/interfaces:interfaces_java",
+ "//mojo/services/vsync/interfaces:interfaces_java",
"//services/authentication",
"//services/input",
"//services/intent_receiver:bindings_java",
@@ -373,6 +375,7 @@
"//services/native_viewport:native_viewport_java",
"//services/nfc_message_sink:bindings_java",
"//services/sharing_sink:bindings_java",
+ "//services/vsync",
"//third_party/android_tools:android_support_v13_java",
]
}
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 b0e81db..67241c1 100644
--- a/shell/android/apk/src/org/chromium/mojo/shell/JavaApplicationRegistry.java
+++ b/shell/android/apk/src/org/chromium/mojo/shell/JavaApplicationRegistry.java
@@ -4,6 +4,8 @@
package org.chromium.mojo.shell;
+import android.os.HandlerThread;
+
import org.chromium.base.ApplicationStatus;
import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
@@ -30,6 +32,8 @@
@JNINamespace("shell")
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 static final class ApplicationRunnable implements Runnable {
private final ApplicationDelegate mApplicationDelegate;
@@ -51,7 +55,9 @@
}
}
- private JavaApplicationRegistry() {}
+ private JavaApplicationRegistry() {
+ mHandlerThread.start();
+ }
private void registerApplicationDelegate(String url, ApplicationDelegate applicationDelegate) {
mApplicationDelegateMap.put(url, applicationDelegate);
@@ -97,6 +103,9 @@
registry.registerApplicationDelegate("mojo:sharing", new SharingApplicationDelegate());
registry.registerApplicationDelegate(
"mojo:native_viewport_support", new NativeViewportSupportApplicationDelegate());
+ registry.registerApplicationDelegate(
+ "mojo:vsync", new ServiceProviderFactoryApplicationDelegate(
+ new VsyncFactory(registry.mHandlerThread.getLooper())));
return registry;
}
diff --git a/shell/android/apk/src/org/chromium/mojo/shell/VsyncFactory.java b/shell/android/apk/src/org/chromium/mojo/shell/VsyncFactory.java
new file mode 100644
index 0000000..b159ce2
--- /dev/null
+++ b/shell/android/apk/src/org/chromium/mojo/shell/VsyncFactory.java
@@ -0,0 +1,35 @@
+// 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.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;
+
+/**
+ * A ServiceFactoryBinder for the vsync service.
+ */
+final class VsyncFactory implements ServiceFactoryBinder<VSyncProvider> {
+ private final Looper mLooper;
+
+ public VsyncFactory(Looper looper) {
+ mLooper = looper;
+ }
+
+ @Override
+ public void bind(InterfaceRequest<VSyncProvider> request) {
+ VSyncProviderImpl implementation = new VSyncProviderImpl(CoreImpl.getInstance(), mLooper);
+ implementation.setBinding(VSyncProvider.MANAGER.bind(implementation, request));
+ }
+
+ @Override
+ public String getInterfaceName() {
+ return VSyncProvider.MANAGER.getName();
+ }
+}