Introduce intent manager.
The intent manager is an Android specific service that allows privileged
android mojo application to listen to external intents.
R=ppi@chromium.org
Review URL: https://codereview.chromium.org/1061313003
diff --git a/mojo/public/java/BUILD.gn b/mojo/public/java/BUILD.gn
index d2ee4cd..4abfc3c 100644
--- a/mojo/public/java/BUILD.gn
+++ b/mojo/public/java/BUILD.gn
@@ -62,6 +62,7 @@
"application/src/org/chromium/mojo/application/ApplicationImpl.java",
"application/src/org/chromium/mojo/application/ApplicationRunner.java",
"application/src/org/chromium/mojo/application/ServiceFactoryBinder.java",
+ "application/src/org/chromium/mojo/application/ShellHelper.java",
]
deps = [
":bindings",
diff --git a/mojo/public/java/application/src/org/chromium/mojo/application/ShellHelper.java b/mojo/public/java/application/src/org/chromium/mojo/application/ShellHelper.java
new file mode 100644
index 0000000..705e22e
--- /dev/null
+++ b/mojo/public/java/application/src/org/chromium/mojo/application/ShellHelper.java
@@ -0,0 +1,40 @@
+// 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.application;
+
+import org.chromium.mojo.bindings.Interface;
+import org.chromium.mojo.bindings.Interface.Proxy;
+import org.chromium.mojo.bindings.InterfaceRequest;
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.Pair;
+import org.chromium.mojom.mojo.ServiceProvider;
+import org.chromium.mojom.mojo.Shell;
+
+/**
+ * Helper class to help connecting to other application through the shell.
+ */
+public class ShellHelper {
+ /**
+ * Connects to a service in another application.
+ *
+ * @param core Implementation of the {@link Core} api.
+ * @param shell Instance of the shell.
+ * @param application URL to the application to use.
+ * @param manager {@link org.chromium.mojo.bindings.Interface.Manager} for the service to
+ * connect to.
+ * @return a proxy to the service.
+ */
+ public static <I extends Interface, P extends Proxy> P connectToService(
+ Core core, Shell shell, String application, Interface.Manager<I, P> manager) {
+ Pair<ServiceProvider.Proxy, InterfaceRequest<ServiceProvider>> providerRequest =
+ ServiceProvider.MANAGER.getInterfaceRequest(core);
+ try (ServiceProvider.Proxy provider = providerRequest.first) {
+ shell.connectToApplication(application, providerRequest.second, null);
+ Pair<P, InterfaceRequest<I>> serviceRequest = manager.getInterfaceRequest(core);
+ provider.connectToService(manager.getName(), serviceRequest.second.passHandle());
+ return serviceRequest.first;
+ }
+ }
+}
diff --git a/services/android/BUILD.gn b/services/android/BUILD.gn
index 588b0f0..b4401be 100644
--- a/services/android/BUILD.gn
+++ b/services/android/BUILD.gn
@@ -4,6 +4,7 @@
import("//build/config/android/rules.gni")
import("//mojo/public/mojo_application.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
shared_library("libjava_handler") {
deps = [
@@ -60,3 +61,9 @@
input_so = "$root_out_dir/lib.stripped/libjava_handler.so"
input_dex_jar = dex_output_path
}
+
+mojom("bindings") {
+ sources = [
+ "intent_receiver.mojom",
+ ]
+}
diff --git a/services/android/intent_receiver.mojom b/services/android/intent_receiver.mojom
new file mode 100644
index 0000000..a61df69
--- /dev/null
+++ b/services/android/intent_receiver.mojom
@@ -0,0 +1,24 @@
+// 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.
+
+[JavaPackage="org.chromium.mojo.intent"]
+module intent_receiver;
+
+// Service to interact with android intents.
+interface IntentReceiverManager {
+ // This method takes an |IntentReceiver| and returns a serialized intent.
+ // The serialized intent can be deserialized using an android parcel. The
+ // caller can then transform this intent into a PendingIntent using
+ // |PendingIntent#getService| and send it to another android application.
+ // Whenever the pending intent is executed, the receiver will be called with
+ // the content of the received intent. To be noted: this will fail if the
+ // received intent is active (contains either a Binder or a file descriptor).
+ RegisterReceiver(IntentReceiver receiver) => (array<uint8> intent);
+};
+
+// Receiver interface, to be used with
+// |IntentReceiverManager.RegisterReceiver|.
+interface IntentReceiver {
+ OnIntent(array<uint8> intent);
+};
diff --git a/shell/BUILD.gn b/shell/BUILD.gn
index b84cba0..3b19445 100644
--- a/shell/BUILD.gn
+++ b/shell/BUILD.gn
@@ -178,6 +178,10 @@
"android/android_handler_loader.h",
"android/background_application_loader.cc",
"android/background_application_loader.h",
+ "android/intent_receiver_manager_factory.cc",
+ "android/intent_receiver_manager_factory.h",
+ "android/intent_receiver_manager_impl.cc",
+ "android/intent_receiver_manager_impl.h",
"android/keyboard_impl.cc",
"android/keyboard_impl.h",
"android/native_viewport_application_loader.cc",
@@ -191,6 +195,7 @@
":run_android_application_function",
"//mojo/application:content_handler",
"//mojo/services/keyboard/public/interfaces",
+ "//services/android:bindings",
"//services/gles2",
"//services/native_viewport:lib",
]
@@ -245,6 +250,7 @@
sources = [
"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/IntentReceiverRegistry.java",
"android/apk/src/org/chromium/mojo/shell/Keyboard.java",
"android/apk/src/org/chromium/mojo/shell/ShellMain.java",
"android/tests/src/org/chromium/mojo/shell/ShellTestBase.java",
@@ -288,7 +294,9 @@
android_library("java") {
java_files = [
"android/apk/src/org/chromium/mojo/shell/AndroidHandler.java",
+ "android/apk/src/org/chromium/mojo/shell/IntentReceiverService.java",
"android/apk/src/org/chromium/mojo/shell/FileHelper.java",
+ "android/apk/src/org/chromium/mojo/shell/IntentReceiverRegistry.java",
"android/apk/src/org/chromium/mojo/shell/Keyboard.java",
"android/apk/src/org/chromium/mojo/shell/MojoShellActivity.java",
"android/apk/src/org/chromium/mojo/shell/MojoShellApplication.java",
diff --git a/shell/android/android_handler.cc b/shell/android/android_handler.cc
index a29759d..47b95c7 100644
--- a/shell/android/android_handler.cc
+++ b/shell/android/android_handler.cc
@@ -100,6 +100,7 @@
bool AndroidHandler::ConfigureIncomingConnection(
ApplicationConnection* connection) {
connection->AddService(&content_handler_factory_);
+ connection->AddService(&intent_receiver_manager_factory_);
return true;
}
diff --git a/shell/android/android_handler.h b/shell/android/android_handler.h
index 1e1ad96..3e7d98c 100644
--- a/shell/android/android_handler.h
+++ b/shell/android/android_handler.h
@@ -11,6 +11,7 @@
#include "mojo/public/cpp/application/application_delegate.h"
#include "mojo/public/cpp/application/interface_factory_impl.h"
#include "mojo/services/content_handler/public/interfaces/content_handler.mojom.h"
+#include "shell/android/intent_receiver_manager_factory.h"
namespace base {
class FilePath;
@@ -35,6 +36,8 @@
URLResponsePtr response) override;
ContentHandlerFactory content_handler_factory_;
+ IntentReceiverManagerFactory intent_receiver_manager_factory_;
+
MOJO_DISALLOW_COPY_AND_ASSIGN(AndroidHandler);
};
diff --git a/shell/android/apk/AndroidManifest.xml b/shell/android/apk/AndroidManifest.xml
index 9e368b9..ff3a3d2 100644
--- a/shell/android/apk/AndroidManifest.xml
+++ b/shell/android/apk/AndroidManifest.xml
@@ -27,6 +27,7 @@
<action android:name="android.intent.action.VIEW"/>
</intent-filter>
</activity>
+ <service android:name=".IntentReceiverService"/>
</application>
</manifest>
diff --git a/shell/android/apk/src/org/chromium/mojo/shell/IntentReceiverRegistry.java b/shell/android/apk/src/org/chromium/mojo/shell/IntentReceiverRegistry.java
new file mode 100644
index 0000000..ba9ffda
--- /dev/null
+++ b/shell/android/apk/src/org/chromium/mojo/shell/IntentReceiverRegistry.java
@@ -0,0 +1,84 @@
+// 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.content.Intent;
+import android.os.Parcel;
+
+import org.chromium.base.ApplicationStatus;
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * Java helper class for services/android/intent_receiver.mojom. This class creates intents for
+ * privileged mojo applications and routes received intents back to the mojo application. The
+ * implementation of the IntentReceiverManager service is in C++, but calls back to this class to
+ * access the android framework.
+ */
+@JNINamespace("mojo::shell")
+public class IntentReceiverRegistry {
+ private static class LazyHolder {
+ private static final IntentReceiverRegistry INSTANCE = new IntentReceiverRegistry();
+ }
+
+ /**
+ * Returns the instance.
+ */
+ public static IntentReceiverRegistry getInstance() {
+ return LazyHolder.INSTANCE;
+ }
+
+ private final Map<String, Long> mReceiversByUuid = new HashMap<>();
+ private final Map<Long, String> mUuidsByReceiver = new HashMap<>();
+
+ private IntentReceiverRegistry() {}
+
+ public void onIntentReceived(Intent intent) {
+ String uuid = intent.getAction();
+ if (uuid == null) return;
+ Long ptr = mReceiversByUuid.get(uuid);
+ if (ptr == null) return;
+ nativeOnIntentReceived(ptr, intentToBuffer(intent));
+ }
+
+ private static ByteBuffer intentToBuffer(Intent intent) {
+ Parcel p = Parcel.obtain();
+ intent.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ byte[] b = p.marshall();
+ ByteBuffer result = ByteBuffer.allocateDirect(b.length);
+ result.put(b);
+ result.flip();
+ return result;
+ }
+
+ @CalledByNative
+ private static ByteBuffer registerReceiver(long intentDispatcher) {
+ IntentReceiverRegistry registry = getInstance();
+ String uuid = UUID.randomUUID().toString();
+ Intent intent = new Intent(
+ uuid, null, ApplicationStatus.getApplicationContext(), IntentReceiverService.class);
+ registry.mReceiversByUuid.put(uuid, intentDispatcher);
+ registry.mUuidsByReceiver.put(intentDispatcher, uuid);
+ return intentToBuffer(intent);
+ }
+
+ @CalledByNative
+ private static void unregisterReceiver(long intentDispatcher) {
+ IntentReceiverRegistry registry = getInstance();
+ String uuid = registry.mUuidsByReceiver.get(intentDispatcher);
+ if (uuid != null) {
+ registry.mUuidsByReceiver.remove(intentDispatcher);
+ registry.mReceiversByUuid.remove(uuid);
+ }
+ }
+
+ private static native void nativeOnIntentReceived(long intentDispatcher, ByteBuffer intent);
+}
diff --git a/shell/android/apk/src/org/chromium/mojo/shell/IntentReceiverService.java b/shell/android/apk/src/org/chromium/mojo/shell/IntentReceiverService.java
new file mode 100644
index 0000000..0a7a3ad
--- /dev/null
+++ b/shell/android/apk/src/org/chromium/mojo/shell/IntentReceiverService.java
@@ -0,0 +1,25 @@
+// 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.app.IntentService;
+import android.content.Intent;
+
+/**
+ * IntentService that will forward received intents to the {@link IntentReceiverRegistry}.
+ */
+public class IntentReceiverService extends IntentService {
+ public IntentReceiverService() {
+ super("IntentReceiverService");
+ }
+
+ /**
+ * @see IntentService#onHandleIntent(Intent)
+ */
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ IntentReceiverRegistry.getInstance().onIntentReceived(intent);
+ }
+}
diff --git a/shell/android/intent_receiver_manager_factory.cc b/shell/android/intent_receiver_manager_factory.cc
new file mode 100644
index 0000000..94ad166
--- /dev/null
+++ b/shell/android/intent_receiver_manager_factory.cc
@@ -0,0 +1,17 @@
+// 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/intent_receiver_manager_factory.h"
+
+namespace mojo {
+namespace shell {
+
+void IntentReceiverManagerFactory::Create(
+ ApplicationConnection* connection,
+ InterfaceRequest<intent_receiver::IntentReceiverManager> request) {
+ intent_manager_.Bind(request.Pass());
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/shell/android/intent_receiver_manager_factory.h b/shell/android/intent_receiver_manager_factory.h
new file mode 100644
index 0000000..775ce18
--- /dev/null
+++ b/shell/android/intent_receiver_manager_factory.h
@@ -0,0 +1,27 @@
+// 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_INTENT_RECEIVER_MANAGER_FACTORY_H_
+#define SHELL_ANDROID_INTENT_RECEIVER_MANAGER_FACTORY_H_
+
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "services/android/intent_receiver.mojom.h"
+#include "shell/android/intent_receiver_manager_impl.h"
+
+namespace mojo {
+namespace shell {
+class IntentReceiverManagerFactory
+ : public InterfaceFactory<intent_receiver::IntentReceiverManager> {
+ private:
+ // From InterfaceFactory:
+ void Create(ApplicationConnection* connection,
+ InterfaceRequest<intent_receiver::IntentReceiverManager> request)
+ override;
+
+ IntentReceiverManagerImpl intent_manager_;
+};
+} // namespace shell
+} // namespace mojo
+
+#endif // SHELL_ANDROID_INTENT_RECEIVER_MANAGER_FACTORY_H_
diff --git a/shell/android/intent_receiver_manager_impl.cc b/shell/android/intent_receiver_manager_impl.cc
new file mode 100644
index 0000000..2a89191
--- /dev/null
+++ b/shell/android/intent_receiver_manager_impl.cc
@@ -0,0 +1,82 @@
+// 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/intent_receiver_manager_impl.h"
+
+#include "base/android/jni_android.h"
+#include "jni/IntentReceiverRegistry_jni.h"
+#include "mojo/public/cpp/bindings/error_handler.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+
+mojo::Array<uint8> BufferToArray(JNIEnv* env, jobject buffer) {
+ const size_t size = env->GetDirectBufferCapacity(buffer);
+ Array<uint8> result(size);
+ memcpy(&result.front(), env->GetDirectBufferAddress(buffer), size);
+ return result.Pass();
+}
+
+class IntentDispatcher : public ErrorHandler {
+ public:
+ IntentDispatcher(intent_receiver::IntentReceiverPtr intent_receiver)
+ : intent_receiver_(intent_receiver.Pass()) {
+ intent_receiver_.set_error_handler(this);
+ }
+
+ ~IntentDispatcher() {
+ Java_IntentReceiverRegistry_unregisterReceiver(
+ base::android::AttachCurrentThread(),
+ reinterpret_cast<uintptr_t>(this));
+ }
+
+ void OnIntentReceived(JNIEnv* env, jobject intent) {
+ intent_receiver_->OnIntent(BufferToArray(env, intent));
+ }
+
+ private:
+ // Overriden from ErrorHandler
+ void OnConnectionError() {
+ intent_receiver_.set_error_handler(nullptr);
+ delete this;
+ }
+
+ intent_receiver::IntentReceiverPtr intent_receiver_;
+};
+
+} // namespace
+
+void IntentReceiverManagerImpl::Bind(
+ InterfaceRequest<intent_receiver::IntentReceiverManager> request) {
+ bindings_.AddBinding(this, request.Pass());
+}
+
+void IntentReceiverManagerImpl::RegisterReceiver(
+ intent_receiver::IntentReceiverPtr receiver,
+ const RegisterReceiverCallback& callback) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ base::android::ScopedJavaLocalRef<jobject> buffer =
+ Java_IntentReceiverRegistry_registerReceiver(
+ env,
+ reinterpret_cast<uintptr_t>(new IntentDispatcher(receiver.Pass())));
+ callback.Run(BufferToArray(env, buffer.obj()));
+}
+
+bool RegisterIntentReceiverRegistry(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+void OnIntentReceived(JNIEnv* env,
+ jclass jcaller,
+ jlong intent_dispatcher_ptr,
+ jobject intent) {
+ IntentDispatcher* intent_dispatcher =
+ reinterpret_cast<IntentDispatcher*>(intent_dispatcher_ptr);
+ intent_dispatcher->OnIntentReceived(env, intent);
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/shell/android/intent_receiver_manager_impl.h b/shell/android/intent_receiver_manager_impl.h
new file mode 100644
index 0000000..769eee5
--- /dev/null
+++ b/shell/android/intent_receiver_manager_impl.h
@@ -0,0 +1,33 @@
+// 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_INTENT_RECEIVER_MANAGER_IMPL_H_
+#define SHELL_ANDROID_INTENT_RECEIVER_MANAGER_IMPL_H_
+
+#include "base/android/jni_android.h"
+#include "mojo/common/weak_binding_set.h"
+#include "services/android/intent_receiver.mojom.h"
+
+namespace mojo {
+namespace shell {
+
+class IntentReceiverManagerImpl
+ : public intent_receiver::IntentReceiverManager {
+ public:
+ void Bind(InterfaceRequest<intent_receiver::IntentReceiverManager> request);
+
+ private:
+ // Implementation of intent_receiver::IntentReceiverManager
+ void RegisterReceiver(intent_receiver::IntentReceiverPtr receiver,
+ const RegisterReceiverCallback& callback) override;
+
+ WeakBindingSet<intent_receiver::IntentReceiverManager> bindings_;
+};
+
+bool RegisterIntentReceiverRegistry(JNIEnv* env);
+
+} // namespace shell
+} // namespace mojo
+
+#endif // SHELL_ANDROID_INTENT_RECEIVER_MANAGER_IMPL_H_
diff --git a/shell/android/library_loader.cc b/shell/android/library_loader.cc
index d5230ac..8479b2f 100644
--- a/shell/android/library_loader.cc
+++ b/shell/android/library_loader.cc
@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "services/native_viewport/platform_viewport_android.h"
#include "shell/android/android_handler.h"
+#include "shell/android/intent_receiver_manager_impl.h"
#include "shell/android/keyboard_impl.h"
#include "shell/android/main.h"
@@ -16,6 +17,7 @@
base::android::RegistrationMethod kMojoRegisteredMethods[] = {
{"AndroidHandler", mojo::shell::RegisterAndroidHandlerJni},
+ {"IntentReceiverRegistry", mojo::shell::RegisterIntentReceiverRegistry},
{"Keyboard", mojo::shell::RegisterKeyboardJni},
{"PlatformViewportAndroid",
native_viewport::PlatformViewportAndroid::Register},