diff --git a/base/android/base_jni_registrar.cc b/base/android/base_jni_registrar.cc index ea4c37bd2adc6a..0645c73030ce96 100644 --- a/base/android/base_jni_registrar.cc +++ b/base/android/base_jni_registrar.cc @@ -8,6 +8,7 @@ #include "base/android/build_info.h" #include "base/android/cpu_features.h" #include "base/android/important_file_writer_android.h" +#include "base/android/java_handler_thread.h" #include "base/android/jni_android.h" #include "base/android/jni_registrar.h" #include "base/android/memory_pressure_listener_android.h" @@ -38,6 +39,7 @@ static RegistrationMethod kBaseRegisteredMethods[] = { base::android::RegisterImportantFileWriterAndroid }, { "MemoryPressureListenerAndroid", base::android::MemoryPressureListenerAndroid::Register }, + { "JavaHandlerThread", base::android::JavaHandlerThread::RegisterBindings }, { "PathService", base::android::RegisterPathService }, { "PathUtils", base::android::RegisterPathUtils }, { "SystemMessageHandler", base::MessagePumpForUI::RegisterBindings }, diff --git a/base/android/java/src/org/chromium/base/JavaHandlerThread.java b/base/android/java/src/org/chromium/base/JavaHandlerThread.java new file mode 100644 index 00000000000000..5f9960e53ce324 --- /dev/null +++ b/base/android/java/src/org/chromium/base/JavaHandlerThread.java @@ -0,0 +1,41 @@ +// Copyright 2013 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.base; + +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; + +/** + * This class is an internal detail of the native counterpart. + * It is instantiated and owned by the native object. + */ +@JNINamespace("base::android") +class JavaHandlerThread { + final HandlerThread mThread; + + private JavaHandlerThread(String name) { + mThread = new HandlerThread(name); + } + + @CalledByNative + private static JavaHandlerThread create(String name) { + return new JavaHandlerThread(name); + } + + @CalledByNative + private void start(final int nativeThread, final int nativeEvent) { + mThread.start(); + new Handler(mThread.getLooper()).post(new Runnable() { + @Override + public void run() { + nativeInitializeThread(nativeThread, nativeEvent); + } + }); + } + + private native void nativeInitializeThread(int nativeJavaHandlerThread, int nativeEvent); +} \ No newline at end of file diff --git a/base/android/java_handler_thread.cc b/base/android/java_handler_thread.cc new file mode 100644 index 00000000000000..0528fe74ba9803 --- /dev/null +++ b/base/android/java_handler_thread.cc @@ -0,0 +1,62 @@ +// Copyright 2013 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 "base/android/java_handler_thread.h" + +#include + +#include "base/android/jni_android.h" +#include "base/android/jni_string.h" +#include "base/message_loop/message_loop.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread_restrictions.h" +#include "jni/JavaHandlerThread_jni.h" + +namespace base { + +namespace android { + +JavaHandlerThread::JavaHandlerThread(const char* name) { + JNIEnv* env = base::android::AttachCurrentThread(); + + java_thread_.Reset(Java_JavaHandlerThread_create( + env, ConvertUTF8ToJavaString(env, name).Release())); +} + +JavaHandlerThread::~JavaHandlerThread() { +} + +void JavaHandlerThread::Start() { + // Check the thread has not already been started. + DCHECK(!message_loop_); + + JNIEnv* env = base::android::AttachCurrentThread(); + base::WaitableEvent initialize_event(false, false); + Java_JavaHandlerThread_start(env, + java_thread_.obj(), + reinterpret_cast(this), + reinterpret_cast(&initialize_event)); + // Wait for thread to be initialized so it is ready to be used when Start + // returns. + base::ThreadRestrictions::ScopedAllowWait wait_allowed; + initialize_event.Wait(); +} + +void JavaHandlerThread::Stop() { +} + +void JavaHandlerThread::InitializeThread(JNIEnv* env, jobject obj, jint event) { + // TYPE_JAVA to get the Android java style message loop. + message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_JAVA)); + static_cast(message_loop_.get())->Start(); + reinterpret_cast(event)->Signal(); +} + +// static +bool JavaHandlerThread::RegisterBindings(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace android +} // namespace base diff --git a/base/android/java_handler_thread.h b/base/android/java_handler_thread.h new file mode 100644 index 00000000000000..9f66d660c84c9f --- /dev/null +++ b/base/android/java_handler_thread.h @@ -0,0 +1,48 @@ +// Copyright 2013 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 BASE_THREADING_JAVA_THREAD_H_ +#define BASE_THREADING_JAVA_THREAD_H_ + +#include + +#include "base/android/scoped_java_ref.h" +#include "base/memory/scoped_ptr.h" + +namespace base { + +class MessageLoop; +class WaitableEvent; + +namespace android { + +// A Java Thread with a native message loop. To run tasks, post them +// to the message loop and they will be scheduled along with Java tasks +// on the thread. +// This is useful for callbacks where the receiver expects a thread +// with a prepared Looper. +class BASE_EXPORT JavaHandlerThread { + public: + JavaHandlerThread(const char* name); + virtual ~JavaHandlerThread(); + + base::MessageLoop* message_loop() const { return message_loop_.get(); } + void Start(); + void Stop(); + + // Called from java on the newly created thread. + // Start() will not return before this methods has finished. + void InitializeThread(JNIEnv* env, jobject obj, jint event); + + static bool RegisterBindings(JNIEnv* env); + + private: + scoped_ptr message_loop_; + ScopedJavaGlobalRef java_thread_; +}; + +} // namespace android +} // namespace base + +#endif // BASE_THREADING_JAVA_THREAD_H_ diff --git a/base/base.gyp b/base/base.gyp index 15776f303094a2..4b92a917e5f604 100644 --- a/base/base.gyp +++ b/base/base.gyp @@ -1160,6 +1160,7 @@ 'android/java/src/org/chromium/base/CpuFeatures.java', 'android/java/src/org/chromium/base/ImportantFileWriterAndroid.java', 'android/java/src/org/chromium/base/MemoryPressureListener.java', + 'android/java/src/org/chromium/base/JavaHandlerThread.java', 'android/java/src/org/chromium/base/PathService.java', 'android/java/src/org/chromium/base/PathUtils.java', 'android/java/src/org/chromium/base/PowerMonitor.java', diff --git a/base/base.gypi b/base/base.gypi index fef37a1dff35e9..bfe5a576583f5d 100644 --- a/base/base.gypi +++ b/base/base.gypi @@ -58,6 +58,8 @@ 'android/jni_string.h', 'android/memory_pressure_listener_android.cc', 'android/memory_pressure_listener_android.h', + 'android/java_handler_thread.cc', + 'android/java_handler_thread.h', 'android/path_service_android.cc', 'android/path_service_android.h', 'android/path_utils.cc', diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc index d2eafbd8489fd3..826c7573ee60e9 100644 --- a/base/message_loop/message_loop.cc +++ b/base/message_loop/message_loop.cc @@ -92,7 +92,7 @@ MessageLoop::MessagePumpFactory* message_pump_for_ui_factory_ = NULL; // time for every task that is added to the MessageLoop incoming queue. bool AlwaysNotifyPump(MessageLoop::Type type) { #if defined(OS_ANDROID) - return type == MessageLoop::TYPE_UI; + return type == MessageLoop::TYPE_UI || type == MessageLoop::TYPE_JAVA; #else return false; #endif @@ -184,6 +184,10 @@ MessageLoop::MessageLoop(Type type) pump_.reset(MESSAGE_PUMP_UI); } else if (type_ == TYPE_IO) { pump_.reset(MESSAGE_PUMP_IO); +#if defined(OS_ANDROID) + } else if (type_ == TYPE_JAVA) { + pump_.reset(MESSAGE_PUMP_UI); +#endif } else { DCHECK_EQ(TYPE_DEFAULT, type_); pump_.reset(new MessagePumpDefault()); diff --git a/base/message_loop/message_loop.h b/base/message_loop/message_loop.h index 6f71a859930b50..f22c9044d1b9d3 100644 --- a/base/message_loop/message_loop.h +++ b/base/message_loop/message_loop.h @@ -109,10 +109,19 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { // This type of ML also supports asynchronous IO. See also // MessageLoopForIO. // + // TYPE_JAVA + // This type of ML is backed by a Java message handler which is responsible + // for running the tasks added to the ML. This is only for use on Android. + // TYPE_JAVA behaves in essence like TYPE_UI, except during construction + // where it does not use the main thread specific pump factory. + // enum Type { TYPE_DEFAULT, TYPE_UI, - TYPE_IO + TYPE_IO, +#if defined(OS_ANDROID) + TYPE_JAVA, +#endif // defined(OS_ANDROID) }; // Normally, it is not necessary to instantiate a MessageLoop. Instead, it diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h index 6490b047fa4615..595b97060e0d12 100644 --- a/base/threading/thread_restrictions.h +++ b/base/threading/thread_restrictions.h @@ -73,6 +73,10 @@ class AutoThread; namespace base { +namespace android { +class JavaHandlerThread; +} + class SequencedWorkerPool; class SimpleThread; class Thread; @@ -185,6 +189,7 @@ class BASE_EXPORT ThreadRestrictions { friend class Thread; friend class ThreadTestHelper; friend class PlatformThread; + friend class android::JavaHandlerThread; // END ALLOWED USAGE. // BEGIN USAGE THAT NEEDS TO BE FIXED. diff --git a/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc b/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc index b10a7ce14ad73e..58791d17ae5d1f 100644 --- a/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc +++ b/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc @@ -290,6 +290,9 @@ class TestMessageLoop : public base::MessageLoop { return BrowserThread::CurrentlyOn(BrowserThread::UI); case base::MessageLoop::TYPE_IO: return BrowserThread::CurrentlyOn(BrowserThread::IO); +#if defined(OS_ANDROID) + case base::MessageLoop::TYPE_JAVA: // fall-through +#endif // defined(OS_ANDROID) case base::MessageLoop::TYPE_DEFAULT: return !BrowserThread::CurrentlyOn(BrowserThread::UI) && !BrowserThread::CurrentlyOn(BrowserThread::IO); diff --git a/content/browser/renderer_host/java/java_bridge_dispatcher_host.cc b/content/browser/renderer_host/java/java_bridge_dispatcher_host.cc index eeb8d96915c866..95a1c6aefe4898 100644 --- a/content/browser/renderer_host/java/java_bridge_dispatcher_host.cc +++ b/content/browser/renderer_host/java/java_bridge_dispatcher_host.cc @@ -4,9 +4,9 @@ #include "content/browser/renderer_host/java/java_bridge_dispatcher_host.h" +#include "base/android/java_handler_thread.h" #include "base/bind.h" #include "base/lazy_instance.h" -#include "base/threading/thread.h" #include "content/browser/renderer_host/java/java_bridge_channel_host.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/child/child_process.h" @@ -17,12 +17,18 @@ #include "content/public/browser/render_process_host.h" #include "third_party/WebKit/public/web/WebBindings.h" +#if !defined(OS_ANDROID) +#error "JavaBridge currently only supports OS_ANDROID" +#endif + namespace content { namespace { -class JavaBridgeThread : public base::Thread { +// The JavaBridge needs to use a Java thread so the callback +// will happen on a thread with a prepared Looper. +class JavaBridgeThread : public base::android::JavaHandlerThread { public: - JavaBridgeThread() : base::Thread("JavaBridge") { + JavaBridgeThread() : base::android::JavaHandlerThread("JavaBridge") { Start(); } virtual ~JavaBridgeThread() {