summaryrefslogtreecommitdiff
path: root/chromium/base/android
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/base/android
downloadqtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz
Initial import.
Diffstat (limited to 'chromium/base/android')
-rw-r--r--chromium/base/android/OWNERS3
-rw-r--r--chromium/base/android/activity_state_list.h16
-rw-r--r--chromium/base/android/activity_status.cc66
-rw-r--r--chromium/base/android/activity_status.h98
-rw-r--r--chromium/base/android/activity_status_unittest.cc128
-rw-r--r--chromium/base/android/base_jni_registrar.cc58
-rw-r--r--chromium/base/android/base_jni_registrar.h21
-rw-r--r--chromium/base/android/build_info.cc78
-rw-r--r--chromium/base/android/build_info.h115
-rw-r--r--chromium/base/android/context_types.cc26
-rw-r--r--chromium/base/android/context_types.h22
-rw-r--r--chromium/base/android/cpu_features.cc26
-rw-r--r--chromium/base/android/cpu_features.h18
-rw-r--r--chromium/base/android/fifo_utils.cc25
-rw-r--r--chromium/base/android/fifo_utils.h32
-rw-r--r--chromium/base/android/important_file_writer_android.cc42
-rw-r--r--chromium/base/android/important_file_writer_android.h18
-rw-r--r--chromium/base/android/java/src/org/chromium/base/AccessedByNative.java20
-rw-r--r--chromium/base/android/java/src/org/chromium/base/ActivityState.template14
-rw-r--r--chromium/base/android/java/src/org/chromium/base/ActivityStatus.java136
-rw-r--r--chromium/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java140
-rw-r--r--chromium/base/android/java/src/org/chromium/base/BuildInfo.java117
-rw-r--r--chromium/base/android/java/src/org/chromium/base/CalledByNative.java23
-rw-r--r--chromium/base/android/java/src/org/chromium/base/CalledByNativeUnchecked.java27
-rw-r--r--chromium/base/android/java/src/org/chromium/base/ChromiumActivity.java49
-rw-r--r--chromium/base/android/java/src/org/chromium/base/ContextTypes.java96
-rw-r--r--chromium/base/android/java/src/org/chromium/base/CpuFeatures.java40
-rw-r--r--chromium/base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java29
-rw-r--r--chromium/base/android/java/src/org/chromium/base/JNINamespace.java20
-rw-r--r--chromium/base/android/java/src/org/chromium/base/JavaHandlerThread.java41
-rw-r--r--chromium/base/android/java/src/org/chromium/base/MemoryPressureLevelList.template12
-rw-r--r--chromium/base/android/java/src/org/chromium/base/MemoryPressureListener.java55
-rw-r--r--chromium/base/android/java/src/org/chromium/base/NativeClassQualifiedName.java25
-rw-r--r--chromium/base/android/java/src/org/chromium/base/ObserverList.java177
-rw-r--r--chromium/base/android/java/src/org/chromium/base/PathService.java24
-rw-r--r--chromium/base/android/java/src/org/chromium/base/PathUtils.java108
-rw-r--r--chromium/base/android/java/src/org/chromium/base/PowerMonitor.java92
-rw-r--r--chromium/base/android/java/src/org/chromium/base/PowerStatusReceiver.java23
-rw-r--r--chromium/base/android/java/src/org/chromium/base/SysUtils.java30
-rw-r--r--chromium/base/android/java/src/org/chromium/base/SystemMessageHandler.java55
-rw-r--r--chromium/base/android/java/src/org/chromium/base/ThreadUtils.java172
-rw-r--r--chromium/base/android/java/src/org/chromium/base/WeakContext.java45
-rw-r--r--chromium/base/android/java_handler_thread.cc62
-rw-r--r--chromium/base/android/java_handler_thread.h48
-rw-r--r--chromium/base/android/javatests/src/org/chromium/base/ContextTypesTest.java43
-rw-r--r--chromium/base/android/javatests/src/org/chromium/base/ObserverListTest.java180
-rw-r--r--chromium/base/android/jni_android.cc323
-rw-r--r--chromium/base/android/jni_android.h132
-rw-r--r--chromium/base/android/jni_android_unittest.cc141
-rw-r--r--chromium/base/android/jni_array.cc186
-rw-r--r--chromium/base/android/jni_array.h86
-rw-r--r--chromium/base/android/jni_array_unittest.cc148
-rw-r--r--chromium/base/android/jni_generator/golden_sample_for_tests_jni.h373
-rw-r--r--chromium/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java288
-rw-r--r--chromium/base/android/jni_generator/jni_generator.gyp67
-rwxr-xr-xchromium/base/android/jni_generator/jni_generator.py1065
-rwxr-xr-xchromium/base/android/jni_generator/jni_generator_tests.py2084
-rw-r--r--chromium/base/android/jni_generator/sample_for_tests.cc106
-rw-r--r--chromium/base/android/jni_generator/sample_for_tests.h46
-rw-r--r--chromium/base/android/jni_helper.cc67
-rw-r--r--chromium/base/android/jni_helper.h41
-rw-r--r--chromium/base/android/jni_registrar.cc30
-rw-r--r--chromium/base/android/jni_registrar.h27
-rw-r--r--chromium/base/android/jni_string.cc99
-rw-r--r--chromium/base/android/jni_string.h45
-rw-r--r--chromium/base/android/jni_string_unittest.cc32
-rw-r--r--chromium/base/android/memory_pressure_listener_android.cc31
-rw-r--r--chromium/base/android/memory_pressure_listener_android.h30
-rw-r--r--chromium/base/android/path_service_android.cc26
-rw-r--r--chromium/base/android/path_service_android.h18
-rw-r--r--chromium/base/android/path_utils.cc67
-rw-r--r--chromium/base/android/path_utils.h48
-rw-r--r--chromium/base/android/path_utils_unittest.cc46
-rw-r--r--chromium/base/android/scoped_java_ref.cc73
-rw-r--r--chromium/base/android/scoped_java_ref.h198
-rw-r--r--chromium/base/android/scoped_java_ref_unittest.cc122
-rw-r--r--chromium/base/android/sys_utils.cc31
-rw-r--r--chromium/base/android/sys_utils.h26
-rw-r--r--chromium/base/android/thread_utils.h16
79 files changed, 8813 insertions, 0 deletions
diff --git a/chromium/base/android/OWNERS b/chromium/base/android/OWNERS
new file mode 100644
index 00000000000..ebc6e2624f0
--- /dev/null
+++ b/chromium/base/android/OWNERS
@@ -0,0 +1,3 @@
+bulach@chromium.org
+joth@chromium.org
+yfriedman@chromium.org
diff --git a/chromium/base/android/activity_state_list.h b/chromium/base/android/activity_state_list.h
new file mode 100644
index 00000000000..43c0f803da3
--- /dev/null
+++ b/chromium/base/android/activity_state_list.h
@@ -0,0 +1,16 @@
+// Copyright (c) 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.
+
+// This file intentionally does not have header guards, it's included
+// inside a macro to generate enum values.
+
+#ifndef DEFINE_ACTIVITY_STATE
+#error "DEFINE_ACTIVITY_STATE should be defined before including this file"
+#endif
+DEFINE_ACTIVITY_STATE(CREATED, 1)
+DEFINE_ACTIVITY_STATE(STARTED, 2)
+DEFINE_ACTIVITY_STATE(RESUMED, 3)
+DEFINE_ACTIVITY_STATE(PAUSED, 4)
+DEFINE_ACTIVITY_STATE(STOPPED, 5)
+DEFINE_ACTIVITY_STATE(DESTROYED, 6)
diff --git a/chromium/base/android/activity_status.cc b/chromium/base/android/activity_status.cc
new file mode 100644
index 00000000000..4d0be32ef93
--- /dev/null
+++ b/chromium/base/android/activity_status.cc
@@ -0,0 +1,66 @@
+// 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/activity_status.h"
+
+#include <jni.h>
+
+#include "base/memory/singleton.h"
+#include "jni/ActivityStatus_jni.h"
+
+namespace base {
+namespace android {
+
+ActivityStatus::Listener::Listener(
+ const ActivityStatus::StateChangeCallback& callback)
+ : callback_(callback) {
+ ActivityStatus::GetInstance()->RegisterListener(this);
+}
+
+ActivityStatus::Listener::~Listener() {
+ ActivityStatus::GetInstance()->UnregisterListener(this);
+}
+
+void ActivityStatus::Listener::Notify(ActivityState state) {
+ callback_.Run(state);
+}
+
+// static
+ActivityStatus* ActivityStatus::GetInstance() {
+ return Singleton<ActivityStatus,
+ LeakySingletonTraits<ActivityStatus> >::get();
+}
+
+static void OnActivityStateChange(JNIEnv* env, jclass clazz, int new_state) {
+ ActivityStatus* activity_status = ActivityStatus::GetInstance();
+ ActivityState activity_state = static_cast<ActivityState>(new_state);
+ activity_status->OnActivityStateChange(activity_state);
+}
+
+bool ActivityStatus::RegisterBindings(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+ActivityStatus::ActivityStatus()
+ : observers_(new ObserverListThreadSafe<Listener>()) {
+ Java_ActivityStatus_registerThreadSafeNativeStateListener(
+ base::android::AttachCurrentThread());
+}
+
+ActivityStatus::~ActivityStatus() {}
+
+void ActivityStatus::RegisterListener(Listener* listener) {
+ observers_->AddObserver(listener);
+}
+
+void ActivityStatus::UnregisterListener(Listener* listener) {
+ observers_->RemoveObserver(listener);
+}
+
+void ActivityStatus::OnActivityStateChange(ActivityState new_state) {
+ observers_->Notify(&ActivityStatus::Listener::Notify, new_state);
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/activity_status.h b/chromium/base/android/activity_status.h
new file mode 100644
index 00000000000..7975a789cd0
--- /dev/null
+++ b/chromium/base/android/activity_status.h
@@ -0,0 +1,98 @@
+// 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_ANDROID_ACTIVITY_STATUS_H_
+#define BASE_ANDROID_ACTIVITY_STATUS_H_
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/singleton.h"
+#include "base/observer_list_threadsafe.h"
+
+namespace base {
+namespace android {
+
+// Define activity state values like ACTIVITY_STATE_CREATED in a
+// way that ensures they're always the same than their Java counterpart.
+enum ActivityState {
+#define DEFINE_ACTIVITY_STATE(x, y) ACTIVITY_STATE_##x = y,
+#include "base/android/activity_state_list.h"
+#undef DEFINE_ACTIVITY_STATE
+};
+
+// A native helper class to listen to state changes of the current
+// Android Activity. This mirrors org.chromium.base.ActivityStatus.
+// any thread.
+//
+// To start listening, create a new instance, passing a callback to a
+// function that takes an ActivityState parameter. To stop listening,
+// simply delete the listener object. The implementation guarantees
+// that the callback will always be called on the thread that created
+// the listener.
+//
+// Example:
+//
+// void OnActivityStateChange(ActivityState state) {
+// ...
+// }
+//
+// // Start listening.
+// ActivityStatus::Listener* my_listener =
+// new ActivityStatus::Listener(base::Bind(&OnActivityStateChange));
+//
+// ...
+//
+// // Stop listening.
+// delete my_listener
+//
+class BASE_EXPORT ActivityStatus {
+ public:
+ typedef base::Callback<void(ActivityState)> StateChangeCallback;
+
+ class Listener {
+ public:
+ explicit Listener(const StateChangeCallback& callback);
+ ~Listener();
+
+ private:
+ friend class ActivityStatus;
+
+ void Notify(ActivityState state);
+
+ StateChangeCallback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(Listener);
+ };
+
+ // NOTE: The Java ActivityStatus is a singleton too.
+ static ActivityStatus* GetInstance();
+
+ // Internal use: must be public to be called from base_jni_registrar.cc
+ static bool RegisterBindings(JNIEnv* env);
+
+ // Internal use only: must be public to be called from JNI and unit tests.
+ void OnActivityStateChange(ActivityState new_state);
+
+ private:
+ friend struct DefaultSingletonTraits<ActivityStatus>;
+
+ ActivityStatus();
+ ~ActivityStatus();
+
+ void RegisterListener(Listener* listener);
+ void UnregisterListener(Listener* listener);
+
+ scoped_refptr<ObserverListThreadSafe<Listener> > observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(ActivityStatus);
+};
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_ACTIVITY_STATUS_H_
diff --git a/chromium/base/android/activity_status_unittest.cc b/chromium/base/android/activity_status_unittest.cc
new file mode 100644
index 00000000000..3eb0d10f736
--- /dev/null
+++ b/chromium/base/android/activity_status_unittest.cc
@@ -0,0 +1,128 @@
+// 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/activity_status.h"
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace android {
+
+namespace {
+
+using base::android::ScopedJavaLocalRef;
+
+// An invalid ActivityState value.
+const ActivityState kInvalidActivityState = static_cast<ActivityState>(100);
+
+// Used to generate a callback that stores the new state at a given location.
+void StoreStateTo(ActivityState* target, ActivityState state) {
+ *target = state;
+}
+
+void RunTasksUntilIdle() {
+ RunLoop run_loop;
+ run_loop.RunUntilIdle();
+}
+
+// Shared state for the multi-threaded test.
+// This uses a thread to register for events and listen to them, while state
+// changes are forced on the main thread.
+class MultiThreadedTest {
+ public:
+ MultiThreadedTest()
+ : activity_status_(ActivityStatus::GetInstance()),
+ state_(kInvalidActivityState),
+ event_(false, false),
+ thread_("ActivityStatusTest thread"),
+ main_() {
+ }
+
+ void Run() {
+ // Start the thread and tell it to register for events.
+ thread_.Start();
+ thread_.message_loop()
+ ->PostTask(FROM_HERE,
+ base::Bind(&MultiThreadedTest::RegisterThreadForEvents,
+ base::Unretained(this)));
+
+ // Wait for its completion.
+ event_.Wait();
+
+ // Change state, then wait for the thread to modify state.
+ activity_status_->OnActivityStateChange(ACTIVITY_STATE_CREATED);
+ event_.Wait();
+ EXPECT_EQ(ACTIVITY_STATE_CREATED, state_);
+
+ // Again
+ activity_status_->OnActivityStateChange(ACTIVITY_STATE_DESTROYED);
+ event_.Wait();
+ EXPECT_EQ(ACTIVITY_STATE_DESTROYED, state_);
+ }
+
+ private:
+ void ExpectOnThread() {
+ EXPECT_EQ(thread_.message_loop(), base::MessageLoop::current());
+ }
+
+ void RegisterThreadForEvents() {
+ ExpectOnThread();
+ listener_.reset(new ActivityStatus::Listener(base::Bind(
+ &MultiThreadedTest::StoreStateAndSignal, base::Unretained(this))));
+ EXPECT_TRUE(listener_.get());
+ event_.Signal();
+ }
+
+ void StoreStateAndSignal(ActivityState state) {
+ ExpectOnThread();
+ state_ = state;
+ event_.Signal();
+ }
+
+ ActivityStatus* const activity_status_;
+ ActivityState state_;
+ base::WaitableEvent event_;
+ base::Thread thread_;
+ base::MessageLoop main_;
+ scoped_ptr<ActivityStatus::Listener> listener_;
+};
+
+} // namespace
+
+TEST(ActivityStatusTest, SingleThread) {
+ MessageLoop message_loop;
+
+ ActivityState result = kInvalidActivityState;
+
+ // Create a new listener that stores the new state into |result| on every
+ // state change.
+ ActivityStatus::Listener listener(
+ base::Bind(&StoreStateTo, base::Unretained(&result)));
+
+ EXPECT_EQ(kInvalidActivityState, result);
+
+ ActivityStatus* const activity_status = ActivityStatus::GetInstance();
+ activity_status->OnActivityStateChange(ACTIVITY_STATE_CREATED);
+ RunTasksUntilIdle();
+ EXPECT_EQ(ACTIVITY_STATE_CREATED, result);
+
+ activity_status->OnActivityStateChange(ACTIVITY_STATE_DESTROYED);
+ RunTasksUntilIdle();
+ EXPECT_EQ(ACTIVITY_STATE_DESTROYED, result);
+}
+
+TEST(ActivityStatusTest, TwoThreads) {
+ MultiThreadedTest test;
+ test.Run();
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/base_jni_registrar.cc b/chromium/base/android/base_jni_registrar.cc
new file mode 100644
index 00000000000..0645c73030c
--- /dev/null
+++ b/chromium/base/android/base_jni_registrar.cc
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 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/base_jni_registrar.h"
+
+#include "base/android/activity_status.h"
+#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"
+#include "base/android/path_service_android.h"
+#include "base/android/path_utils.h"
+#include "base/android/sys_utils.h"
+#include "base/android/thread_utils.h"
+#include "base/basictypes.h"
+#include "base/debug/trace_event.h"
+#include "base/message_loop/message_pump_android.h"
+#include "base/power_monitor/power_monitor_device_source_android.h"
+
+#if defined(GOOGLE_TV)
+#include "base/android/context_types.h"
+#endif
+
+namespace base {
+namespace android {
+
+static RegistrationMethod kBaseRegisteredMethods[] = {
+ { "ActivityStatus", base::android::ActivityStatus::RegisterBindings },
+ { "BuildInfo", base::android::BuildInfo::RegisterBindings },
+#if defined(GOOGLE_TV)
+ { "ContextTypes", base::android::RegisterContextTypes },
+#endif
+ { "CpuFeatures", base::android::RegisterCpuFeatures },
+ { "ImportantFileWriterAndroid",
+ 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 },
+ { "SysUtils", base::android::SysUtils::Register },
+ { "PowerMonitor", base::RegisterPowerMonitor },
+ { "ThreadUtils", base::RegisterThreadUtils },
+};
+
+bool RegisterJni(JNIEnv* env) {
+ TRACE_EVENT0("startup", "base_android::RegisterJni");
+ return RegisterNativeMethods(env, kBaseRegisteredMethods,
+ arraysize(kBaseRegisteredMethods));
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/base_jni_registrar.h b/chromium/base/android/base_jni_registrar.h
new file mode 100644
index 00000000000..fdaf5f2c6cd
--- /dev/null
+++ b/chromium/base/android/base_jni_registrar.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 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_ANDROID_BASE_JNI_REGISTRAR_H_
+#define BASE_ANDROID_BASE_JNI_REGISTRAR_H_
+
+#include <jni.h>
+
+#include "base/base_export.h"
+
+namespace base {
+namespace android {
+
+// Register all JNI bindings necessary for base.
+BASE_EXPORT bool RegisterJni(JNIEnv* env);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_BASE_JNI_REGISTRAR_H_
diff --git a/chromium/base/android/build_info.cc b/chromium/base/android/build_info.cc
new file mode 100644
index 00000000000..cdde6a90052
--- /dev/null
+++ b/chromium/base/android/build_info.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2012 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/build_info.h"
+
+#include <string>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "jni/BuildInfo_jni.h"
+
+namespace {
+
+// The caller takes ownership of the returned const char*.
+const char* StrDupJString(const base::android::JavaRef<jstring>& java_string) {
+ std::string str = ConvertJavaStringToUTF8(java_string);
+ return strdup(str.c_str());
+}
+
+} // namespace
+
+namespace base {
+namespace android {
+
+struct BuildInfoSingletonTraits {
+ static BuildInfo* New() {
+ return new BuildInfo(AttachCurrentThread());
+ }
+
+ static void Delete(BuildInfo* x) {
+ // We're leaking this type, see kRegisterAtExit.
+ NOTREACHED();
+ }
+
+ static const bool kRegisterAtExit = false;
+ static const bool kAllowedToAccessOnNonjoinableThread = true;
+};
+
+BuildInfo::BuildInfo(JNIEnv* env)
+ : device_(StrDupJString(Java_BuildInfo_getDevice(env))),
+ model_(StrDupJString(Java_BuildInfo_getDeviceModel(env))),
+ brand_(StrDupJString(Java_BuildInfo_getBrand(env))),
+ android_build_id_(StrDupJString(Java_BuildInfo_getAndroidBuildId(env))),
+ android_build_fp_(StrDupJString(
+ Java_BuildInfo_getAndroidBuildFingerprint(env))),
+ package_version_code_(StrDupJString(Java_BuildInfo_getPackageVersionCode(
+ env, GetApplicationContext()))),
+ package_version_name_(StrDupJString(Java_BuildInfo_getPackageVersionName(
+ env, GetApplicationContext()))),
+ package_label_(StrDupJString(Java_BuildInfo_getPackageLabel(
+ env, GetApplicationContext()))),
+ package_name_(StrDupJString(Java_BuildInfo_getPackageName(
+ env, GetApplicationContext()))),
+ sdk_int_(Java_BuildInfo_getSdkInt(env)),
+ java_exception_info_(NULL) {
+}
+
+// static
+BuildInfo* BuildInfo::GetInstance() {
+ return Singleton<BuildInfo, BuildInfoSingletonTraits >::get();
+}
+
+void BuildInfo::set_java_exception_info(const std::string& info) {
+ DCHECK(!java_exception_info_) << "info should be set only once.";
+ java_exception_info_ = strndup(info.c_str(), 1024);
+}
+
+// static
+bool BuildInfo::RegisterBindings(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/build_info.h b/chromium/base/android/build_info.h
new file mode 100644
index 00000000000..a5f44c2e0ac
--- /dev/null
+++ b/chromium/base/android/build_info.h
@@ -0,0 +1,115 @@
+// Copyright (c) 2012 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_ANDROID_BUILD_INFO_H_
+#define BASE_ANDROID_BUILD_INFO_H_
+
+#include <jni.h>
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/memory/singleton.h"
+
+namespace base {
+namespace android {
+
+// BuildInfo is a singleton class that stores android build and device
+// information. It will be called from Android specific code and gets used
+// primarily in crash reporting.
+
+// It is also used to store the last java exception seen during JNI.
+// TODO(nileshagrawal): Find a better place to store this info.
+class BASE_EXPORT BuildInfo {
+ public:
+
+ ~BuildInfo() {}
+
+ // Static factory method for getting the singleton BuildInfo instance.
+ // Note that ownership is not conferred on the caller and the BuildInfo in
+ // question isn't actually freed until shutdown. This is ok because there
+ // should only be one instance of BuildInfo ever created.
+ static BuildInfo* GetInstance();
+
+ // Const char* is used instead of std::strings because these values must be
+ // available even if the process is in a crash state. Sadly
+ // std::string.c_str() doesn't guarantee that memory won't be allocated when
+ // it is called.
+ const char* device() const {
+ return device_;
+ }
+
+ const char* model() const {
+ return model_;
+ }
+
+ const char* brand() const {
+ return brand_;
+ }
+
+ const char* android_build_id() const {
+ return android_build_id_;
+ }
+
+ const char* android_build_fp() const {
+ return android_build_fp_;
+ }
+
+ const char* package_version_code() const {
+ return package_version_code_;
+ }
+
+ const char* package_version_name() const {
+ return package_version_name_;
+ }
+
+ const char* package_label() const {
+ return package_label_;
+ }
+
+ const char* package_name() const {
+ return package_name_;
+ }
+
+ int sdk_int() const {
+ return sdk_int_;
+ }
+
+ const char* java_exception_info() const {
+ return java_exception_info_;
+ }
+
+ void set_java_exception_info(const std::string& info);
+
+ static bool RegisterBindings(JNIEnv* env);
+
+ private:
+ friend struct BuildInfoSingletonTraits;
+
+ explicit BuildInfo(JNIEnv* env);
+
+ // Const char* is used instead of std::strings because these values must be
+ // available even if the process is in a crash state. Sadly
+ // std::string.c_str() doesn't guarantee that memory won't be allocated when
+ // it is called.
+ const char* const device_;
+ const char* const model_;
+ const char* const brand_;
+ const char* const android_build_id_;
+ const char* const android_build_fp_;
+ const char* const package_version_code_;
+ const char* const package_version_name_;
+ const char* const package_label_;
+ const char* const package_name_;
+ const int sdk_int_;
+ // This is set via set_java_exception_info, not at constructor time.
+ const char* java_exception_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(BuildInfo);
+};
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_BUILD_INFO_H_
diff --git a/chromium/base/android/context_types.cc b/chromium/base/android/context_types.cc
new file mode 100644
index 00000000000..084b1365ce2
--- /dev/null
+++ b/chromium/base/android/context_types.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 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/context_types.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/files/file_path.h"
+#include "jni/ContextTypes_jni.h"
+
+namespace base {
+namespace android {
+
+bool IsRunningInWebapp() {
+ JNIEnv* env = AttachCurrentThread();
+ return static_cast<bool>(
+ Java_ContextTypes_isRunningInWebapp(env, GetApplicationContext()));
+}
+
+bool RegisterContextTypes(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/context_types.h b/chromium/base/android/context_types.h
new file mode 100644
index 00000000000..a132167199e
--- /dev/null
+++ b/chromium/base/android/context_types.h
@@ -0,0 +1,22 @@
+// Copyright (c) 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_ANDROID_CONTEXT_TYPES_H_
+#define BASE_ANDROID_CONTEXT_TYPES_H_
+
+#include <jni.h>
+
+#include "base/base_export.h"
+
+namespace base {
+namespace android {
+
+BASE_EXPORT bool IsRunningInWebapp();
+
+bool RegisterContextTypes(JNIEnv* env);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_CONTEXT_TYPES_H_
diff --git a/chromium/base/android/cpu_features.cc b/chromium/base/android/cpu_features.cc
new file mode 100644
index 00000000000..6a1869534b0
--- /dev/null
+++ b/chromium/base/android/cpu_features.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 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 <cpu-features.h>
+
+#include "base/android/jni_android.h"
+#include "jni/CpuFeatures_jni.h"
+
+namespace base {
+namespace android {
+
+jint GetCoreCount(JNIEnv*, jclass) {
+ return android_getCpuCount();
+}
+
+jlong GetCpuFeatures(JNIEnv*, jclass) {
+ return android_getCpuFeatures();
+}
+
+bool RegisterCpuFeatures(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/cpu_features.h b/chromium/base/android/cpu_features.h
new file mode 100644
index 00000000000..0a278228d2f
--- /dev/null
+++ b/chromium/base/android/cpu_features.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2012 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_ANDROID_CPU_FEATURES_H_
+#define BASE_ANDROID_CPU_FEATURES_H_
+
+#include <jni.h>
+
+namespace base {
+namespace android {
+
+bool RegisterCpuFeatures(JNIEnv* env);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_CPU_FEATURES_H_
diff --git a/chromium/base/android/fifo_utils.cc b/chromium/base/android/fifo_utils.cc
new file mode 100644
index 00000000000..8f3e95f92db
--- /dev/null
+++ b/chromium/base/android/fifo_utils.cc
@@ -0,0 +1,25 @@
+// 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/fifo_utils.h"
+
+#include <sys/stat.h>
+
+#include "base/files/file_path.h"
+
+namespace base {
+namespace android {
+
+bool CreateFIFO(const FilePath& path, int mode) {
+ // Default permissions for mkfifo() is ignored, chmod() is required.
+ return mkfifo(path.value().c_str(), mode) == 0 &&
+ chmod(path.value().c_str(), mode) == 0;
+}
+
+bool RedirectStream(FILE* stream, const FilePath& path, const char* mode) {
+ return freopen(path.value().c_str(), mode, stream) != NULL;
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/fifo_utils.h b/chromium/base/android/fifo_utils.h
new file mode 100644
index 00000000000..1936defaf6e
--- /dev/null
+++ b/chromium/base/android/fifo_utils.h
@@ -0,0 +1,32 @@
+// 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_ANDROID_FIFO_UTILS_H_
+#define BASE_ANDROID_FIFO_UTILS_H_
+
+#include <stdio.h>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+
+class FilePath;
+
+namespace android {
+
+// Creates a fifo at the given |path| with POSIX permissions set to |mode|,
+// returning true if it was successfully created and permissions were set.
+BASE_EXPORT bool CreateFIFO(const FilePath& path, int mode);
+
+// Redirects the |stream| to the file provided by |path| with |mode|
+// permissions, returning true if successful.
+BASE_EXPORT bool RedirectStream(FILE* stream,
+ const FilePath& path,
+ const char* mode);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_FIFO_UTILS_H_
diff --git a/chromium/base/android/important_file_writer_android.cc b/chromium/base/android/important_file_writer_android.cc
new file mode 100644
index 00000000000..bcbd785da04
--- /dev/null
+++ b/chromium/base/android/important_file_writer_android.cc
@@ -0,0 +1,42 @@
+// 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/important_file_writer_android.h"
+
+#include <string>
+
+#include "base/android/jni_string.h"
+#include "base/files/important_file_writer.h"
+#include "base/threading/thread_restrictions.h"
+#include "jni/ImportantFileWriterAndroid_jni.h"
+
+namespace base {
+namespace android {
+
+static jboolean WriteFileAtomically(JNIEnv* env,
+ jclass /* clazz */,
+ jstring file_name,
+ jbyteArray data) {
+ // This is called on the UI thread during shutdown to save tab data, so
+ // needs to enable IO.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+ std::string native_file_name;
+ base::android::ConvertJavaStringToUTF8(env, file_name, &native_file_name);
+ base::FilePath path(native_file_name);
+ int data_length = env->GetArrayLength(data);
+ jbyte* native_data = env->GetByteArrayElements(data, NULL);
+ std::string native_data_string(reinterpret_cast<char *>(native_data),
+ data_length);
+ bool result = base::ImportantFileWriter::WriteFileAtomically(
+ path, native_data_string);
+ env->ReleaseByteArrayElements(data, native_data, JNI_ABORT);
+ return result;
+}
+
+bool RegisterImportantFileWriterAndroid(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/important_file_writer_android.h b/chromium/base/android/important_file_writer_android.h
new file mode 100644
index 00000000000..20956babcea
--- /dev/null
+++ b/chromium/base/android/important_file_writer_android.h
@@ -0,0 +1,18 @@
+// 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 NATIVE_FRAMEWORK_CHROME_IMPORTANT_FILE_WRITE_ANDROID_H_
+#define NATIVE_FRAMEWORK_CHROME_IMPORTANT_FILE_WRITE_ANDROID_H_
+
+#include <jni.h>
+
+namespace base {
+namespace android {
+
+bool RegisterImportantFileWriterAndroid(JNIEnv* env);
+
+} // namespace android
+} // namespace base
+
+#endif // NATIVE_FRAMEWORK_CHROME_IMPORTANT_FILE_WRITE_ANDROID_H_
diff --git a/chromium/base/android/java/src/org/chromium/base/AccessedByNative.java b/chromium/base/android/java/src/org/chromium/base/AccessedByNative.java
new file mode 100644
index 00000000000..0a732587210
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/AccessedByNative.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 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 java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @AccessedByNative is used to ensure proguard will keep this field, since it's
+ * only accessed by native.
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.CLASS)
+public @interface AccessedByNative {
+ public String value() default "";
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/ActivityState.template b/chromium/base/android/java/src/org/chromium/base/ActivityState.template
new file mode 100644
index 00000000000..adf990a576d
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/ActivityState.template
@@ -0,0 +1,14 @@
+// Copyright (c) 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;
+
+// A simple auto-generated interface used to list the various
+// states of an activity as used by both org.chromium.base.ActivityStatus
+// and base/android/activity_status.h
+interface ActivityState {
+#define DEFINE_ACTIVITY_STATE(x,y) public final int x = y;
+#include "base/android/activity_state_list.h"
+#undef DEFINE_ACTIVITY_STATE
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/ActivityStatus.java b/chromium/base/android/java/src/org/chromium/base/ActivityStatus.java
new file mode 100644
index 00000000000..47472342b20
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/ActivityStatus.java
@@ -0,0 +1,136 @@
+// Copyright (c) 2012 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.app.Activity;
+import android.os.Handler;
+import android.os.Looper;
+
+/**
+ * Provides information about the current activity's status, and a way
+ * to register / unregister listeners for state changes.
+ */
+@JNINamespace("base::android")
+public class ActivityStatus {
+
+ // Constants matching activity states reported to StateListener.onStateChange
+ // As an implementation detail, these are now defined in the auto-generated
+ // ActivityState interface, to be shared with C++.
+ public static final int CREATED = ActivityState.CREATED;
+ public static final int STARTED = ActivityState.STARTED;
+ public static final int RESUMED = ActivityState.RESUMED;
+ public static final int PAUSED = ActivityState.PAUSED;
+ public static final int STOPPED = ActivityState.STOPPED;
+ public static final int DESTROYED = ActivityState.DESTROYED;
+
+ // Current main activity, or null if none.
+ private static Activity sActivity;
+
+ // Current main activity's state. This can be set even if sActivity is null, to simplify unit
+ // testing.
+ private static int sActivityState;
+
+ private static final ObserverList<StateListener> sStateListeners =
+ new ObserverList<StateListener>();
+
+ /**
+ * Interface to be implemented by listeners.
+ */
+ public interface StateListener {
+ /**
+ * Called when the activity's state changes.
+ * @param newState New activity state.
+ */
+ public void onActivityStateChange(int newState);
+ }
+
+ private ActivityStatus() {}
+
+ /**
+ * Must be called by the main activity when it changes state.
+ * @param activity Current activity.
+ * @param newState New state value.
+ */
+ public static void onStateChange(Activity activity, int newState) {
+ if (sActivity != activity) {
+ // ActivityStatus is notified with the CREATED event very late during the main activity
+ // creation to avoid making startup performance worse than it is by notifying observers
+ // that could do some expensive work. This can lead to non-CREATED events being fired
+ // before the CREATED event which is problematic.
+ // TODO(pliard): fix http://crbug.com/176837.
+ sActivity = activity;
+ }
+ sActivityState = newState;
+ for (StateListener listener : sStateListeners) {
+ listener.onActivityStateChange(newState);
+ }
+ if (newState == DESTROYED) {
+ sActivity = null;
+ }
+ }
+
+ /**
+ * Indicates that the parent activity is currently paused.
+ */
+ public static boolean isPaused() {
+ return sActivityState == PAUSED;
+ }
+
+ /**
+ * Returns the current main application activity.
+ */
+ public static Activity getActivity() {
+ return sActivity;
+ }
+
+ /**
+ * Returns the current main application activity's state.
+ */
+ public static int getState() {
+ return sActivityState;
+ }
+
+ /**
+ * Registers the given listener to receive activity state changes.
+ * @param listener Listener to receive state changes.
+ */
+ public static void registerStateListener(StateListener listener) {
+ sStateListeners.addObserver(listener);
+ }
+
+ /**
+ * Unregisters the given listener from receiving activity state changes.
+ * @param listener Listener that doesn't want to receive state changes.
+ */
+ public static void unregisterStateListener(StateListener listener) {
+ sStateListeners.removeObserver(listener);
+ }
+
+ /**
+ * Registers the single thread-safe native activity status listener.
+ * This handles the case where the caller is not on the main thread.
+ * Note that this is used by a leaky singleton object from the native
+ * side, hence lifecycle management is greatly simplified.
+ */
+ @CalledByNative
+ private static void registerThreadSafeNativeStateListener() {
+ ThreadUtils.runOnUiThread(new Runnable () {
+ @Override
+ public void run() {
+ // Register a new listener that calls nativeOnActivityStateChange.
+ sStateListeners.addObserver(new StateListener() {
+ @Override
+ public void onActivityStateChange(int newState) {
+ nativeOnActivityStateChange(newState);
+ }
+ });
+ }
+ });
+ }
+
+ // Called to notify the native side of state changes.
+ // IMPORTANT: This is always called on the main thread!
+ private static native void nativeOnActivityStateChange(int newState);
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java b/chromium/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
new file mode 100644
index 00000000000..8c4ac0fe01b
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
@@ -0,0 +1,140 @@
+// 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.app.Notification;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.view.ViewTreeObserver;
+
+/**
+ * Utility class to use new APIs that were added after ICS (API level 14).
+ */
+public class ApiCompatibilityUtils {
+
+ private ApiCompatibilityUtils() {
+ }
+
+ /**
+ * Returns true if view's layout direction is right-to-left.
+ *
+ * @param view the View whose layout is being considered
+ */
+ public static boolean isLayoutRtl(View view) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ return view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+ } else {
+ // All layouts are LTR before JB MR1.
+ return false;
+ }
+ }
+
+ /**
+ * @see android.view.View#setLayoutDirection(int)
+ */
+ public static void setLayoutDirection(View view, int layoutDirection) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ view.setLayoutDirection(layoutDirection);
+ } else {
+ // Do nothing. RTL layouts aren't supported before JB MR1.
+ }
+ }
+
+ /**
+ * @see android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)
+ */
+ public static void setMarginEnd(MarginLayoutParams layoutParams, int end) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ layoutParams.setMarginEnd(end);
+ } else {
+ layoutParams.rightMargin = end;
+ }
+ }
+
+ /**
+ * @see android.view.ViewGroup.MarginLayoutParams#getMarginEnd()
+ */
+ public static int getMarginEnd(MarginLayoutParams layoutParams) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ return layoutParams.getMarginEnd();
+ } else {
+ return layoutParams.rightMargin;
+ }
+ }
+
+ /**
+ * @see android.view.ViewGroup.MarginLayoutParams#setMarginStart(int)
+ */
+ public static void setMarginStart(MarginLayoutParams layoutParams, int start) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ layoutParams.setMarginStart(start);
+ } else {
+ layoutParams.leftMargin = start;
+ }
+ }
+
+ /**
+ * @see android.view.ViewGroup.MarginLayoutParams#getMarginStart()
+ */
+ public static int getMarginStart(MarginLayoutParams layoutParams) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ return layoutParams.getMarginStart();
+ } else {
+ return layoutParams.leftMargin;
+ }
+ }
+
+ /**
+ * @see android.view.View#postInvalidateOnAnimation()
+ */
+ public static void postInvalidateOnAnimation(View view) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ view.postInvalidateOnAnimation();
+ } else {
+ view.postInvalidate();
+ }
+ }
+
+ // These methods have a new name, and the old name is deprecated.
+
+ /**
+ * @see android.view.View#setBackground(Drawable)
+ */
+ @SuppressWarnings("deprecation")
+ public static void setBackgroundForView(View view, Drawable drawable) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ view.setBackground(drawable);
+ } else {
+ view.setBackgroundDrawable(drawable);
+ }
+ }
+
+ /**
+ * @see android.view.ViewTreeObserver#removeOnGlobalLayoutListener()
+ */
+ @SuppressWarnings("deprecation")
+ public static void removeOnGlobalLayoutListener(
+ View view, ViewTreeObserver.OnGlobalLayoutListener listener) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ view.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
+ } else {
+ view.getViewTreeObserver().removeGlobalOnLayoutListener(listener);
+ }
+ }
+
+ /**
+ * @see android.app.Notification.Builder#build()
+ */
+ @SuppressWarnings("deprecation")
+ public static Notification buildNotification(Notification.Builder builder) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ return builder.build();
+ } else {
+ return builder.getNotification();
+ }
+ }
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/BuildInfo.java b/chromium/base/android/java/src/org/chromium/base/BuildInfo.java
new file mode 100644
index 00000000000..81a8f345590
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/BuildInfo.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2012 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.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Build;
+import android.util.Log;
+
+import org.chromium.base.CalledByNative;
+
+/**
+ * BuildInfo is a utility class providing easy access to {@link PackageInfo}
+ * information. This is primarly of use for accessesing package information
+ * from native code.
+ */
+public class BuildInfo {
+ private static final String TAG = "BuildInfo";
+ private static final int MAX_FINGERPRINT_LENGTH = 128;
+
+ /**
+ * BuildInfo is a static utility class and therefore shouldn't be
+ * instantiated.
+ */
+ private BuildInfo() {
+ }
+
+ @CalledByNative
+ public static String getDevice() {
+ return Build.DEVICE;
+ }
+
+ @CalledByNative
+ public static String getBrand() {
+ return Build.BRAND;
+ }
+
+ @CalledByNative
+ public static String getAndroidBuildId() {
+ return Build.ID;
+ }
+
+ /**
+ * @return The build fingerprint for the current Android install. The value is truncated to a
+ * 128 characters as this is used for crash and UMA reporting, which should avoid huge
+ * strings.
+ */
+ @CalledByNative
+ public static String getAndroidBuildFingerprint() {
+ return Build.FINGERPRINT.substring(
+ 0, Math.min(Build.FINGERPRINT.length(), MAX_FINGERPRINT_LENGTH));
+ }
+
+ @CalledByNative
+ public static String getDeviceModel() {
+ return Build.MODEL;
+ }
+
+ @CalledByNative
+ public static String getPackageVersionCode(Context context) {
+ String msg = "versionCode not available.";
+ try {
+ PackageManager pm = context.getPackageManager();
+ PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0);
+ msg = "";
+ if (pi.versionCode > 0) {
+ msg = Integer.toString(pi.versionCode);
+ }
+ } catch (NameNotFoundException e) {
+ Log.d(TAG, msg);
+ }
+ return msg;
+
+ }
+
+ @CalledByNative
+ public static String getPackageVersionName(Context context) {
+ String msg = "versionName not available";
+ try {
+ PackageManager pm = context.getPackageManager();
+ PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0);
+ msg = pi.versionName;
+ } catch (NameNotFoundException e) {
+ Log.d(TAG, msg);
+ }
+ return msg;
+ }
+
+ @CalledByNative
+ public static String getPackageLabel(Context context) {
+ try {
+ PackageManager packageManager = context.getPackageManager();
+ ApplicationInfo appInfo = packageManager.getApplicationInfo(context.getPackageName(),
+ PackageManager.GET_META_DATA);
+ CharSequence label = packageManager.getApplicationLabel(appInfo);
+ return label != null ? label.toString() : "";
+ } catch (NameNotFoundException e) {
+ return "";
+ }
+ }
+
+ @CalledByNative
+ public static String getPackageName(Context context) {
+ String packageName = context != null ? context.getPackageName() : null;
+ return packageName != null ? packageName : "";
+ }
+
+ @CalledByNative
+ public static int getSdkInt() {
+ return Build.VERSION.SDK_INT;
+ }
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/CalledByNative.java b/chromium/base/android/java/src/org/chromium/base/CalledByNative.java
new file mode 100644
index 00000000000..db01b0d0720
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/CalledByNative.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2012 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 java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @CalledByNative is used by the JNI generator to create the necessary JNI
+ * bindings and expose this method to native code.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.CLASS)
+public @interface CalledByNative {
+ /*
+ * If present, tells which inner class the method belongs to.
+ */
+ public String value() default "";
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/CalledByNativeUnchecked.java b/chromium/base/android/java/src/org/chromium/base/CalledByNativeUnchecked.java
new file mode 100644
index 00000000000..38bb0c04508
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/CalledByNativeUnchecked.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2012 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 java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @CalledByNativeUnchecked is used to generate JNI bindings that do not check for exceptions.
+ * It only makes sense to use this annotation on methods that declare a throws... spec.
+ * However, note that the exception received native side maybe an 'unchecked' (RuntimeExpception)
+ * such as NullPointerException, so the native code should differentiate these cases.
+ * Usage of this should be very rare; where possible handle exceptions in the Java side and use a
+ * return value to indicate success / failure.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.CLASS)
+public @interface CalledByNativeUnchecked {
+ /*
+ * If present, tells which inner class the method belongs to.
+ */
+ public String value() default "";
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/ChromiumActivity.java b/chromium/base/android/java/src/org/chromium/base/ChromiumActivity.java
new file mode 100644
index 00000000000..65f5ce929b0
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/ChromiumActivity.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 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.app.Activity;
+import android.os.Bundle;
+
+// All Chromium main activities should extend this class. This allows various sub-systems to
+// properly react to activity state changes.
+public class ChromiumActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstance) {
+ super.onCreate(savedInstance);
+ ActivityStatus.onStateChange(this, ActivityStatus.CREATED);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ ActivityStatus.onStateChange(this, ActivityStatus.STARTED);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ ActivityStatus.onStateChange(this, ActivityStatus.RESUMED);
+ }
+
+ @Override
+ protected void onPause() {
+ ActivityStatus.onStateChange(this, ActivityStatus.PAUSED);
+ super.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ ActivityStatus.onStateChange(this, ActivityStatus.STOPPED);
+ super.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ ActivityStatus.onStateChange(this, ActivityStatus.DESTROYED);
+ super.onDestroy();
+ }
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/ContextTypes.java b/chromium/base/android/java/src/org/chromium/base/ContextTypes.java
new file mode 100644
index 00000000000..35ecd8fd822
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/ContextTypes.java
@@ -0,0 +1,96 @@
+// Copyright (c) 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.content.Context;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.Map;
+
+/**
+ * Maintains the {@link Context}-to-"context type" mapping. The context type
+ * {@code MODE_APP} is chosen for the application context associated with
+ * the activity running in application mode, while {@code MODE_NORMAL} for main
+ * Chromium activity.
+ *
+ * <p>Used as singleton instance.
+ */
+public class ContextTypes {
+
+ // Available context types.
+ public static final int CONTEXT_TYPE_NORMAL = 1;
+ public static final int CONTEXT_TYPE_WEBAPP = 2;
+
+ private final Map<Context, Integer> mContextMap;
+
+ private ContextTypes() {
+ mContextMap = new ConcurrentHashMap<Context, Integer>();
+ }
+
+ private static class ContextTypesHolder {
+ private static final ContextTypes INSTANCE = new ContextTypes();
+ }
+
+ public static ContextTypes getInstance() {
+ return ContextTypesHolder.INSTANCE;
+ }
+
+ /**
+ * Adds the mapping for the given {@link Context}.
+ *
+ * @param context {@link Context} in interest
+ * @param type the type associated with the context
+ * @throws IllegalArgumentException if type is not a valid one.
+ */
+ public void put(Context context, int type) throws IllegalArgumentException {
+ if (type != CONTEXT_TYPE_NORMAL && type != CONTEXT_TYPE_WEBAPP) {
+ throw new IllegalArgumentException("Wrong context type");
+ }
+ mContextMap.put(context, type);
+ }
+
+ /**
+ * Removes the mapping for the given context.
+ *
+ * @param context {@link Context} in interest
+ */
+ public void remove(Context context) {
+ mContextMap.remove(context);
+ }
+
+ /**
+ * Returns type of the given context.
+ *
+ * @param context {@link Context} in interest
+ * @return type associated with the context. Returns {@code MODE_NORMAL} by
+ * default if the mapping for the queried context is not present.
+ */
+ public int getType(Context context) {
+ Integer contextType = mContextMap.get(context);
+ return contextType == null ? CONTEXT_TYPE_NORMAL : contextType;
+ }
+
+ /**
+ * Returns whether activity is running in web app mode.
+ *
+ * @param appContext {@link Context} in interest
+ * @return {@code true} when activity is running in web app mode.
+ */
+ @CalledByNative
+ public static boolean isRunningInWebapp(Context appContext) {
+ return ContextTypes.getInstance().getType(appContext)
+ == CONTEXT_TYPE_WEBAPP;
+ }
+
+ /**
+ * Checks if the mapping exists for the given context.
+ *
+ * @param context {@link Context} in interest
+ * @return {@code true} if the mapping exists; otherwise {@code false}
+ */
+ public boolean contains(Context context) {
+ return mContextMap.containsKey(context);
+ }
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/CpuFeatures.java b/chromium/base/android/java/src/org/chromium/base/CpuFeatures.java
new file mode 100644
index 00000000000..f298fb1e165
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/CpuFeatures.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 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;
+
+// The only purpose of this class is to allow sending CPU properties
+// from the browser process to sandboxed renderer processes. This is
+// needed because sandboxed processes cannot, on ARM, query the kernel
+// about the CPU's properties by parsing /proc, so this operation must
+// be performed in the browser process, and the result passed to
+// renderer ones.
+//
+// For more context, see http://crbug.com/164154
+//
+// Technically, this is a wrapper around the native NDK cpufeatures
+// library. The exact CPU features bits are never used in Java so
+// there is no point in duplicating their definitions here.
+//
+@JNINamespace("base::android")
+public abstract class CpuFeatures {
+ /**
+ * Return the number of CPU Cores on the device.
+ */
+ public static int getCount() {
+ return nativeGetCoreCount();
+ }
+
+ /**
+ * Return the CPU feature mask.
+ * This is a 64-bit integer that corresponds to the CPU's features.
+ * The value comes directly from android_getCpuFeatures().
+ */
+ public static long getMask() {
+ return nativeGetCpuFeatures();
+ }
+
+ private static native int nativeGetCoreCount();
+ private static native long nativeGetCpuFeatures();
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java b/chromium/base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java
new file mode 100644
index 00000000000..1c7c0185dae
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java
@@ -0,0 +1,29 @@
+// Copyright (c) 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;
+
+/**
+ * This class provides an interface to the native class for writing
+ * important data files without risking data loss.
+ */
+@JNINamespace("base::android")
+public class ImportantFileWriterAndroid {
+
+ /**
+ * Write a binary file atomically.
+ *
+ * This either writes all the data or leaves the file unchanged.
+ *
+ * @param fileName The complete path of the file to be written
+ * @param data The data to be written to the file
+ * @return true if the data was written to the file, false if not.
+ */
+ public static boolean writeFileAtomically(String fileName, byte[] data) {
+ return nativeWriteFileAtomically(fileName, data);
+ }
+
+ private static native boolean nativeWriteFileAtomically(
+ String fileName, byte[] data);
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/JNINamespace.java b/chromium/base/android/java/src/org/chromium/base/JNINamespace.java
new file mode 100644
index 00000000000..cfffc91a346
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/JNINamespace.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 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 java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @JNINamespace is used by the JNI generator to create the necessary JNI
+ * bindings and expose this method to native code using the specified namespace.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface JNINamespace {
+ public String value();
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/JavaHandlerThread.java b/chromium/base/android/java/src/org/chromium/base/JavaHandlerThread.java
new file mode 100644
index 00000000000..5f9960e53ce
--- /dev/null
+++ b/chromium/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/chromium/base/android/java/src/org/chromium/base/MemoryPressureLevelList.template b/chromium/base/android/java/src/org/chromium/base/MemoryPressureLevelList.template
new file mode 100644
index 00000000000..cebca842e33
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/MemoryPressureLevelList.template
@@ -0,0 +1,12 @@
+// 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;
+
+class MemoryPressureLevelList {
+#define DEFINE_MEMORY_PRESSURE_LEVEL(name, value) \
+ static final int name = value;
+#include "base/memory/memory_pressure_level_list.h"
+#undef DEFINE_MEMORY_PRESSURE_LEVEL
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/MemoryPressureListener.java b/chromium/base/android/java/src/org/chromium/base/MemoryPressureListener.java
new file mode 100644
index 00000000000..311b0f6ff49
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/MemoryPressureListener.java
@@ -0,0 +1,55 @@
+// 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.content.ComponentCallbacks2;
+import android.content.Context;
+import android.content.res.Configuration;
+
+
+/**
+ * This is an internal implementation of the C++ counterpart.
+ * It registers a ComponentCallbacks2 with the system, and dispatches into
+ * native.
+ */
+public class MemoryPressureListener {
+ @CalledByNative
+ private static void registerSystemCallback(Context context) {
+ context.registerComponentCallbacks(
+ new ComponentCallbacks2() {
+ @Override
+ public void onTrimMemory(int level) {
+ maybeNotifyMemoryPresure(level);
+ }
+
+ @Override
+ public void onLowMemory() {
+ nativeOnMemoryPressure(MemoryPressureLevelList.MEMORY_PRESSURE_CRITICAL);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration configuration) {
+ }
+ });
+ }
+
+ /**
+ * Used by applications to simulate a memory pressure signal.
+ */
+ public static void simulateMemoryPressureSignal(int level) {
+ maybeNotifyMemoryPresure(level);
+ }
+
+ private static void maybeNotifyMemoryPresure(int level) {
+ if (level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
+ nativeOnMemoryPressure(MemoryPressureLevelList.MEMORY_PRESSURE_CRITICAL);
+ } else if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND ||
+ level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
+ nativeOnMemoryPressure(MemoryPressureLevelList.MEMORY_PRESSURE_MODERATE);
+ }
+ }
+
+ private static native void nativeOnMemoryPressure(int memoryPressureType);
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/NativeClassQualifiedName.java b/chromium/base/android/java/src/org/chromium/base/NativeClassQualifiedName.java
new file mode 100644
index 00000000000..309169bb1d6
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/NativeClassQualifiedName.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2012 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 java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @NativeClassQualifiedName is used by the JNI generator to create the necessary JNI
+ * bindings to call into the specified native class name.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NativeClassQualifiedName {
+ /*
+ * Tells which native class the method is going to be bound to.
+ * The first parameter of the annotated method must be an int nativePtr pointing to
+ * an instance of this class.
+ */
+ public String value();
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/ObserverList.java b/chromium/base/android/java/src/org/chromium/base/ObserverList.java
new file mode 100644
index 00000000000..13a81c5c195
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/ObserverList.java
@@ -0,0 +1,177 @@
+// 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 java.lang.Iterable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import javax.annotation.concurrent.NotThreadSafe;
+
+/**
+ * A container for a list of observers.
+ * <p/>
+ * This container can be modified during iteration without invalidating the iterator.
+ * So, it safely handles the case of an observer removing itself or other observers from the list
+ * while observers are being notified.
+ * <p/>
+ * The implementation (and the interface) is heavily influenced by the C++ ObserverList.
+ * Notable differences:
+ * - The iterator implements NOTIFY_EXISTING_ONLY.
+ * - The FOR_EACH_OBSERVER closure is left to the clients to implement in terms of iterator().
+ * <p/>
+ * This class is not threadsafe. Observers MUST be added, removed and will be notified on the same
+ * thread this is created.
+ */
+@NotThreadSafe
+public class ObserverList<E> implements Iterable<E> {
+ public final List<E> mObservers = new ArrayList<E>();
+ private int mIterationDepth = 0;
+
+ public ObserverList() {}
+
+ /**
+ * Add an observer to the list.
+ * <p/>
+ * An observer should not be added to the same list more than once. If an iteration is already
+ * in progress, this observer will be not be visible during that iteration.
+ */
+ public void addObserver(E obs) {
+ // Avoid adding null elements to the list as they may be removed on a compaction.
+ if (obs == null || mObservers.contains(obs)) {
+ assert false;
+ return;
+ }
+
+ // Structurally modifying the underlying list here. This means we
+ // cannot use the underlying list's iterator to iterate over the list.
+ mObservers.add(obs);
+ }
+
+ /**
+ * Remove an observer from the list if it is in the list.
+ */
+ public void removeObserver(E obs) {
+ int index = mObservers.indexOf(obs);
+
+ if (index == -1)
+ return;
+
+ if (mIterationDepth == 0) {
+ // No one is iterating over the list.
+ mObservers.remove(obs);
+ } else {
+ mObservers.set(index, null);
+ }
+ }
+
+ public boolean hasObserver(E obs) {
+ return mObservers.contains(obs);
+ }
+
+ public void clear() {
+ if (mIterationDepth == 0) {
+ mObservers.clear();
+ return;
+ }
+
+ int size = mObservers.size();
+ for (int i = 0; i < size; i++)
+ mObservers.set(i, null);
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ return new ObserverListIterator();
+ }
+
+ /**
+ * Compact the underlying list be removing null elements.
+ * <p/>
+ * Should only be called when mIterationDepth is zero.
+ */
+ private void compact() {
+ assert mIterationDepth == 0;
+ // Safe to use the underlying list's iterator, as we know that no-one else
+ // is iterating over the list.
+ Iterator<E> it = mObservers.iterator();
+ while (it.hasNext()) {
+ E el = it.next();
+ if (el == null)
+ it.remove();
+ }
+ }
+
+ private void incrementIterationDepth() {
+ mIterationDepth++;
+ }
+
+ private void decrementIterationDepthAndCompactIfNeeded() {
+ mIterationDepth--;
+ assert mIterationDepth >= 0;
+ if (mIterationDepth == 0)
+ compact();
+ }
+
+ private int getSize() {
+ return mObservers.size();
+ }
+
+ private E getObserverAt(int index) {
+ return mObservers.get(index);
+ }
+
+ private class ObserverListIterator implements Iterator<E> {
+ private final int mListEndMarker;
+ private int mIndex = 0;
+ private boolean mIsExhausted = false;
+
+ private ObserverListIterator() {
+ ObserverList.this.incrementIterationDepth();
+ mListEndMarker = ObserverList.this.getSize();
+ }
+
+ @Override
+ public boolean hasNext() {
+ int lookupIndex = mIndex;
+ while (lookupIndex < mListEndMarker &&
+ ObserverList.this.getObserverAt(lookupIndex) == null)
+ lookupIndex++;
+ if (lookupIndex < mListEndMarker)
+ return true;
+
+ // We have reached the end of the list, allow for compaction.
+ compactListIfNeeded();
+ return false;
+ }
+
+ @Override
+ public E next() {
+ // Advance if the current element is null.
+ while (mIndex < mListEndMarker && ObserverList.this.getObserverAt(mIndex) == null)
+ mIndex++;
+ if (mIndex < mListEndMarker)
+ return ObserverList.this.getObserverAt(mIndex++);
+
+ // We have reached the end of the list, allow for compaction.
+ compactListIfNeeded();
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ private void compactListIfNeeded() {
+ if (!mIsExhausted) {
+ mIsExhausted = true;
+ ObserverList.this.decrementIterationDepthAndCompactIfNeeded();
+ }
+ }
+ }
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/PathService.java b/chromium/base/android/java/src/org/chromium/base/PathService.java
new file mode 100644
index 00000000000..dfda736d01a
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/PathService.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 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;
+
+/**
+ * This class provides java side access to the native PathService.
+ */
+@JNINamespace("base::android")
+public abstract class PathService {
+
+ // Must match the value of DIR_MODULE in base/base_paths.h!
+ public static final int DIR_MODULE = 3;
+
+ // Prevent instantiation.
+ private PathService() {}
+
+ public static void override(int what, String path) {
+ nativeOverride(what, path);
+ }
+
+ private static native void nativeOverride(int what, String path);
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/PathUtils.java b/chromium/base/android/java/src/org/chromium/base/PathUtils.java
new file mode 100644
index 00000000000..aee5c050c34
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/PathUtils.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2012 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.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.Environment;
+
+import java.io.File;
+
+/**
+ * This class provides the path related methods for the native library.
+ */
+public abstract class PathUtils {
+
+ private static String sDataDirectorySuffix;
+ private static String sWebappDirectorySuffix;
+ private static String sWebappCacheDirectory;
+
+ // Prevent instantiation.
+ private PathUtils() {}
+
+ /**
+ * Sets the suffix that should be used for the directory where private data is to be stored
+ * by the application.
+ * @param suffix The private data directory suffix.
+ * @see Context#getDir(String, int)
+ */
+ public static void setPrivateDataDirectorySuffix(String suffix) {
+ sDataDirectorySuffix = suffix;
+ }
+
+ /**
+ * Sets the directory info used for chrome process running in application mode.
+ *
+ * @param webappSuffix The suffix of the directory used for storing webapp-specific profile
+ * @param cacheDir Cache directory name for web apps.
+ */
+ public static void setWebappDirectoryInfo(String webappSuffix, String cacheDir) {
+ sWebappDirectorySuffix = webappSuffix;
+ sWebappCacheDirectory = cacheDir;
+ }
+
+ /**
+ * @return the private directory that is used to store application data.
+ */
+ @CalledByNative
+ public static String getDataDirectory(Context appContext) {
+ if (sDataDirectorySuffix == null) {
+ throw new IllegalStateException(
+ "setDataDirectorySuffix must be called before getDataDirectory");
+ }
+ return appContext.getDir(sDataDirectorySuffix, Context.MODE_PRIVATE).getPath();
+ }
+
+ /**
+ * @return the cache directory.
+ */
+ @SuppressWarnings("unused")
+ @CalledByNative
+ public static String getCacheDirectory(Context appContext) {
+ if (ContextTypes.getInstance().getType(appContext) == ContextTypes.CONTEXT_TYPE_NORMAL) {
+ return appContext.getCacheDir().getPath();
+ }
+ if (sWebappDirectorySuffix == null || sWebappCacheDirectory == null) {
+ throw new IllegalStateException(
+ "setWebappDirectoryInfo must be called before getCacheDirectory");
+ }
+ return new File(appContext.getDir(sWebappDirectorySuffix, appContext.MODE_PRIVATE),
+ sWebappCacheDirectory).getPath();
+ }
+
+ /**
+ * @return the public downloads directory.
+ */
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private static String getDownloadsDirectory(Context appContext) {
+ return Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS).getPath();
+ }
+
+ /**
+ * @return the path to native libraries.
+ */
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private static String getNativeLibraryDirectory(Context appContext) {
+ ApplicationInfo ai = appContext.getApplicationInfo();
+ if ((ai.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0 ||
+ (ai.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ return ai.nativeLibraryDir;
+ }
+
+ return "/system/lib/";
+ }
+
+ /**
+ * @return the external storage directory.
+ */
+ @SuppressWarnings("unused")
+ @CalledByNative
+ public static String getExternalStorageDirectory() {
+ return Environment.getExternalStorageDirectory().getAbsolutePath();
+ }
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/PowerMonitor.java b/chromium/base/android/java/src/org/chromium/base/PowerMonitor.java
new file mode 100644
index 00000000000..b7a691e3ce3
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/PowerMonitor.java
@@ -0,0 +1,92 @@
+// Copyright (c) 2012 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.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.Handler;
+import android.os.Looper;
+
+
+/**
+ * Integrates native PowerMonitor with the java side.
+ */
+@JNINamespace("base::android")
+public class PowerMonitor implements ActivityStatus.StateListener {
+ private static final long SUSPEND_DELAY_MS = 1 * 60 * 1000; // 1 minute.
+ private static class LazyHolder {
+ private static final PowerMonitor INSTANCE = new PowerMonitor();
+ }
+ private static PowerMonitor sInstance;
+
+ private boolean mIsBatteryPower;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+ // Asynchronous task used to fire the "paused" event to the native side 1 minute after the main
+ // activity transitioned to the "paused" state. This event is not sent immediately because it
+ // would be too aggressive. An Android activity can be in the "paused" state quite often. This
+ // can happen when a dialog window shows up for instance.
+ private static final Runnable sSuspendTask = new Runnable() {
+ @Override
+ public void run() {
+ nativeOnMainActivitySuspended();
+ }
+ };
+
+ public static void createForTests(Context context) {
+ // Applications will create this once the JNI side has been fully wired up both sides. For
+ // tests, we just need native -> java, that is, we don't need to notify java -> native on
+ // creation.
+ sInstance = LazyHolder.INSTANCE;
+ }
+
+ public static void create(Context context) {
+ if (sInstance == null) {
+ sInstance = LazyHolder.INSTANCE;
+ ActivityStatus.registerStateListener(sInstance);
+ IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ Intent batteryStatusIntent = context.registerReceiver(null, ifilter);
+ onBatteryChargingChanged(batteryStatusIntent);
+ }
+ }
+
+ private PowerMonitor() {
+ }
+
+ public static void onBatteryChargingChanged(Intent intent) {
+ if (sInstance == null) {
+ // We may be called by the framework intent-filter before being fully initialized. This
+ // is not a problem, since our constructor will check for the state later on.
+ return;
+ }
+ int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
+ // If we're not plugged, assume we're running on battery power.
+ sInstance.mIsBatteryPower = chargePlug != BatteryManager.BATTERY_PLUGGED_USB &&
+ chargePlug != BatteryManager.BATTERY_PLUGGED_AC;
+ nativeOnBatteryChargingChanged();
+ }
+
+ @Override
+ public void onActivityStateChange(int newState) {
+ if (newState == ActivityStatus.RESUMED) {
+ // Remove the callback from the message loop in case it hasn't been executed yet.
+ mHandler.removeCallbacks(sSuspendTask);
+ nativeOnMainActivityResumed();
+ } else if (newState == ActivityStatus.PAUSED) {
+ mHandler.postDelayed(sSuspendTask, SUSPEND_DELAY_MS);
+ }
+ }
+
+ @CalledByNative
+ private static boolean isBatteryPower() {
+ return sInstance.mIsBatteryPower;
+ }
+
+ private static native void nativeOnBatteryChargingChanged();
+ private static native void nativeOnMainActivitySuspended();
+ private static native void nativeOnMainActivityResumed();
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/PowerStatusReceiver.java b/chromium/base/android/java/src/org/chromium/base/PowerStatusReceiver.java
new file mode 100644
index 00000000000..f36c146273f
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/PowerStatusReceiver.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2012 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.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+
+/**
+ * A BroadcastReceiver that listens to changes in power status and notifies
+ * PowerMonitor.
+ * It's instantiated by the framework via the application intent-filter
+ * declared in its manifest.
+ */
+public class PowerStatusReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ PowerMonitor.onBatteryChargingChanged(intent);
+ }
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/SysUtils.java b/chromium/base/android/java/src/org/chromium/base/SysUtils.java
new file mode 100644
index 00000000000..7af5a40069a
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/SysUtils.java
@@ -0,0 +1,30 @@
+// 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.app.ActivityManager;
+import android.app.ActivityManager.MemoryInfo;
+import android.content.Context;
+import android.os.Build;
+
+/**
+ * Exposes system related information about the current device.
+ */
+public class SysUtils {
+ private static Boolean sLowEndDevice;
+
+ private SysUtils() { }
+
+ /**
+ * @return Whether or not this device should be considered a low end device.
+ */
+ public static boolean isLowEndDevice() {
+ if (sLowEndDevice == null) sLowEndDevice = nativeIsLowEndDevice();
+
+ return sLowEndDevice.booleanValue();
+ }
+
+ private static native boolean nativeIsLowEndDevice();
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/SystemMessageHandler.java b/chromium/base/android/java/src/org/chromium/base/SystemMessageHandler.java
new file mode 100644
index 00000000000..fc25ff8457e
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/SystemMessageHandler.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 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.Message;
+import android.os.SystemClock;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+class SystemMessageHandler extends Handler {
+
+ private static final int TIMER_MESSAGE = 1;
+ private static final int DELAYED_TIMER_MESSAGE = 2;
+
+ // Native class pointer set by the constructor of the SharedClient native class.
+ private int mMessagePumpDelegateNative = 0;
+
+ private SystemMessageHandler(int messagePumpDelegateNative) {
+ mMessagePumpDelegateNative = messagePumpDelegateNative;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ nativeDoRunLoopOnce(mMessagePumpDelegateNative);
+ }
+
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private void setTimer() {
+ sendEmptyMessage(TIMER_MESSAGE);
+ }
+
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private void setDelayedTimer(long millis) {
+ removeMessages(DELAYED_TIMER_MESSAGE);
+ sendEmptyMessageDelayed(DELAYED_TIMER_MESSAGE, millis);
+ }
+
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private void removeTimer() {
+ removeMessages(TIMER_MESSAGE);
+ }
+
+ @CalledByNative
+ private static SystemMessageHandler create(int messagePumpDelegateNative) {
+ return new SystemMessageHandler(messagePumpDelegateNative);
+ }
+
+ private native void nativeDoRunLoopOnce(int messagePumpDelegateNative);
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/ThreadUtils.java b/chromium/base/android/java/src/org/chromium/base/ThreadUtils.java
new file mode 100644
index 00000000000..a880ede1e3d
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/ThreadUtils.java
@@ -0,0 +1,172 @@
+// Copyright (c) 2012 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.Looper;
+import android.os.Process;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
+/**
+ * Helper methods to deal with threading related tasks.
+ */
+public class ThreadUtils {
+
+ /**
+ * Run the supplied Runnable on the main thread. The method will block until the Runnable
+ * completes.
+ *
+ * @param r The Runnable to run.
+ */
+ public static void runOnUiThreadBlocking(final Runnable r) {
+ if (runningOnUiThread()) {
+ r.run();
+ } else {
+ FutureTask<Void> task = new FutureTask<Void>(r, null);
+ postOnUiThread(task);
+ try {
+ task.get();
+ } catch (Exception e) {
+ throw new RuntimeException("Exception occured while waiting for runnable", e);
+ }
+ }
+ }
+
+ /**
+ * Run the supplied Callable on the main thread, wrapping any exceptions in a RuntimeException.
+ * The method will block until the Callable completes.
+ *
+ * @param c The Callable to run
+ * @return The result of the callable
+ */
+ public static <T> T runOnUiThreadBlockingNoException(Callable<T> c) {
+ try {
+ return runOnUiThreadBlocking(c);
+ } catch (ExecutionException e) {
+ throw new RuntimeException("Error occured waiting for callable", e);
+ }
+ }
+
+ /**
+ * Run the supplied Callable on the main thread, The method will block until the Callable
+ * completes.
+ *
+ * @param c The Callable to run
+ * @return The result of the callable
+ * @throws ExecutionException c's exception
+ */
+ public static <T> T runOnUiThreadBlocking(Callable<T> c) throws ExecutionException {
+ FutureTask<T> task = new FutureTask<T>(c);
+ runOnUiThread(task);
+ try {
+ return task.get();
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted waiting for callable", e);
+ }
+ }
+
+ /**
+ * Run the supplied FutureTask on the main thread. The method will block only if the current
+ * thread is the main thread.
+ *
+ * @param task The FutureTask to run
+ * @return The queried task (to aid inline construction)
+ */
+ public static <T> FutureTask<T> runOnUiThread(FutureTask<T> task) {
+ if (runningOnUiThread()) {
+ task.run();
+ } else {
+ postOnUiThread(task);
+ }
+ return task;
+ }
+
+ /**
+ * Run the supplied Callable on the main thread. The method will block only if the current
+ * thread is the main thread.
+ *
+ * @param c The Callable to run
+ * @return A FutureTask wrapping the callable to retrieve results
+ */
+ public static <T> FutureTask<T> runOnUiThread(Callable<T> c) {
+ return runOnUiThread(new FutureTask<T>(c));
+ }
+
+ /**
+ * Run the supplied Runnable on the main thread. The method will block only if the current
+ * thread is the main thread.
+ *
+ * @param r The Runnable to run
+ */
+ public static void runOnUiThread(Runnable r) {
+ if (runningOnUiThread()) {
+ r.run();
+ } else {
+ LazyHolder.sUiThreadHandler.post(r);
+ }
+ }
+
+ /**
+ * Post the supplied FutureTask to run on the main thread. The method will not block, even if
+ * called on the UI thread.
+ *
+ * @param task The FutureTask to run
+ * @return The queried task (to aid inline construction)
+ */
+ public static <T> FutureTask<T> postOnUiThread(FutureTask<T> task) {
+ LazyHolder.sUiThreadHandler.post(task);
+ return task;
+ }
+
+ /**
+ * Post the supplied Runnable to run on the main thread. The method will not block, even if
+ * called on the UI thread.
+ *
+ * @param task The Runnable to run
+ */
+ public static void postOnUiThread(Runnable r) {
+ LazyHolder.sUiThreadHandler.post(r);
+ }
+
+ /**
+ * Post the supplied Runnable to run on the main thread after the given amount of time. The
+ * method will not block, even if called on the UI thread.
+ *
+ * @param task The Runnable to run
+ * @param delayMillis The delay in milliseconds until the Runnable will be run
+ */
+ public static void postOnUiThreadDelayed(Runnable r, long delayMillis) {
+ LazyHolder.sUiThreadHandler.postDelayed(r, delayMillis);
+ }
+
+ /**
+ * Asserts that the current thread is running on the main thread.
+ */
+ public static void assertOnUiThread() {
+ assert runningOnUiThread();
+ }
+
+ /**
+ * @return true iff the current thread is the main (UI) thread.
+ */
+ public static boolean runningOnUiThread() {
+ return Looper.getMainLooper() == Looper.myLooper();
+ }
+
+ /**
+ * Set thread priority to audio.
+ */
+ @CalledByNative
+ public static void setThreadPriorityAudio(int tid) {
+ Process.setThreadPriority(tid, Process.THREAD_PRIORITY_AUDIO);
+ }
+
+ private static class LazyHolder {
+ private static Handler sUiThreadHandler = new Handler(Looper.getMainLooper());
+ }
+}
diff --git a/chromium/base/android/java/src/org/chromium/base/WeakContext.java b/chromium/base/android/java/src/org/chromium/base/WeakContext.java
new file mode 100644
index 00000000000..d660cc99406
--- /dev/null
+++ b/chromium/base/android/java/src/org/chromium/base/WeakContext.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 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.content.Context;
+
+import java.lang.ref.WeakReference;
+import java.util.concurrent.Callable;
+
+// Holds a WeakReference to Context to allow it to be GC'd.
+// Also provides utility functions to getSystemService from the UI or any
+// other thread (may return null, if the Context has been nullified).
+public class WeakContext {
+ private static WeakReference<Context> sWeakContext;
+
+ public static void initializeWeakContext(final Context context) {
+ sWeakContext = new WeakReference<Context>(context);
+ }
+
+ public static Context getContext() {
+ return sWeakContext.get();
+ }
+
+ // Returns a system service. May be called from any thread.
+ // If necessary, it will send a message to the main thread to acquire the
+ // service, and block waiting for it to complete.
+ // May return null if context is no longer available.
+ public static Object getSystemService(final String name) {
+ final Context context = sWeakContext.get();
+ if (context == null) {
+ return null;
+ }
+ if (ThreadUtils.runningOnUiThread()) {
+ return context.getSystemService(name);
+ }
+ return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Object>() {
+ @Override
+ public Object call() {
+ return context.getSystemService(name);
+ }
+ });
+ }
+}
diff --git a/chromium/base/android/java_handler_thread.cc b/chromium/base/android/java_handler_thread.cc
new file mode 100644
index 00000000000..0528fe74ba9
--- /dev/null
+++ b/chromium/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 <jni.h>
+
+#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<jint>(this),
+ reinterpret_cast<jint>(&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<MessageLoopForUI*>(message_loop_.get())->Start();
+ reinterpret_cast<base::WaitableEvent*>(event)->Signal();
+}
+
+// static
+bool JavaHandlerThread::RegisterBindings(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/java_handler_thread.h b/chromium/base/android/java_handler_thread.h
new file mode 100644
index 00000000000..9f66d660c84
--- /dev/null
+++ b/chromium/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 <jni.h>
+
+#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<base::MessageLoop> message_loop_;
+ ScopedJavaGlobalRef<jobject> java_thread_;
+};
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_THREADING_JAVA_THREAD_H_
diff --git a/chromium/base/android/javatests/src/org/chromium/base/ContextTypesTest.java b/chromium/base/android/javatests/src/org/chromium/base/ContextTypesTest.java
new file mode 100644
index 00000000000..a26b3177d83
--- /dev/null
+++ b/chromium/base/android/javatests/src/org/chromium/base/ContextTypesTest.java
@@ -0,0 +1,43 @@
+// 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.content.Context;
+import android.test.AndroidTestCase;
+import android.test.mock.MockContext;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import static org.chromium.base.ContextTypes.CONTEXT_TYPE_NORMAL;
+import static org.chromium.base.ContextTypes.CONTEXT_TYPE_WEBAPP;
+
+public class ContextTypesTest extends AndroidTestCase {
+
+ @SmallTest
+ public void testReturnsExpectedType() {
+ ContextTypes contextTypes = ContextTypes.getInstance();
+ Context normal = new MockContext();
+ Context webapp = new MockContext();
+ contextTypes.put(normal, CONTEXT_TYPE_NORMAL);
+ contextTypes.put(webapp, CONTEXT_TYPE_WEBAPP);
+ assertEquals(CONTEXT_TYPE_NORMAL, contextTypes.getType(normal));
+ assertEquals(CONTEXT_TYPE_WEBAPP, contextTypes.getType(webapp));
+ }
+
+ @SmallTest
+ public void testAbsentContextReturnsNormalType() {
+ assertEquals(CONTEXT_TYPE_NORMAL, ContextTypes.getInstance().getType(new MockContext()));
+ }
+
+ @SmallTest
+ public void testPutInvalidTypeThrowsException() {
+ boolean exceptionThrown = false;
+ try {
+ ContextTypes.getInstance().put(new MockContext(), -1);
+ } catch (IllegalArgumentException e) {
+ exceptionThrown = true;
+ }
+ assertTrue(exceptionThrown);
+ }
+}
diff --git a/chromium/base/android/javatests/src/org/chromium/base/ObserverListTest.java b/chromium/base/android/javatests/src/org/chromium/base/ObserverListTest.java
new file mode 100644
index 00000000000..10c898c2a3a
--- /dev/null
+++ b/chromium/base/android/javatests/src/org/chromium/base/ObserverListTest.java
@@ -0,0 +1,180 @@
+// 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.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.chromium.base.test.util.Feature;
+
+import java.lang.Iterable;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Tests for (@link ObserverList}.
+ */
+public class ObserverListTest extends InstrumentationTestCase {
+ interface Observer {
+ void observe(int x);
+ }
+
+ private static class Foo implements Observer {
+ private final int mScalar;
+ private int mTotal = 0;
+
+ Foo(int scalar) {
+ mScalar = scalar;
+ }
+
+ @Override
+ public void observe(int x) {
+ mTotal += x * mScalar;
+ }
+ }
+
+ /**
+ * An observer which add a given Observer object to the list when observe is called.
+ */
+ private static class FooAdder implements Observer {
+ private final ObserverList<Observer> mList;
+ private final Observer mLucky;
+
+ FooAdder(ObserverList<Observer> list, Observer oblivious) {
+ mList = list;
+ mLucky = oblivious;
+ }
+
+ @Override
+ public void observe(int x) {
+ mList.addObserver(mLucky);
+ }
+ }
+
+ /**
+ * An observer which removes a given Observer object from the list when observe is called.
+ */
+ private static class FooRemover implements Observer {
+ private final ObserverList<Observer> mList;
+ private final Observer mDoomed;
+
+ FooRemover(ObserverList<Observer> list, Observer innocent) {
+ mList = list;
+ mDoomed = innocent;
+ }
+
+ @Override
+ public void observe(int x) {
+ mList.removeObserver(mDoomed);
+ }
+ }
+
+ private static <T> int getSizeOfIterable(Iterable<T> iterable) {
+ int num = 0;
+ for (T el : iterable)
+ num++;
+ return num;
+ }
+
+ @SmallTest
+ @Feature({"Android-AppBase"})
+ public void testRemoveWhileIteration() {
+ ObserverList<Observer> observerList = new ObserverList<Observer>();
+ Foo a = new Foo(1);
+ Foo b = new Foo(-1);
+ Foo c = new Foo(1);
+ Foo d = new Foo(-1);
+ Foo e = new Foo(-1);
+ FooRemover evil = new FooRemover(observerList, c);
+
+ observerList.addObserver(a);
+ observerList.addObserver(b);
+
+ for (Observer obs : observerList)
+ obs.observe(10);
+
+ // Removing an observer not in the list should do nothing.
+ observerList.removeObserver(e);
+
+ observerList.addObserver(evil);
+ observerList.addObserver(c);
+ observerList.addObserver(d);
+
+ for (Observer obs : observerList)
+ obs.observe(10);
+
+ // observe should be called twice on a.
+ assertEquals(20, a.mTotal);
+ // observe should be called twice on b.
+ assertEquals(-20, b.mTotal);
+ // evil removed c from the observerList before it got any callbacks.
+ assertEquals(0, c.mTotal);
+ // observe should be called once on d.
+ assertEquals(-10, d.mTotal);
+ // e was never added to the list, observe should not be called.
+ assertEquals(0, e.mTotal);
+ }
+
+ @SmallTest
+ @Feature({"Android-AppBase"})
+ public void testAddWhileIteration() {
+ ObserverList<Observer> observerList = new ObserverList<Observer>();
+ Foo a = new Foo(1);
+ Foo b = new Foo(-1);
+ Foo c = new Foo(1);
+ FooAdder evil = new FooAdder(observerList, c);
+
+ observerList.addObserver(evil);
+ observerList.addObserver(a);
+ observerList.addObserver(b);
+
+ for (Observer obs : observerList)
+ obs.observe(10);
+
+ assertTrue(observerList.hasObserver(c));
+ assertEquals(10, a.mTotal);
+ assertEquals(-10, b.mTotal);
+ assertEquals(0, c.mTotal);
+ }
+
+ @SmallTest
+ @Feature({"Android-AppBase"})
+ public void testIterator() {
+ ObserverList<Integer> observerList = new ObserverList<Integer>();
+ observerList.addObserver(5);
+ observerList.addObserver(10);
+ observerList.addObserver(15);
+ assertEquals(3, getSizeOfIterable(observerList));
+
+ observerList.removeObserver(10);
+ assertEquals(2, getSizeOfIterable(observerList));
+
+ Iterator<Integer> it = observerList.iterator();
+ assertTrue(it.hasNext());
+ assertTrue(5 == it.next());
+ assertTrue(it.hasNext());
+ assertTrue(15 == it.next());
+ assertFalse(it.hasNext());
+
+ boolean removeExceptionThrown = false;
+ try {
+ it.remove();
+ fail("Expecting UnsupportedOperationException to be thrown here.");
+ } catch (UnsupportedOperationException e) {
+ removeExceptionThrown = true;
+ }
+ assertTrue(removeExceptionThrown);
+ assertEquals(2, getSizeOfIterable(observerList));
+
+ boolean noElementExceptionThrown = false;
+ try {
+ it.next();
+ fail("Expecting NoSuchElementException to be thrown here.");
+ } catch (NoSuchElementException e) {
+ noElementExceptionThrown = true;
+ }
+ assertTrue(noElementExceptionThrown);
+ }
+}
diff --git a/chromium/base/android/jni_android.cc b/chromium/base/android/jni_android.cc
new file mode 100644
index 00000000000..fc89b5403f8
--- /dev/null
+++ b/chromium/base/android/jni_android.cc
@@ -0,0 +1,323 @@
+// Copyright (c) 2012 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/jni_android.h"
+
+#include <map>
+
+#include "base/android/build_info.h"
+#include "base/android/jni_string.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/platform_thread.h"
+
+namespace {
+using base::android::GetClass;
+using base::android::MethodID;
+using base::android::ScopedJavaLocalRef;
+
+struct MethodIdentifier {
+ const char* class_name;
+ const char* method;
+ const char* jni_signature;
+
+ bool operator<(const MethodIdentifier& other) const {
+ int r = strcmp(class_name, other.class_name);
+ if (r < 0) {
+ return true;
+ } else if (r > 0) {
+ return false;
+ }
+
+ r = strcmp(method, other.method);
+ if (r < 0) {
+ return true;
+ } else if (r > 0) {
+ return false;
+ }
+
+ return strcmp(jni_signature, other.jni_signature) < 0;
+ }
+};
+
+typedef std::map<MethodIdentifier, jmethodID> MethodIDMap;
+
+const base::subtle::AtomicWord kUnlocked = 0;
+const base::subtle::AtomicWord kLocked = 1;
+base::subtle::AtomicWord g_method_id_map_lock = kUnlocked;
+JavaVM* g_jvm = NULL;
+// Leak the global app context, as it is used from a non-joinable worker thread
+// that may still be running at shutdown. There is no harm in doing this.
+base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky
+ g_application_context = LAZY_INSTANCE_INITIALIZER;
+base::LazyInstance<MethodIDMap> g_method_id_map = LAZY_INSTANCE_INITIALIZER;
+
+std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) {
+ ScopedJavaLocalRef<jclass> throwable_clazz =
+ GetClass(env, "java/lang/Throwable");
+ jmethodID throwable_printstacktrace =
+ MethodID::Get<MethodID::TYPE_INSTANCE>(
+ env, throwable_clazz.obj(), "printStackTrace",
+ "(Ljava/io/PrintStream;)V");
+
+ // Create an instance of ByteArrayOutputStream.
+ ScopedJavaLocalRef<jclass> bytearray_output_stream_clazz =
+ GetClass(env, "java/io/ByteArrayOutputStream");
+ jmethodID bytearray_output_stream_constructor =
+ MethodID::Get<MethodID::TYPE_INSTANCE>(
+ env, bytearray_output_stream_clazz.obj(), "<init>", "()V");
+ jmethodID bytearray_output_stream_tostring =
+ MethodID::Get<MethodID::TYPE_INSTANCE>(
+ env, bytearray_output_stream_clazz.obj(), "toString",
+ "()Ljava/lang/String;");
+ ScopedJavaLocalRef<jobject> bytearray_output_stream(env,
+ env->NewObject(bytearray_output_stream_clazz.obj(),
+ bytearray_output_stream_constructor));
+
+ // Create an instance of PrintStream.
+ ScopedJavaLocalRef<jclass> printstream_clazz =
+ GetClass(env, "java/io/PrintStream");
+ jmethodID printstream_constructor =
+ MethodID::Get<MethodID::TYPE_INSTANCE>(
+ env, printstream_clazz.obj(), "<init>",
+ "(Ljava/io/OutputStream;)V");
+ ScopedJavaLocalRef<jobject> printstream(env,
+ env->NewObject(printstream_clazz.obj(), printstream_constructor,
+ bytearray_output_stream.obj()));
+
+ // Call Throwable.printStackTrace(PrintStream)
+ env->CallVoidMethod(java_throwable, throwable_printstacktrace,
+ printstream.obj());
+
+ // Call ByteArrayOutputStream.toString()
+ ScopedJavaLocalRef<jstring> exception_string(
+ env, static_cast<jstring>(
+ env->CallObjectMethod(bytearray_output_stream.obj(),
+ bytearray_output_stream_tostring)));
+
+ return ConvertJavaStringToUTF8(exception_string);
+}
+
+} // namespace
+
+namespace base {
+namespace android {
+
+JNIEnv* AttachCurrentThread() {
+ DCHECK(g_jvm);
+ JNIEnv* env = NULL;
+ jint ret = g_jvm->AttachCurrentThread(&env, NULL);
+ DCHECK_EQ(JNI_OK, ret);
+ return env;
+}
+
+void DetachFromVM() {
+ // Ignore the return value, if the thread is not attached, DetachCurrentThread
+ // will fail. But it is ok as the native thread may never be attached.
+ if (g_jvm)
+ g_jvm->DetachCurrentThread();
+}
+
+void InitVM(JavaVM* vm) {
+ DCHECK(!g_jvm);
+ g_jvm = vm;
+}
+
+void InitApplicationContext(const JavaRef<jobject>& context) {
+ DCHECK(g_application_context.Get().is_null());
+ g_application_context.Get().Reset(context);
+}
+
+const jobject GetApplicationContext() {
+ DCHECK(!g_application_context.Get().is_null());
+ return g_application_context.Get().obj();
+}
+
+ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) {
+ jclass clazz = env->FindClass(class_name);
+ CHECK(!ClearException(env) && clazz) << "Failed to find class " << class_name;
+ return ScopedJavaLocalRef<jclass>(env, clazz);
+}
+
+bool HasClass(JNIEnv* env, const char* class_name) {
+ ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name));
+ if (!clazz.obj()) {
+ ClearException(env);
+ return false;
+ }
+ bool error = ClearException(env);
+ DCHECK(!error);
+ return true;
+}
+
+template<MethodID::Type type>
+jmethodID MethodID::Get(JNIEnv* env,
+ jclass clazz,
+ const char* method_name,
+ const char* jni_signature) {
+ jmethodID id = type == TYPE_STATIC ?
+ env->GetStaticMethodID(clazz, method_name, jni_signature) :
+ env->GetMethodID(clazz, method_name, jni_signature);
+ CHECK(base::android::ClearException(env) || id) <<
+ "Failed to find " <<
+ (type == TYPE_STATIC ? "static " : "") <<
+ "method " << method_name << " " << jni_signature;
+ return id;
+}
+
+// If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call
+// into ::Get() above. If there's a race, it's ok since the values are the same
+// (and the duplicated effort will happen only once).
+template<MethodID::Type type>
+jmethodID MethodID::LazyGet(JNIEnv* env,
+ jclass clazz,
+ const char* method_name,
+ const char* jni_signature,
+ base::subtle::AtomicWord* atomic_method_id) {
+ COMPILE_ASSERT(sizeof(subtle::AtomicWord) >= sizeof(jmethodID),
+ AtomicWord_SmallerThan_jMethodID);
+ subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_method_id);
+ if (value)
+ return reinterpret_cast<jmethodID>(value);
+ jmethodID id = MethodID::Get<type>(env, clazz, method_name, jni_signature);
+ base::subtle::Release_Store(
+ atomic_method_id, reinterpret_cast<subtle::AtomicWord>(id));
+ return id;
+}
+
+// Various template instantiations.
+template jmethodID MethodID::Get<MethodID::TYPE_STATIC>(
+ JNIEnv* env, jclass clazz, const char* method_name,
+ const char* jni_signature);
+
+template jmethodID MethodID::Get<MethodID::TYPE_INSTANCE>(
+ JNIEnv* env, jclass clazz, const char* method_name,
+ const char* jni_signature);
+
+template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>(
+ JNIEnv* env, jclass clazz, const char* method_name,
+ const char* jni_signature, base::subtle::AtomicWord* atomic_method_id);
+
+template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>(
+ JNIEnv* env, jclass clazz, const char* method_name,
+ const char* jni_signature, base::subtle::AtomicWord* atomic_method_id);
+
+jfieldID GetFieldID(JNIEnv* env,
+ const JavaRef<jclass>& clazz,
+ const char* field_name,
+ const char* jni_signature) {
+ jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature);
+ CHECK(!ClearException(env) && field_id) << "Failed to find field " <<
+ field_name << " " << jni_signature;
+ return field_id;
+}
+
+bool HasField(JNIEnv* env,
+ const JavaRef<jclass>& clazz,
+ const char* field_name,
+ const char* jni_signature) {
+ jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature);
+ if (!field_id) {
+ ClearException(env);
+ return false;
+ }
+ bool error = ClearException(env);
+ DCHECK(!error);
+ return true;
+}
+
+jfieldID GetStaticFieldID(JNIEnv* env,
+ const JavaRef<jclass>& clazz,
+ const char* field_name,
+ const char* jni_signature) {
+ jfieldID field_id =
+ env->GetStaticFieldID(clazz.obj(), field_name, jni_signature);
+ CHECK(!ClearException(env) && field_id) << "Failed to find static field " <<
+ field_name << " " << jni_signature;
+ return field_id;
+}
+
+jmethodID GetMethodIDFromClassName(JNIEnv* env,
+ const char* class_name,
+ const char* method,
+ const char* jni_signature) {
+ MethodIdentifier key;
+ key.class_name = class_name;
+ key.method = method;
+ key.jni_signature = jni_signature;
+
+ MethodIDMap* map = g_method_id_map.Pointer();
+ bool found = false;
+
+ while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock,
+ kUnlocked,
+ kLocked) != kUnlocked) {
+ base::PlatformThread::YieldCurrentThread();
+ }
+ MethodIDMap::const_iterator iter = map->find(key);
+ if (iter != map->end()) {
+ found = true;
+ }
+ base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked);
+
+ // Addition to the map does not invalidate this iterator.
+ if (found) {
+ return iter->second;
+ }
+
+ ScopedJavaLocalRef<jclass> clazz(env, env->FindClass(class_name));
+ jmethodID id = MethodID::Get<MethodID::TYPE_INSTANCE>(
+ env, clazz.obj(), method, jni_signature);
+
+ while (base::subtle::Acquire_CompareAndSwap(&g_method_id_map_lock,
+ kUnlocked,
+ kLocked) != kUnlocked) {
+ base::PlatformThread::YieldCurrentThread();
+ }
+ // Another thread may have populated the map already.
+ std::pair<MethodIDMap::const_iterator, bool> result =
+ map->insert(std::make_pair(key, id));
+ DCHECK_EQ(id, result.first->second);
+ base::subtle::Release_Store(&g_method_id_map_lock, kUnlocked);
+
+ return id;
+}
+
+bool HasException(JNIEnv* env) {
+ return env->ExceptionCheck() != JNI_FALSE;
+}
+
+bool ClearException(JNIEnv* env) {
+ if (!HasException(env))
+ return false;
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return true;
+}
+
+void CheckException(JNIEnv* env) {
+ if (!HasException(env)) return;
+
+ // Exception has been found, might as well tell breakpad about it.
+ jthrowable java_throwable = env->ExceptionOccurred();
+ if (!java_throwable) {
+ // Do nothing but return false.
+ CHECK(false);
+ }
+
+ // Clear the pending exception, since a local reference is now held.
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+
+ // Set the exception_string in BuildInfo so that breakpad can read it.
+ // RVO should avoid any extra copies of the exception string.
+ base::android::BuildInfo::GetInstance()->set_java_exception_info(
+ GetJavaExceptionInfo(env, java_throwable));
+
+ // Now, feel good about it and die.
+ CHECK(false);
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/jni_android.h b/chromium/base/android/jni_android.h
new file mode 100644
index 00000000000..e8f68acfb7c
--- /dev/null
+++ b/chromium/base/android/jni_android.h
@@ -0,0 +1,132 @@
+// Copyright (c) 2012 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_ANDROID_JNI_ANDROID_H_
+#define BASE_ANDROID_JNI_ANDROID_H_
+
+#include <jni.h>
+#include <sys/types.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/atomicops.h"
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+
+namespace base {
+namespace android {
+
+// Used to mark symbols to be exported in a shared library's symbol table.
+#define JNI_EXPORT __attribute__ ((visibility("default")))
+
+// Contains the registration method information for initializing JNI bindings.
+struct RegistrationMethod {
+ const char* name;
+ bool (*func)(JNIEnv* env);
+};
+
+// Attach the current thread to the VM (if necessary) and return the JNIEnv*.
+BASE_EXPORT JNIEnv* AttachCurrentThread();
+
+// Detach the current thread from VM if it is attached.
+BASE_EXPORT void DetachFromVM();
+
+// Initializes the global JVM. It is not necessarily called before
+// InitApplicationContext().
+BASE_EXPORT void InitVM(JavaVM* vm);
+
+// Initializes the global application context object. The |context| can be any
+// valid reference to the application context. Internally holds a global ref to
+// the context. InitVM and InitApplicationContext maybe called in either order.
+BASE_EXPORT void InitApplicationContext(const JavaRef<jobject>& context);
+
+// Gets a global ref to the application context set with
+// InitApplicationContext(). Ownership is retained by the function - the caller
+// must NOT release it.
+const BASE_EXPORT jobject GetApplicationContext();
+
+// Finds the class named |class_name| and returns it.
+// Use this method instead of invoking directly the JNI FindClass method (to
+// prevent leaking local references).
+// This method triggers a fatal assertion if the class could not be found.
+// Use HasClass if you need to check whether the class exists.
+BASE_EXPORT ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env,
+ const char* class_name);
+
+// Returns true iff the class |class_name| could be found.
+BASE_EXPORT bool HasClass(JNIEnv* env, const char* class_name);
+
+// This class is a wrapper for JNIEnv Get(Static)MethodID.
+class BASE_EXPORT MethodID {
+ public:
+ enum Type {
+ TYPE_STATIC,
+ TYPE_INSTANCE,
+ };
+
+ // Returns the method ID for the method with the specified name and signature.
+ // This method triggers a fatal assertion if the method could not be found.
+ template<Type type>
+ static jmethodID Get(JNIEnv* env,
+ jclass clazz,
+ const char* method_name,
+ const char* jni_signature);
+
+ // The caller is responsible to zero-initialize |atomic_method_id|.
+ // It's fine to simultaneously call this on multiple threads referencing the
+ // same |atomic_method_id|.
+ template<Type type>
+ static jmethodID LazyGet(JNIEnv* env,
+ jclass clazz,
+ const char* method_name,
+ const char* jni_signature,
+ base::subtle::AtomicWord* atomic_method_id);
+};
+
+// Gets the method ID from the class name. Clears the pending Java exception
+// and returns NULL if the method is not found. Caches results. Note that
+// MethodID::Get() above avoids a class lookup, but does not cache results.
+// Strings passed to this function are held in the cache and MUST remain valid
+// beyond the duration of all future calls to this function, across all
+// threads. In practice, this means that the function should only be used with
+// string constants.
+BASE_EXPORT jmethodID GetMethodIDFromClassName(JNIEnv* env,
+ const char* class_name,
+ const char* method,
+ const char* jni_signature);
+
+// Gets the field ID for a class field.
+// This method triggers a fatal assertion if the field could not be found.
+BASE_EXPORT jfieldID GetFieldID(JNIEnv* env,
+ const JavaRef<jclass>& clazz,
+ const char* field_name,
+ const char* jni_signature);
+
+// Returns true if |clazz| as a field with the given name and signature.
+// TODO(jcivelli): Determine whether we explicitly have to pass the environment.
+BASE_EXPORT bool HasField(JNIEnv* env,
+ const JavaRef<jclass>& clazz,
+ const char* field_name,
+ const char* jni_signature);
+
+// Gets the field ID for a static class field.
+// This method triggers a fatal assertion if the field could not be found.
+BASE_EXPORT jfieldID GetStaticFieldID(JNIEnv* env,
+ const JavaRef<jclass>& clazz,
+ const char* field_name,
+ const char* jni_signature);
+
+// Returns true if an exception is pending in the provided JNIEnv*.
+BASE_EXPORT bool HasException(JNIEnv* env);
+
+// If an exception is pending in the provided JNIEnv*, this function clears it
+// and returns true.
+BASE_EXPORT bool ClearException(JNIEnv* env);
+
+// This function will call CHECK() macro if there's any pending exception.
+BASE_EXPORT void CheckException(JNIEnv* env);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_JNI_ANDROID_H_
diff --git a/chromium/base/android/jni_android_unittest.cc b/chromium/base/android/jni_android_unittest.cc
new file mode 100644
index 00000000000..920b3956590
--- /dev/null
+++ b/chromium/base/android/jni_android_unittest.cc
@@ -0,0 +1,141 @@
+// Copyright (c) 2012 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/jni_android.h"
+
+#include "base/at_exit.h"
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace android {
+
+namespace {
+
+const char kJavaLangObject[] = "java/lang/Object";
+const char kGetClass[] = "getClass";
+const char kToString[] = "toString";
+const char kReturningJavaLangClass[] = "()Ljava/lang/Class;";
+const char kReturningJavaLangString[] = "()Ljava/lang/String;";
+
+const char* g_last_method;
+const char* g_last_jni_signature;
+jmethodID g_last_method_id;
+
+const JNINativeInterface* g_previous_functions;
+
+jmethodID GetMethodIDWrapper(JNIEnv* env, jclass clazz, const char* method,
+ const char* jni_signature) {
+ g_last_method = method;
+ g_last_jni_signature = jni_signature;
+ g_last_method_id = g_previous_functions->GetMethodID(env, clazz, method,
+ jni_signature);
+ return g_last_method_id;
+}
+
+} // namespace
+
+class JNIAndroidTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ JNIEnv* env = AttachCurrentThread();
+ g_previous_functions = env->functions;
+ hooked_functions = *g_previous_functions;
+ env->functions = &hooked_functions;
+ hooked_functions.GetMethodID = &GetMethodIDWrapper;
+ }
+
+ virtual void TearDown() {
+ JNIEnv* env = AttachCurrentThread();
+ env->functions = g_previous_functions;
+ Reset();
+ }
+
+ void Reset() {
+ g_last_method = 0;
+ g_last_jni_signature = 0;
+ g_last_method_id = NULL;
+ }
+ // Needed to cleanup the cached method map in the implementation between
+ // runs (e.g. if using --gtest_repeat)
+ base::ShadowingAtExitManager exit_manager;
+ // From JellyBean release, the instance of this struct provided in JNIEnv is
+ // read-only, so we deep copy it to allow individual functions to be hooked.
+ JNINativeInterface hooked_functions;
+};
+
+TEST_F(JNIAndroidTest, GetMethodIDFromClassNameCaching) {
+ JNIEnv* env = AttachCurrentThread();
+
+ Reset();
+ jmethodID id1 = GetMethodIDFromClassName(env, kJavaLangObject, kGetClass,
+ kReturningJavaLangClass);
+ EXPECT_STREQ(kGetClass, g_last_method);
+ EXPECT_STREQ(kReturningJavaLangClass, g_last_jni_signature);
+ EXPECT_EQ(g_last_method_id, id1);
+
+ Reset();
+ jmethodID id2 = GetMethodIDFromClassName(env, kJavaLangObject, kGetClass,
+ kReturningJavaLangClass);
+ EXPECT_STREQ(0, g_last_method);
+ EXPECT_STREQ(0, g_last_jni_signature);
+ EXPECT_EQ(NULL, g_last_method_id);
+ EXPECT_EQ(id1, id2);
+
+ Reset();
+ jmethodID id3 = GetMethodIDFromClassName(env, kJavaLangObject, kToString,
+ kReturningJavaLangString);
+ EXPECT_STREQ(kToString, g_last_method);
+ EXPECT_STREQ(kReturningJavaLangString, g_last_jni_signature);
+ EXPECT_EQ(g_last_method_id, id3);
+}
+
+namespace {
+
+base::subtle::AtomicWord g_atomic_id = 0;
+int LazyMethodIDCall(JNIEnv* env, jclass clazz, int p) {
+ jmethodID id = base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_STATIC>(
+ env, clazz,
+ "abs",
+ "(I)I",
+ &g_atomic_id);
+
+ return env->CallStaticIntMethod(clazz, id, p);
+}
+
+int MethodIDCall(JNIEnv* env, jclass clazz, jmethodID id, int p) {
+ return env->CallStaticIntMethod(clazz, id, p);
+}
+
+} // namespace
+
+TEST(JNIAndroidMicrobenchmark, MethodId) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jclass> clazz(GetClass(env, "java/lang/Math"));
+ base::Time start_lazy = base::Time::Now();
+ int o = 0;
+ for (int i = 0; i < 1024; ++i)
+ o += LazyMethodIDCall(env, clazz.obj(), i);
+ base::Time end_lazy = base::Time::Now();
+
+ jmethodID id = reinterpret_cast<jmethodID>(g_atomic_id);
+ base::Time start = base::Time::Now();
+ for (int i = 0; i < 1024; ++i)
+ o += MethodIDCall(env, clazz.obj(), id, i);
+ base::Time end = base::Time::Now();
+
+ // On a Galaxy Nexus, results were in the range of:
+ // JNI LazyMethodIDCall (us) 1984
+ // JNI MethodIDCall (us) 1861
+ LOG(ERROR) << "JNI LazyMethodIDCall (us) " <<
+ base::TimeDelta(end_lazy - start_lazy).InMicroseconds();
+ LOG(ERROR) << "JNI MethodIDCall (us) " <<
+ base::TimeDelta(end - start).InMicroseconds();
+ LOG(ERROR) << "JNI " << o;
+}
+
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/jni_array.cc b/chromium/base/android/jni_array.cc
new file mode 100644
index 00000000000..bbe00c61290
--- /dev/null
+++ b/chromium/base/android/jni_array.cc
@@ -0,0 +1,186 @@
+// Copyright (c) 2012 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/jni_array.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/logging.h"
+
+namespace base {
+namespace android {
+
+ScopedJavaLocalRef<jbyteArray> ToJavaByteArray(
+ JNIEnv* env, const uint8* bytes, size_t len) {
+ jbyteArray byte_array = env->NewByteArray(len);
+ CheckException(env);
+ DCHECK(byte_array);
+
+ jbyte* elements = env->GetByteArrayElements(byte_array, NULL);
+ memcpy(elements, bytes, len);
+ env->ReleaseByteArrayElements(byte_array, elements, 0);
+ CheckException(env);
+
+ return ScopedJavaLocalRef<jbyteArray>(env, byte_array);
+}
+
+ScopedJavaLocalRef<jlongArray> ToJavaLongArray(
+ JNIEnv* env, const int64* longs, size_t len) {
+ jlongArray long_array = env->NewLongArray(len);
+ CheckException(env);
+ DCHECK(long_array);
+
+ jlong* elements = env->GetLongArrayElements(long_array, NULL);
+ memcpy(elements, longs, len * sizeof(*longs));
+ env->ReleaseLongArrayElements(long_array, elements, 0);
+ CheckException(env);
+
+ return ScopedJavaLocalRef<jlongArray>(env, long_array);
+}
+
+// Returns a new Java long array converted from the given int64 array.
+BASE_EXPORT ScopedJavaLocalRef<jlongArray> ToJavaLongArray(
+ JNIEnv* env, const std::vector<int64>& longs) {
+ return ToJavaLongArray(env, longs.begin(), longs.size());
+}
+
+ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfByteArray(
+ JNIEnv* env, const std::vector<std::string>& v) {
+ ScopedJavaLocalRef<jclass> byte_array_clazz = GetClass(env, "[B");
+ jobjectArray joa = env->NewObjectArray(v.size(),
+ byte_array_clazz.obj(), NULL);
+ CheckException(env);
+
+ for (size_t i = 0; i < v.size(); ++i) {
+ ScopedJavaLocalRef<jbyteArray> byte_array = ToJavaByteArray(env,
+ reinterpret_cast<const uint8*>(v[i].data()), v[i].length());
+ env->SetObjectArrayElement(joa, i, byte_array.obj());
+ }
+ return ScopedJavaLocalRef<jobjectArray>(env, joa);
+}
+
+ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfStrings(
+ JNIEnv* env, const std::vector<std::string>& v) {
+ ScopedJavaLocalRef<jclass> string_clazz = GetClass(env, "java/lang/String");
+ jobjectArray joa = env->NewObjectArray(v.size(), string_clazz.obj(), NULL);
+ CheckException(env);
+
+ for (size_t i = 0; i < v.size(); ++i) {
+ ScopedJavaLocalRef<jstring> item = ConvertUTF8ToJavaString(env, v[i]);
+ env->SetObjectArrayElement(joa, i, item.obj());
+ }
+ return ScopedJavaLocalRef<jobjectArray>(env, joa);
+}
+
+ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfStrings(
+ JNIEnv* env, const std::vector<string16>& v) {
+ ScopedJavaLocalRef<jclass> string_clazz = GetClass(env, "java/lang/String");
+ jobjectArray joa = env->NewObjectArray(v.size(), string_clazz.obj(), NULL);
+ CheckException(env);
+
+ for (size_t i = 0; i < v.size(); ++i) {
+ ScopedJavaLocalRef<jstring> item = ConvertUTF16ToJavaString(env, v[i]);
+ env->SetObjectArrayElement(joa, i, item.obj());
+ }
+ return ScopedJavaLocalRef<jobjectArray>(env, joa);
+}
+
+void AppendJavaStringArrayToStringVector(JNIEnv* env,
+ jobjectArray array,
+ std::vector<string16>* out) {
+ DCHECK(out);
+ if (!array)
+ return;
+ jsize len = env->GetArrayLength(array);
+ size_t back = out->size();
+ out->resize(back + len);
+ for (jsize i = 0; i < len; ++i) {
+ ScopedJavaLocalRef<jstring> str(env,
+ static_cast<jstring>(env->GetObjectArrayElement(array, i)));
+ ConvertJavaStringToUTF16(env, str.obj(), &((*out)[back + i]));
+ }
+}
+
+void AppendJavaStringArrayToStringVector(JNIEnv* env,
+ jobjectArray array,
+ std::vector<std::string>* out) {
+ DCHECK(out);
+ if (!array)
+ return;
+ jsize len = env->GetArrayLength(array);
+ size_t back = out->size();
+ out->resize(back + len);
+ for (jsize i = 0; i < len; ++i) {
+ ScopedJavaLocalRef<jstring> str(env,
+ static_cast<jstring>(env->GetObjectArrayElement(array, i)));
+ ConvertJavaStringToUTF8(env, str.obj(), &((*out)[back + i]));
+ }
+}
+
+void AppendJavaByteArrayToByteVector(JNIEnv* env,
+ jbyteArray byte_array,
+ std::vector<uint8>* out) {
+ DCHECK(out);
+ if (!byte_array)
+ return;
+ jsize len = env->GetArrayLength(byte_array);
+ jbyte* bytes = env->GetByteArrayElements(byte_array, NULL);
+ out->insert(out->end(), bytes, bytes + len);
+ env->ReleaseByteArrayElements(byte_array, bytes, JNI_ABORT);
+}
+
+void JavaByteArrayToByteVector(JNIEnv* env,
+ jbyteArray byte_array,
+ std::vector<uint8>* out) {
+ DCHECK(out);
+ out->clear();
+ AppendJavaByteArrayToByteVector(env, byte_array, out);
+}
+
+void JavaIntArrayToIntVector(JNIEnv* env,
+ jintArray int_array,
+ std::vector<int>* out) {
+ DCHECK(out);
+ out->clear();
+ jsize len = env->GetArrayLength(int_array);
+ jint* ints = env->GetIntArrayElements(int_array, NULL);
+ for (jsize i = 0; i < len; ++i) {
+ out->push_back(static_cast<int>(ints[i]));
+ }
+ env->ReleaseIntArrayElements(int_array, ints, JNI_ABORT);
+}
+
+void JavaFloatArrayToFloatVector(JNIEnv* env,
+ jfloatArray float_array,
+ std::vector<float>* out) {
+ DCHECK(out);
+ out->clear();
+ jsize len = env->GetArrayLength(float_array);
+ jfloat* floats = env->GetFloatArrayElements(float_array, NULL);
+ for (jsize i = 0; i < len; ++i) {
+ out->push_back(static_cast<float>(floats[i]));
+ }
+ env->ReleaseFloatArrayElements(float_array, floats, JNI_ABORT);
+}
+
+void JavaArrayOfByteArrayToStringVector(
+ JNIEnv* env,
+ jobjectArray array,
+ std::vector<std::string>* out) {
+ DCHECK(out);
+ out->clear();
+ jsize len = env->GetArrayLength(array);
+ out->resize(len);
+ for (jsize i = 0; i < len; ++i) {
+ jbyteArray bytes_array = static_cast<jbyteArray>(
+ env->GetObjectArrayElement(array, i));
+ jsize bytes_len = env->GetArrayLength(bytes_array);
+ jbyte* bytes = env->GetByteArrayElements(bytes_array, NULL);
+ (*out)[i].assign(reinterpret_cast<const char*>(bytes), bytes_len);
+ env->ReleaseByteArrayElements(bytes_array, bytes, JNI_ABORT);
+ }
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/jni_array.h b/chromium/base/android/jni_array.h
new file mode 100644
index 00000000000..efa6aa8ffe8
--- /dev/null
+++ b/chromium/base/android/jni_array.h
@@ -0,0 +1,86 @@
+// Copyright (c) 2012 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_ANDROID_JNI_ARRAY_H_
+#define BASE_ANDROID_JNI_ARRAY_H_
+
+#include <jni.h>
+#include <string>
+#include <vector>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+
+namespace base {
+namespace android {
+
+// Returns a new Java byte array converted from the given bytes array.
+BASE_EXPORT ScopedJavaLocalRef<jbyteArray> ToJavaByteArray(
+ JNIEnv* env, const uint8* bytes, size_t len);
+
+// Returns a new Java long array converted from the given int64 array.
+BASE_EXPORT ScopedJavaLocalRef<jlongArray> ToJavaLongArray(
+ JNIEnv* env, const int64* longs, size_t len);
+
+BASE_EXPORT ScopedJavaLocalRef<jlongArray> ToJavaLongArray(
+ JNIEnv* env, const std::vector<int64>& longs);
+
+// Returns a array of Java byte array converted from |v|.
+BASE_EXPORT ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfByteArray(
+ JNIEnv* env, const std::vector<std::string>& v);
+
+BASE_EXPORT ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfStrings(
+ JNIEnv* env, const std::vector<std::string>& v);
+
+BASE_EXPORT ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfStrings(
+ JNIEnv* env, const std::vector<string16>& v);
+
+// Converts a Java string array to a native array.
+BASE_EXPORT void AppendJavaStringArrayToStringVector(
+ JNIEnv* env,
+ jobjectArray array,
+ std::vector<string16>* out);
+
+BASE_EXPORT void AppendJavaStringArrayToStringVector(
+ JNIEnv* env,
+ jobjectArray array,
+ std::vector<std::string>* out);
+
+// Appends the Java bytes in |bytes_array| onto the end of |out|.
+BASE_EXPORT void AppendJavaByteArrayToByteVector(
+ JNIEnv* env,
+ jbyteArray byte_array,
+ std::vector<uint8>* out);
+
+// Replaces the content of |out| with the Java bytes in |bytes_array|.
+BASE_EXPORT void JavaByteArrayToByteVector(
+ JNIEnv* env,
+ jbyteArray byte_array,
+ std::vector<uint8>* out);
+
+// Replaces the content of |out| with the Java ints in |int_array|.
+BASE_EXPORT void JavaIntArrayToIntVector(
+ JNIEnv* env,
+ jintArray int_array,
+ std::vector<int>* out);
+
+// Replaces the content of |out| with the Java floats in |float_array|.
+BASE_EXPORT void JavaFloatArrayToFloatVector(
+ JNIEnv* env,
+ jfloatArray float_array,
+ std::vector<float>* out);
+
+// Assuming |array| is an byte[][] (array of byte arrays), replaces the
+// content of |out| with the corresponding vector of strings. No UTF-8
+// conversion is performed.
+BASE_EXPORT void JavaArrayOfByteArrayToStringVector(
+ JNIEnv* env,
+ jobjectArray array,
+ std::vector<std::string>* out);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_JNI_ARRAY_H_
diff --git a/chromium/base/android/jni_array_unittest.cc b/chromium/base/android/jni_array_unittest.cc
new file mode 100644
index 00000000000..3ac7855e402
--- /dev/null
+++ b/chromium/base/android/jni_array_unittest.cc
@@ -0,0 +1,148 @@
+// Copyright (c) 2012 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/jni_array.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace android {
+
+TEST(JniArray, BasicConversions) {
+ const uint8 kBytes[] = { 0, 1, 2, 3 };
+ const size_t kLen = arraysize(kBytes);
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jbyteArray> bytes = ToJavaByteArray(env, kBytes, kLen);
+ ASSERT_TRUE(bytes.obj());
+
+ std::vector<uint8> vec(5);
+ JavaByteArrayToByteVector(env, bytes.obj(), &vec);
+ EXPECT_EQ(4U, vec.size());
+ EXPECT_EQ(std::vector<uint8>(kBytes, kBytes + kLen), vec);
+
+ AppendJavaByteArrayToByteVector(env, bytes.obj(), &vec);
+ EXPECT_EQ(8U, vec.size());
+}
+
+void CheckLongConversion(
+ JNIEnv* env,
+ const int64* long_array,
+ const size_t len,
+ const ScopedJavaLocalRef<jlongArray>& longs) {
+ ASSERT_TRUE(longs.obj());
+
+ jsize java_array_len = env->GetArrayLength(longs.obj());
+ ASSERT_EQ(static_cast<jsize>(len), java_array_len);
+
+ jlong value;
+ for (size_t i = 0; i < len; ++i) {
+ env->GetLongArrayRegion(longs.obj(), i, 1, &value);
+ ASSERT_EQ(long_array[i], value);
+ }
+}
+
+TEST(JniArray, LongConversions) {
+ const int64 kLongs[] = { 0, 1, -1, kint64min, kint64max};
+ const size_t kLen = arraysize(kLongs);
+
+ JNIEnv* env = AttachCurrentThread();
+ CheckLongConversion(env, kLongs, kLen, ToJavaLongArray(env, kLongs, kLen));
+
+ const std::vector<int64> vec(kLongs, kLongs + kLen);
+ CheckLongConversion(env, kLongs, kLen, ToJavaLongArray(env, vec));
+}
+
+TEST(JniArray, JavaIntArrayToIntVector) {
+ const int kInts[] = {0, 1, -1};
+ const size_t kLen = arraysize(kInts);
+
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jintArray> jints(env, env->NewIntArray(kLen));
+ ASSERT_TRUE(jints.obj());
+
+ for (size_t i = 0; i < kLen; ++i) {
+ jint j = static_cast<jint>(kInts[i]);
+ env->SetIntArrayRegion(jints.obj(), i, 1, &j);
+ ASSERT_FALSE(HasException(env));
+ }
+
+ std::vector<int> ints;
+ JavaIntArrayToIntVector(env, jints.obj(), &ints);
+
+ ASSERT_EQ(static_cast<jsize>(ints.size()), env->GetArrayLength(jints.obj()));
+
+ jint value;
+ for (size_t i = 0; i < kLen; ++i) {
+ env->GetIntArrayRegion(jints.obj(), i, 1, &value);
+ ASSERT_EQ(ints[i], value);
+ }
+}
+
+TEST(JniArray, JavaFloatArrayToFloatVector) {
+ const float kFloats[] = {0.0, 0.5, -0.5};
+ const size_t kLen = arraysize(kFloats);
+
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jfloatArray> jfloats(env, env->NewFloatArray(kLen));
+ ASSERT_TRUE(jfloats.obj());
+
+ for (size_t i = 0; i < kLen; ++i) {
+ jfloat j = static_cast<jfloat>(kFloats[i]);
+ env->SetFloatArrayRegion(jfloats.obj(), i, 1, &j);
+ ASSERT_FALSE(HasException(env));
+ }
+
+ std::vector<float> floats;
+ JavaFloatArrayToFloatVector(env, jfloats.obj(), &floats);
+
+ ASSERT_EQ(static_cast<jsize>(floats.size()),
+ env->GetArrayLength(jfloats.obj()));
+
+ jfloat value;
+ for (size_t i = 0; i < kLen; ++i) {
+ env->GetFloatArrayRegion(jfloats.obj(), i, 1, &value);
+ ASSERT_EQ(floats[i], value);
+ }
+}
+
+TEST(JniArray, JavaArrayOfByteArrayToStringVector) {
+ const int kMaxItems = 50;
+ JNIEnv* env = AttachCurrentThread();
+
+ // Create a byte[][] object.
+ ScopedJavaLocalRef<jclass> byte_array_clazz(env, env->FindClass("[B"));
+ ASSERT_TRUE(byte_array_clazz.obj());
+
+ ScopedJavaLocalRef<jobjectArray> array(
+ env, env->NewObjectArray(kMaxItems, byte_array_clazz.obj(), NULL));
+ ASSERT_TRUE(array.obj());
+
+ // Create kMaxItems byte buffers.
+ char text[16];
+ for (int i = 0; i < kMaxItems; ++i) {
+ snprintf(text, sizeof text, "%d", i);
+ ScopedJavaLocalRef<jbyteArray> byte_array = ToJavaByteArray(
+ env, reinterpret_cast<uint8*>(text),
+ static_cast<size_t>(strlen(text)));
+ ASSERT_TRUE(byte_array.obj());
+
+ env->SetObjectArrayElement(array.obj(), i, byte_array.obj());
+ ASSERT_FALSE(HasException(env));
+ }
+
+ // Convert to std::vector<std::string>, check the content.
+ std::vector<std::string> vec;
+ JavaArrayOfByteArrayToStringVector(env, array.obj(), &vec);
+
+ EXPECT_EQ(static_cast<size_t>(kMaxItems), vec.size());
+ for (int i = 0; i < kMaxItems; ++i) {
+ snprintf(text, sizeof text, "%d", i);
+ EXPECT_STREQ(text, vec[i].c_str());
+ }
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/jni_generator/golden_sample_for_tests_jni.h b/chromium/base/android/jni_generator/golden_sample_for_tests_jni.h
new file mode 100644
index 00000000000..2c5f6c00255
--- /dev/null
+++ b/chromium/base/android/jni_generator/golden_sample_for_tests_jni.h
@@ -0,0 +1,373 @@
+// Copyright (c) 2012 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.
+
+// This file is autogenerated by
+// base/android/jni_generator/jni_generator_tests.py
+// For
+// org/chromium/example/jni_generator/SampleForTests
+
+#ifndef org_chromium_example_jni_generator_SampleForTests_JNI
+#define org_chromium_example_jni_generator_SampleForTests_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+using base::android::ScopedJavaLocalRef;
+
+// Step 1: forward declarations.
+namespace {
+const char kInnerStructAClassPath[] =
+ "org/chromium/example/jni_generator/SampleForTests$InnerStructA";
+const char kSampleForTestsClassPath[] =
+ "org/chromium/example/jni_generator/SampleForTests";
+const char kInnerStructBClassPath[] =
+ "org/chromium/example/jni_generator/SampleForTests$InnerStructB";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_InnerStructA_clazz = NULL;
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_SampleForTests_clazz = NULL;
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_InnerStructB_clazz = NULL;
+} // namespace
+
+namespace base {
+namespace android {
+
+static jint Init(JNIEnv* env, jobject obj,
+ jstring param);
+
+static jdouble GetDoubleFunction(JNIEnv* env, jobject obj);
+
+static jfloat GetFloatFunction(JNIEnv* env, jclass clazz);
+
+static void SetNonPODDatatype(JNIEnv* env, jobject obj,
+ jobject rect);
+
+static jobject GetNonPODDatatype(JNIEnv* env, jobject obj);
+
+// Step 2: method stubs.
+static void Destroy(JNIEnv* env, jobject obj,
+ jint nativeCPPClass) {
+ DCHECK(nativeCPPClass) << "Destroy";
+ CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
+ return native->Destroy(env, obj);
+}
+
+static jint Method(JNIEnv* env, jobject obj,
+ jint nativeCPPClass) {
+ DCHECK(nativeCPPClass) << "Method";
+ CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
+ return native->Method(env, obj);
+}
+
+static jdouble MethodOtherP0(JNIEnv* env, jobject obj,
+ jint nativePtr) {
+ DCHECK(nativePtr) << "MethodOtherP0";
+ CPPClass::InnerClass* native =
+ reinterpret_cast<CPPClass::InnerClass*>(nativePtr);
+ return native->MethodOtherP0(env, obj);
+}
+
+static void AddStructB(JNIEnv* env, jobject obj,
+ jint nativeCPPClass,
+ jobject b) {
+ DCHECK(nativeCPPClass) << "AddStructB";
+ CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
+ return native->AddStructB(env, obj, b);
+}
+
+static void IterateAndDoSomethingWithStructB(JNIEnv* env, jobject obj,
+ jint nativeCPPClass) {
+ DCHECK(nativeCPPClass) << "IterateAndDoSomethingWithStructB";
+ CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
+ return native->IterateAndDoSomethingWithStructB(env, obj);
+}
+
+static base::subtle::AtomicWord g_SampleForTests_javaMethod = 0;
+static jint Java_SampleForTests_javaMethod(JNIEnv* env, jobject obj, jint foo,
+ jint bar) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_SampleForTests_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_SampleForTests_clazz,
+ "javaMethod",
+
+"("
+"I"
+"I"
+")"
+"I",
+ &g_SampleForTests_javaMethod);
+
+ jint ret =
+ env->CallIntMethod(obj,
+ method_id, foo, bar);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_SampleForTests_staticJavaMethod = 0;
+static jboolean Java_SampleForTests_staticJavaMethod(JNIEnv* env) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_SampleForTests_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_STATIC>(
+ env, g_SampleForTests_clazz,
+ "staticJavaMethod",
+
+"("
+")"
+"Z",
+ &g_SampleForTests_staticJavaMethod);
+
+ jboolean ret =
+ env->CallStaticBooleanMethod(g_SampleForTests_clazz,
+ method_id);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_SampleForTests_packagePrivateJavaMethod = 0;
+static void Java_SampleForTests_packagePrivateJavaMethod(JNIEnv* env, jobject
+ obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_SampleForTests_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_SampleForTests_clazz,
+ "packagePrivateJavaMethod",
+
+"("
+")"
+"V",
+ &g_SampleForTests_packagePrivateJavaMethod);
+
+ env->CallVoidMethod(obj,
+ method_id);
+ base::android::CheckException(env);
+
+}
+
+static base::subtle::AtomicWord g_SampleForTests_methodThatThrowsException = 0;
+static void Java_SampleForTests_methodThatThrowsException(JNIEnv* env, jobject
+ obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_SampleForTests_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_SampleForTests_clazz,
+ "methodThatThrowsException",
+
+"("
+")"
+"V",
+ &g_SampleForTests_methodThatThrowsException);
+
+ env->CallVoidMethod(obj,
+ method_id);
+
+}
+
+static base::subtle::AtomicWord g_InnerStructA_create = 0;
+static ScopedJavaLocalRef<jobject> Java_InnerStructA_create(JNIEnv* env, jlong
+ l,
+ jint i,
+ jstring s) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InnerStructA_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_STATIC>(
+ env, g_InnerStructA_clazz,
+ "create",
+
+"("
+"J"
+"I"
+"Ljava/lang/String;"
+")"
+"Lorg/chromium/example/jni_generator/SampleForTests$InnerStructA;",
+ &g_InnerStructA_create);
+
+ jobject ret =
+ env->CallStaticObjectMethod(g_InnerStructA_clazz,
+ method_id, l, i, s);
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+static base::subtle::AtomicWord g_SampleForTests_addStructA = 0;
+static void Java_SampleForTests_addStructA(JNIEnv* env, jobject obj, jobject a)
+ {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_SampleForTests_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_SampleForTests_clazz,
+ "addStructA",
+
+"("
+"Lorg/chromium/example/jni_generator/SampleForTests$InnerStructA;"
+")"
+"V",
+ &g_SampleForTests_addStructA);
+
+ env->CallVoidMethod(obj,
+ method_id, a);
+ base::android::CheckException(env);
+
+}
+
+static base::subtle::AtomicWord g_SampleForTests_iterateAndDoSomething = 0;
+static void Java_SampleForTests_iterateAndDoSomething(JNIEnv* env, jobject obj)
+ {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_SampleForTests_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_SampleForTests_clazz,
+ "iterateAndDoSomething",
+
+"("
+")"
+"V",
+ &g_SampleForTests_iterateAndDoSomething);
+
+ env->CallVoidMethod(obj,
+ method_id);
+ base::android::CheckException(env);
+
+}
+
+static base::subtle::AtomicWord g_InnerStructB_getKey = 0;
+static jlong Java_InnerStructB_getKey(JNIEnv* env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InnerStructB_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InnerStructB_clazz,
+ "getKey",
+
+"("
+")"
+"J",
+ &g_InnerStructB_getKey);
+
+ jlong ret =
+ env->CallLongMethod(obj,
+ method_id);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_InnerStructB_getValue = 0;
+static ScopedJavaLocalRef<jstring> Java_InnerStructB_getValue(JNIEnv* env,
+ jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InnerStructB_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InnerStructB_clazz,
+ "getValue",
+
+"("
+")"
+"Ljava/lang/String;",
+ &g_InnerStructB_getValue);
+
+ jstring ret =
+ static_cast<jstring>(env->CallObjectMethod(obj,
+ method_id));
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jstring>(env, ret);
+}
+
+// Step 3: RegisterNatives.
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+
+ g_InnerStructA_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetClass(env, kInnerStructAClassPath).obj()));
+ g_SampleForTests_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetClass(env, kSampleForTestsClassPath).obj()));
+ g_InnerStructB_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetClass(env, kInnerStructBClassPath).obj()));
+ static const JNINativeMethod kMethodsSampleForTests[] = {
+ { "nativeInit",
+"("
+"Ljava/lang/String;"
+")"
+"I", reinterpret_cast<void*>(Init) },
+ { "nativeDestroy",
+"("
+"I"
+")"
+"V", reinterpret_cast<void*>(Destroy) },
+ { "nativeGetDoubleFunction",
+"("
+")"
+"D", reinterpret_cast<void*>(GetDoubleFunction) },
+ { "nativeGetFloatFunction",
+"("
+")"
+"F", reinterpret_cast<void*>(GetFloatFunction) },
+ { "nativeSetNonPODDatatype",
+"("
+"Landroid/graphics/Rect;"
+")"
+"V", reinterpret_cast<void*>(SetNonPODDatatype) },
+ { "nativeGetNonPODDatatype",
+"("
+")"
+"Ljava/lang/Object;", reinterpret_cast<void*>(GetNonPODDatatype) },
+ { "nativeMethod",
+"("
+"I"
+")"
+"I", reinterpret_cast<void*>(Method) },
+ { "nativeMethodOtherP0",
+"("
+"I"
+")"
+"D", reinterpret_cast<void*>(MethodOtherP0) },
+ { "nativeAddStructB",
+"("
+"I"
+"Lorg/chromium/example/jni_generator/SampleForTests$InnerStructB;"
+")"
+"V", reinterpret_cast<void*>(AddStructB) },
+ { "nativeIterateAndDoSomethingWithStructB",
+"("
+"I"
+")"
+"V", reinterpret_cast<void*>(IterateAndDoSomethingWithStructB) },
+ };
+ const int kMethodsSampleForTestsSize = arraysize(kMethodsSampleForTests);
+
+ if (env->RegisterNatives(g_SampleForTests_clazz,
+ kMethodsSampleForTests,
+ kMethodsSampleForTestsSize) < 0) {
+ LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
+ return false;
+ }
+
+ return true;
+}
+} // namespace android
+} // namespace base
+
+#endif // org_chromium_example_jni_generator_SampleForTests_JNI
diff --git a/chromium/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java b/chromium/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java
new file mode 100644
index 00000000000..71cfc72d7e6
--- /dev/null
+++ b/chromium/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java
@@ -0,0 +1,288 @@
+// Copyright (c) 2012 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.example.jni_generator;
+
+import android.graphics.Rect;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.chromium.base.AccessedByNative;
+import org.chromium.base.CalledByNative;
+import org.chromium.base.CalledByNativeUnchecked;
+import org.chromium.base.JNINamespace;
+import org.chromium.base.NativeClassQualifiedName;
+
+
+// This class serves as a reference test for the bindings generator, and as example documentation
+// for how to use the jni generator.
+// The C++ counter-part is sample_for_tests.cc.
+// jni_generator.gyp has a jni_generator_tests target that will:
+// * Generate a header file for the JNI bindings based on this file.
+// * Compile sample_for_tests.cc using the generated header file.
+// * link a native executable to prove the generated header + cc file are self-contained.
+// All comments are informational only, and are ignored by the jni generator.
+//
+// Binding C/C++ with Java is not trivial, specially when ownership and object lifetime
+// semantics needs to be managed across boundaries.
+// Following a few guidelines will make the code simpler and less buggy:
+//
+// - Never write any JNI "by hand". Rely on the bindings generator to have a thin
+// layer of type-safety.
+//
+// - Treat the types from the other side as "opaque" as possible. Do not inspect any
+// object directly, but rather, rely on well-defined getters / setters.
+//
+// - Minimize the surface API between the two sides, and rather than calling multiple
+// functions across boundaries, call only one (and then, internally in the other side,
+// call as many little functions as required).
+//
+// - If a Java object "owns" a native object, stash the pointer in a "int mNativeClassName".
+// Note that it needs to have a "destruction path", i.e., it must eventually call a method
+// to delete the native object (for example, the java object has a "close()" method that
+// in turn deletes the native object). Avoid relying on finalizers: those run in a different
+// thread and makes the native lifetime management more difficult.
+//
+// - For native object "owning" java objects:
+// - If there's a strong 1:1 to relationship between native and java, the best way is to
+// stash the java object into a base::android::ScopedJavaGlobalRef. This will ensure the
+// java object can be GC'd once the native object is destroyed but note that this global strong
+// ref implies a new GC root, so be sure it will not leak and it must never rely on being
+// triggered (transitively) from a java side GC.
+// - In all other cases, the native side should keep a JavaObjectWeakGlobalRef, and check whether
+// that reference is still valid before de-referencing it. Note that you will need another
+// java-side object to be holding a strong reference to this java object while it is in use, to
+// avoid unpredictable GC of the object before native side has finished with it.
+//
+// - The best way to pass "compound" datatypes across in either direction is to create an inner
+// class with PODs and a factory function. If possible, make it immutable (i.e., mark all the
+// fields as "final"). See examples with "InnerStructB" below.
+//
+// - It's simpler to create thin wrappers with a well defined JNI interface than to
+// expose a lot of internal details. This is specially significant for system classes where it's
+// simpler to wrap factory methods and a few getters / setters than expose the entire class.
+//
+// - Use static factory functions annotated with @CalledByNative rather than calling the
+// constructors directly.
+//
+// - Iterate over containers where they are originally owned, then create inner structs or
+// directly call methods on the other side. It's much simpler than trying to amalgamate
+// java and stl containers.
+//
+// This JNINamespace annotation indicates that all native methods should be
+// generated inside this namespace, including the native class that this
+// object binds to.
+@JNINamespace("base::android")
+class SampleForTests {
+ // Classes can store their C++ pointer counter part as an int that is normally initialized by
+ // calling out a nativeInit() function.
+ int mNativeCPPObject;
+
+ // You can define methods and attributes on the java class just like any other.
+ // Methods without the @CalledByNative annotation won't be exposed to JNI.
+ public SampleForTests() {
+ }
+
+ public void startExample() {
+ // Calls native code and holds a pointer to the C++ class.
+ mNativeCPPObject = nativeInit("myParam");
+ }
+
+ public void doStuff() {
+ // This will call CPPClass::Method() using nativePtr as a pointer to the object. This must be
+ // done to:
+ // * avoid leaks.
+ // * using finalizers are not allowed to destroy the cpp class.
+ nativeMethod(mNativeCPPObject);
+ }
+
+ public void finishExample() {
+ // We're done, so let's destroy nativePtr object.
+ nativeDestroy(mNativeCPPObject);
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // The following methods demonstrate exporting Java methods for invocation from C++ code.
+ // Java functions are mapping into C global functions by prefixing the method name with
+ // "Java_<Class>_"
+ // This is triggered by the @CalledByNative annotation; the methods may be named as you wish.
+
+ // Exported to C++ as:
+ // Java_Example_javaMethod(JNIEnv* env, jobject obj, jint foo, jint bar)
+ // Typically the C++ code would have obtained the jobject via the Init() call described above.
+ @CalledByNative
+ public int javaMethod(int foo,
+ int bar) {
+ return 0;
+ }
+
+ // Exported to C++ as Java_Example_staticJavaMethod(JNIEnv* env)
+ // Note no jobject argument, as it is static.
+ @CalledByNative
+ public static boolean staticJavaMethod() {
+ return true;
+ }
+
+ // No prefix, so this method is package private. It will still be exported.
+ @CalledByNative
+ void packagePrivateJavaMethod() {}
+
+ // Note the "Unchecked" suffix. By default, @CalledByNative will always generate bindings that
+ // call CheckException(). With "@CalledByNativeUnchecked", the client C++ code is responsible to
+ // call ClearException() and act as appropriate.
+ // See more details at the "@CalledByNativeUnchecked" annotation.
+ @CalledByNativeUnchecked
+ void methodThatThrowsException() throws Exception {}
+
+ // The generator is not confused by inline comments:
+ // @CalledByNative void thisShouldNotAppearInTheOutput();
+ // @CalledByNativeUnchecked public static void neitherShouldThis(int foo);
+
+ /**
+ * The generator is not confused by block comments:
+ * @CalledByNative void thisShouldNotAppearInTheOutputEither();
+ * @CalledByNativeUnchecked public static void andDefinitelyNotThis(int foo);
+ */
+
+ // String constants that look like comments don't confuse the generator:
+ private String arrgh = "*/*";
+
+ //------------------------------------------------------------------------------------------------
+ // Java fields which are accessed from C++ code only must be annotated with @AccessedByNative to
+ // prevent them being eliminated when unreferenced code is stripped.
+ @AccessedByNative
+ private int javaField;
+
+ //------------------------------------------------------------------------------------------------
+ // The following methods demonstrate declaring methods to call into C++ from Java.
+ // The generator detects the "native" and "static" keywords, the type and name of the first
+ // parameter, and the "native" prefix to the function name to determine the C++ function
+ // signatures. Besides these constraints the methods can be freely named.
+
+ // This declares a C++ function which the application code must implement:
+ // static jint Init(JNIEnv* env, jobject obj);
+ // The jobject parameter refers back to this java side object instance.
+ // The implementation must return the pointer to the C++ object cast to jint.
+ // The caller of this method should store it, and supply it as a the nativeCPPClass param to
+ // subsequent native method calls (see the methods below that take an "int native..." as first
+ // param).
+ private native int nativeInit(String param);
+
+ // This defines a function binding to the associated C++ class member function. The name is
+ // derived from |nativeDestroy| and |nativeCPPClass| to arrive at CPPClass::Destroy() (i.e. native
+ // prefixes stripped).
+ // The |nativeCPPClass| is automatically cast to type CPPClass* in order to obtain the object on
+ // which to invoke the member function.
+ private native void nativeDestroy(int nativeCPPClass);
+
+ // This declares a C++ function which the application code must implement:
+ // static jdouble GetDoubleFunction(JNIEnv* env, jobject obj);
+ // The jobject parameter refers back to this java side object instance.
+ private native double nativeGetDoubleFunction();
+
+ // Similar to nativeGetDoubleFunction(), but here the C++ side will receive a jclass rather than
+ // jobject param, as the function is declared static.
+ private static native float nativeGetFloatFunction();
+
+ // This function takes a non-POD datatype. We have a list mapping them to their full classpath in
+ // jni_generator.py JavaParamToJni. If you require a new datatype, make sure you add to that
+ // function.
+ private native void nativeSetNonPODDatatype(Rect rect);
+
+ // This declares a C++ function which the application code must implement:
+ // static ScopedJavaLocalRef<jobject> GetNonPODDatatype(JNIEnv* env, jobject obj);
+ // The jobject parameter refers back to this java side object instance.
+ // Note that it returns a ScopedJavaLocalRef<jobject> so that you don' have to worry about
+ // deleting the JNI local reference. This is similar with Strings and arrays.
+ private native Object nativeGetNonPODDatatype();
+
+ // Similar to nativeDestroy above, this will cast nativeCPPClass into pointer of CPPClass type and
+ // call its Method member function.
+ private native int nativeMethod(int nativeCPPClass);
+
+ // Similar to nativeMethod above, but here the C++ fully qualified class name is taken from the
+ // annotation rather than parameter name, which can thus be chosen freely.
+ @NativeClassQualifiedName("CPPClass::InnerClass")
+ private native double nativeMethodOtherP0(int nativePtr);
+
+ // This "struct" will be created by the native side using |createInnerStructA|,
+ // and used by the java-side somehow.
+ // Note that |@CalledByNative| has to contain the inner class name.
+ static class InnerStructA {
+ private final long mLong;
+ private final int mInt;
+ private final String mString;
+
+ private InnerStructA(long l, int i, String s) {
+ mLong = l;
+ mInt = i;
+ mString = s;
+ }
+
+ @CalledByNative("InnerStructA")
+ private static InnerStructA create(long l, int i, String s) {
+ return new InnerStructA(l, i, s);
+ }
+ }
+
+ private List<InnerStructA> mListInnerStructA = new ArrayList<InnerStructA>();
+
+ @CalledByNative
+ private void addStructA(InnerStructA a) {
+ // Called by the native side to append another element.
+ mListInnerStructA.add(a);
+ }
+
+ @CalledByNative
+ private void iterateAndDoSomething() {
+ Iterator<InnerStructA> it = mListInnerStructA.iterator();
+ while (it.hasNext()) {
+ InnerStructA element = it.next();
+ // Now, do something with element.
+ }
+ // Done, clear the list.
+ mListInnerStructA.clear();
+ }
+
+ // This "struct" will be created by the java side passed to native, which
+ // will use its getters.
+ // Note that |@CalledByNative| has to contain the inner class name.
+ static class InnerStructB {
+ private final long mKey;
+ private final String mValue;
+
+ private InnerStructB(long k, String v) {
+ mKey = k;
+ mValue = v;
+ }
+
+ @CalledByNative("InnerStructB")
+ private long getKey() {
+ return mKey;
+ }
+
+ @CalledByNative("InnerStructB")
+ private String getValue() {
+ return mValue;
+ }
+ }
+
+ List<InnerStructB> mListInnerStructB = new ArrayList<InnerStructB>();
+
+ void iterateAndDoSomethingWithMap() {
+ Iterator<InnerStructB> it = mListInnerStructB.iterator();
+ while (it.hasNext()) {
+ InnerStructB element = it.next();
+ // Now, do something with element.
+ nativeAddStructB(mNativeCPPObject, element);
+ }
+ nativeIterateAndDoSomethingWithStructB(mNativeCPPObject);
+ }
+
+ native void nativeAddStructB(int nativeCPPClass, InnerStructB b);
+ native void nativeIterateAndDoSomethingWithStructB(int nativeCPPClass);
+}
diff --git a/chromium/base/android/jni_generator/jni_generator.gyp b/chromium/base/android/jni_generator/jni_generator.gyp
new file mode 100644
index 00000000000..4bad0dc4832
--- /dev/null
+++ b/chromium/base/android/jni_generator/jni_generator.gyp
@@ -0,0 +1,67 @@
+# Copyright (c) 2012 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'jni_generator_py_tests',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'run_jni_generator_py_tests',
+ 'inputs': [
+ 'jni_generator.py',
+ 'jni_generator_tests.py',
+ 'java/src/org/chromium/example/jni_generator/SampleForTests.java',
+ 'golden_sample_for_tests_jni.h',
+ ],
+ 'outputs': [
+ '',
+ ],
+ 'action': [
+ 'python', 'jni_generator_tests.py',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'jni_sample_header',
+ 'type': 'none',
+ 'sources': [
+ 'java/src/org/chromium/example/jni_generator/SampleForTests.java',
+ ],
+ 'variables': {
+ 'jni_gen_package': 'example',
+ },
+ 'includes': [ '../../../build/jni_generator.gypi' ],
+ },
+ {
+ 'target_name': 'jni_sample_java',
+ 'type': 'none',
+ 'variables': {
+ 'java_in_dir': '../../../base/android/jni_generator/java',
+ },
+ 'dependencies': [
+ '<(DEPTH)/base/base.gyp:base_java',
+ ],
+ 'includes': [ '../../../build/java.gypi' ],
+ },
+ {
+ 'target_name': 'jni_generator_tests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../../base.gyp:test_support_base',
+ 'jni_generator_py_tests',
+ 'jni_sample_header',
+ 'jni_sample_java',
+ ],
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)/example',
+ ],
+ 'sources': [
+ 'sample_for_tests.cc',
+ ],
+ },
+ ],
+}
diff --git a/chromium/base/android/jni_generator/jni_generator.py b/chromium/base/android/jni_generator/jni_generator.py
new file mode 100755
index 00000000000..de865d527be
--- /dev/null
+++ b/chromium/base/android/jni_generator/jni_generator.py
@@ -0,0 +1,1065 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 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.
+
+"""Extracts native methods from a Java file and generates the JNI bindings.
+If you change this, please run and update the tests."""
+
+import collections
+import errno
+import optparse
+import os
+import re
+import string
+from string import Template
+import subprocess
+import sys
+import textwrap
+import zipfile
+
+
+class ParseError(Exception):
+ """Exception thrown when we can't parse the input file."""
+
+ def __init__(self, description, *context_lines):
+ Exception.__init__(self)
+ self.description = description
+ self.context_lines = context_lines
+
+ def __str__(self):
+ context = '\n'.join(self.context_lines)
+ return '***\nERROR: %s\n\n%s\n***' % (self.description, context)
+
+
+class Param(object):
+ """Describes a param for a method, either java or native."""
+
+ def __init__(self, **kwargs):
+ self.datatype = kwargs['datatype']
+ self.name = kwargs['name']
+
+
+class NativeMethod(object):
+ """Describes a C/C++ method that is called by Java code"""
+
+ def __init__(self, **kwargs):
+ self.static = kwargs['static']
+ self.java_class_name = kwargs['java_class_name']
+ self.return_type = kwargs['return_type']
+ self.name = kwargs['name']
+ self.params = kwargs['params']
+ if self.params:
+ assert type(self.params) is list
+ assert type(self.params[0]) is Param
+ if (self.params and
+ self.params[0].datatype == 'int' and
+ self.params[0].name.startswith('native')):
+ self.type = 'method'
+ self.p0_type = self.params[0].name[len('native'):]
+ if kwargs.get('native_class_name'):
+ self.p0_type = kwargs['native_class_name']
+ else:
+ self.type = 'function'
+ self.method_id_var_name = kwargs.get('method_id_var_name', None)
+
+
+class CalledByNative(object):
+ """Describes a java method exported to c/c++"""
+
+ def __init__(self, **kwargs):
+ self.system_class = kwargs['system_class']
+ self.unchecked = kwargs['unchecked']
+ self.static = kwargs['static']
+ self.java_class_name = kwargs['java_class_name']
+ self.return_type = kwargs['return_type']
+ self.name = kwargs['name']
+ self.params = kwargs['params']
+ self.method_id_var_name = kwargs.get('method_id_var_name', None)
+ self.is_constructor = kwargs.get('is_constructor', False)
+ self.env_call = GetEnvCall(self.is_constructor, self.static,
+ self.return_type)
+ self.static_cast = GetStaticCastForReturnType(self.return_type)
+
+
+def JavaDataTypeToC(java_type):
+ """Returns a C datatype for the given java type."""
+ java_pod_type_map = {
+ 'int': 'jint',
+ 'byte': 'jbyte',
+ 'char': 'jchar',
+ 'short': 'jshort',
+ 'boolean': 'jboolean',
+ 'long': 'jlong',
+ 'double': 'jdouble',
+ 'float': 'jfloat',
+ }
+ java_type_map = {
+ 'void': 'void',
+ 'String': 'jstring',
+ 'java/lang/String': 'jstring',
+ 'Class': 'jclass',
+ 'java/lang/Class': 'jclass',
+ }
+
+ if java_type in java_pod_type_map:
+ return java_pod_type_map[java_type]
+ elif java_type in java_type_map:
+ return java_type_map[java_type]
+ elif java_type.endswith('[]'):
+ if java_type[:-2] in java_pod_type_map:
+ return java_pod_type_map[java_type[:-2]] + 'Array'
+ return 'jobjectArray'
+ else:
+ return 'jobject'
+
+
+class JniParams(object):
+ _imports = []
+ _fully_qualified_class = ''
+ _package = ''
+ _inner_classes = []
+ _remappings = []
+
+ @staticmethod
+ def SetFullyQualifiedClass(fully_qualified_class):
+ JniParams._fully_qualified_class = 'L' + fully_qualified_class
+ JniParams._package = '/'.join(fully_qualified_class.split('/')[:-1])
+
+ @staticmethod
+ def ExtractImportsAndInnerClasses(contents):
+ contents = contents.replace('\n', '')
+ re_import = re.compile(r'import.*?(?P<class>\S*?);')
+ for match in re.finditer(re_import, contents):
+ JniParams._imports += ['L' + match.group('class').replace('.', '/')]
+
+ re_inner = re.compile(r'(class|interface)\s+?(?P<name>\w+?)\W')
+ for match in re.finditer(re_inner, contents):
+ inner = match.group('name')
+ if not JniParams._fully_qualified_class.endswith(inner):
+ JniParams._inner_classes += [JniParams._fully_qualified_class + '$' +
+ inner]
+
+ @staticmethod
+ def JavaToJni(param):
+ """Converts a java param into a JNI signature type."""
+ pod_param_map = {
+ 'int': 'I',
+ 'boolean': 'Z',
+ 'char': 'C',
+ 'short': 'S',
+ 'long': 'J',
+ 'double': 'D',
+ 'float': 'F',
+ 'byte': 'B',
+ 'void': 'V',
+ }
+ object_param_list = [
+ 'Ljava/lang/Boolean',
+ 'Ljava/lang/Integer',
+ 'Ljava/lang/Long',
+ 'Ljava/lang/Object',
+ 'Ljava/lang/String',
+ 'Ljava/lang/Class',
+ ]
+ prefix = ''
+ # Array?
+ while param[-2:] == '[]':
+ prefix += '['
+ param = param[:-2]
+ # Generic?
+ if '<' in param:
+ param = param[:param.index('<')]
+ if param in pod_param_map:
+ return prefix + pod_param_map[param]
+ if '/' in param:
+ # Coming from javap, use the fully qualified param directly.
+ return prefix + 'L' + JniParams.RemapClassName(param) + ';'
+ for qualified_name in (object_param_list +
+ [JniParams._fully_qualified_class] +
+ JniParams._inner_classes):
+ if (qualified_name.endswith('/' + param) or
+ qualified_name.endswith('$' + param.replace('.', '$')) or
+ qualified_name == 'L' + param):
+ return prefix + JniParams.RemapClassName(qualified_name) + ';'
+
+ # Is it from an import? (e.g. referecing Class from import pkg.Class;
+ # note that referencing an inner class Inner from import pkg.Class.Inner
+ # is not supported).
+ for qualified_name in JniParams._imports:
+ if qualified_name.endswith('/' + param):
+ # Ensure it's not an inner class.
+ components = qualified_name.split('/')
+ if len(components) > 2 and components[-2][0].isupper():
+ raise SyntaxError('Inner class (%s) can not be imported '
+ 'and used by JNI (%s). Please import the outer '
+ 'class and use Outer.Inner instead.' %
+ (qualified_name, param))
+ return prefix + JniParams.RemapClassName(qualified_name) + ';'
+
+ # Is it an inner class from an outer class import? (e.g. referencing
+ # Class.Inner from import pkg.Class).
+ if '.' in param:
+ components = param.split('.')
+ outer = '/'.join(components[:-1])
+ inner = components[-1]
+ for qualified_name in JniParams._imports:
+ if qualified_name.endswith('/' + outer):
+ return (prefix + JniParams.RemapClassName(qualified_name) +
+ '$' + inner + ';')
+
+ # Type not found, falling back to same package as this class.
+ return (prefix + 'L' +
+ JniParams.RemapClassName(JniParams._package + '/' + param) + ';')
+
+ @staticmethod
+ def Signature(params, returns, wrap):
+ """Returns the JNI signature for the given datatypes."""
+ items = ['(']
+ items += [JniParams.JavaToJni(param.datatype) for param in params]
+ items += [')']
+ items += [JniParams.JavaToJni(returns)]
+ if wrap:
+ return '\n' + '\n'.join(['"' + item + '"' for item in items])
+ else:
+ return '"' + ''.join(items) + '"'
+
+ @staticmethod
+ def Parse(params):
+ """Parses the params into a list of Param objects."""
+ if not params:
+ return []
+ ret = []
+ for p in [p.strip() for p in params.split(',')]:
+ items = p.split(' ')
+ if 'final' in items:
+ items.remove('final')
+ param = Param(
+ datatype=items[0],
+ name=(items[1] if len(items) > 1 else 'p%s' % len(ret)),
+ )
+ ret += [param]
+ return ret
+
+ @staticmethod
+ def RemapClassName(class_name):
+ """Remaps class names using the jarjar mapping table."""
+ for old, new in JniParams._remappings:
+ if old in class_name:
+ return class_name.replace(old, new, 1)
+ return class_name
+
+ @staticmethod
+ def SetJarJarMappings(mappings):
+ """Parse jarjar mappings from a string."""
+ JniParams._remappings = []
+ for line in mappings.splitlines():
+ keyword, src, dest = line.split()
+ if keyword != 'rule':
+ continue
+ assert src.endswith('.**')
+ src = src[:-2].replace('.', '/')
+ dest = dest.replace('.', '/')
+ if dest.endswith('@0'):
+ JniParams._remappings.append((src, dest[:-2] + src))
+ else:
+ assert dest.endswith('@1')
+ JniParams._remappings.append((src, dest[:-2]))
+
+
+def ExtractJNINamespace(contents):
+ re_jni_namespace = re.compile('.*?@JNINamespace\("(.*?)"\)')
+ m = re.findall(re_jni_namespace, contents)
+ if not m:
+ return ''
+ return m[0]
+
+
+def ExtractFullyQualifiedJavaClassName(java_file_name, contents):
+ re_package = re.compile('.*?package (.*?);')
+ matches = re.findall(re_package, contents)
+ if not matches:
+ raise SyntaxError('Unable to find "package" line in %s' % java_file_name)
+ return (matches[0].replace('.', '/') + '/' +
+ os.path.splitext(os.path.basename(java_file_name))[0])
+
+
+def ExtractNatives(contents):
+ """Returns a list of dict containing information about a native method."""
+ contents = contents.replace('\n', '')
+ natives = []
+ re_native = re.compile(r'(@NativeClassQualifiedName'
+ '\(\"(?P<native_class_name>.*?)\"\))?\s*'
+ '(@NativeCall(\(\"(?P<java_class_name>.*?)\"\)))?\s*'
+ '(?P<qualifiers>\w+\s\w+|\w+|\s+)\s*?native '
+ '(?P<return_type>\S*?) '
+ '(?P<name>\w+?)\((?P<params>.*?)\);')
+ for match in re.finditer(re_native, contents):
+ native = NativeMethod(
+ static='static' in match.group('qualifiers'),
+ java_class_name=match.group('java_class_name'),
+ native_class_name=match.group('native_class_name'),
+ return_type=match.group('return_type'),
+ name=match.group('name').replace('native', ''),
+ params=JniParams.Parse(match.group('params')))
+ natives += [native]
+ return natives
+
+
+def GetStaticCastForReturnType(return_type):
+ type_map = { 'String' : 'jstring',
+ 'java/lang/String' : 'jstring',
+ 'boolean[]': 'jbooleanArray',
+ 'byte[]': 'jbyteArray',
+ 'char[]': 'jcharArray',
+ 'short[]': 'jshortArray',
+ 'int[]': 'jintArray',
+ 'long[]': 'jlongArray',
+ 'double[]': 'jdoubleArray' }
+ ret = type_map.get(return_type, None)
+ if ret:
+ return ret
+ if return_type.endswith('[]'):
+ return 'jobjectArray'
+ return None
+
+
+def GetEnvCall(is_constructor, is_static, return_type):
+ """Maps the types availabe via env->Call__Method."""
+ if is_constructor:
+ return 'NewObject'
+ env_call_map = {'boolean': 'Boolean',
+ 'byte': 'Byte',
+ 'char': 'Char',
+ 'short': 'Short',
+ 'int': 'Int',
+ 'long': 'Long',
+ 'float': 'Float',
+ 'void': 'Void',
+ 'double': 'Double',
+ 'Object': 'Object',
+ }
+ call = env_call_map.get(return_type, 'Object')
+ if is_static:
+ call = 'Static' + call
+ return 'Call' + call + 'Method'
+
+
+def GetMangledParam(datatype):
+ """Returns a mangled identifier for the datatype."""
+ if len(datatype) <= 2:
+ return datatype.replace('[', 'A')
+ ret = ''
+ for i in range(1, len(datatype)):
+ c = datatype[i]
+ if c == '[':
+ ret += 'A'
+ elif c.isupper() or datatype[i - 1] in ['/', 'L']:
+ ret += c.upper()
+ return ret
+
+
+def GetMangledMethodName(name, params, return_type):
+ """Returns a mangled method name for the given signature.
+
+ The returned name can be used as a C identifier and will be unique for all
+ valid overloads of the same method.
+
+ Args:
+ name: string.
+ params: list of Param.
+ return_type: string.
+
+ Returns:
+ A mangled name.
+ """
+ mangled_items = []
+ for datatype in [return_type] + [x.datatype for x in params]:
+ mangled_items += [GetMangledParam(JniParams.JavaToJni(datatype))]
+ mangled_name = name + '_'.join(mangled_items)
+ assert re.match(r'[0-9a-zA-Z_]+', mangled_name)
+ return mangled_name
+
+
+def MangleCalledByNatives(called_by_natives):
+ """Mangles all the overloads from the call_by_natives list."""
+ method_counts = collections.defaultdict(
+ lambda: collections.defaultdict(lambda: 0))
+ for called_by_native in called_by_natives:
+ java_class_name = called_by_native.java_class_name
+ name = called_by_native.name
+ method_counts[java_class_name][name] += 1
+ for called_by_native in called_by_natives:
+ java_class_name = called_by_native.java_class_name
+ method_name = called_by_native.name
+ method_id_var_name = method_name
+ if method_counts[java_class_name][method_name] > 1:
+ method_id_var_name = GetMangledMethodName(method_name,
+ called_by_native.params,
+ called_by_native.return_type)
+ called_by_native.method_id_var_name = method_id_var_name
+ return called_by_natives
+
+
+# Regex to match the JNI return types that should be included in a
+# ScopedJavaLocalRef.
+RE_SCOPED_JNI_RETURN_TYPES = re.compile('jobject|jclass|jstring|.*Array')
+
+# Regex to match a string like "@CalledByNative public void foo(int bar)".
+RE_CALLED_BY_NATIVE = re.compile(
+ '@CalledByNative(?P<Unchecked>(Unchecked)*?)(?:\("(?P<annotation>.*)"\))?'
+ '\s+(?P<prefix>[\w ]*?)'
+ '\s*(?P<return_type>\S+?)'
+ '\s+(?P<name>\w+)'
+ '\s*\((?P<params>[^\)]*)\)')
+
+
+def ExtractCalledByNatives(contents):
+ """Parses all methods annotated with @CalledByNative.
+
+ Args:
+ contents: the contents of the java file.
+
+ Returns:
+ A list of dict with information about the annotated methods.
+ TODO(bulach): return a CalledByNative object.
+
+ Raises:
+ ParseError: if unable to parse.
+ """
+ called_by_natives = []
+ for match in re.finditer(RE_CALLED_BY_NATIVE, contents):
+ called_by_natives += [CalledByNative(
+ system_class=False,
+ unchecked='Unchecked' in match.group('Unchecked'),
+ static='static' in match.group('prefix'),
+ java_class_name=match.group('annotation') or '',
+ return_type=match.group('return_type'),
+ name=match.group('name'),
+ params=JniParams.Parse(match.group('params')))]
+ # Check for any @CalledByNative occurrences that weren't matched.
+ unmatched_lines = re.sub(RE_CALLED_BY_NATIVE, '', contents).split('\n')
+ for line1, line2 in zip(unmatched_lines, unmatched_lines[1:]):
+ if '@CalledByNative' in line1:
+ raise ParseError('could not parse @CalledByNative method signature',
+ line1, line2)
+ return MangleCalledByNatives(called_by_natives)
+
+
+class JNIFromJavaP(object):
+ """Uses 'javap' to parse a .class file and generate the JNI header file."""
+
+ def __init__(self, contents, namespace):
+ self.contents = contents
+ self.namespace = namespace
+ self.fully_qualified_class = re.match(
+ '.*?(class|interface) (?P<class_name>.*?)( |{)',
+ contents[1]).group('class_name')
+ self.fully_qualified_class = self.fully_qualified_class.replace('.', '/')
+ JniParams.SetFullyQualifiedClass(self.fully_qualified_class)
+ self.java_class_name = self.fully_qualified_class.split('/')[-1]
+ if not self.namespace:
+ self.namespace = 'JNI_' + self.java_class_name
+ re_method = re.compile('(?P<prefix>.*?)(?P<return_type>\S+?) (?P<name>\w+?)'
+ '\((?P<params>.*?)\)')
+ self.called_by_natives = []
+ for content in contents[2:]:
+ match = re.match(re_method, content)
+ if not match:
+ continue
+ self.called_by_natives += [CalledByNative(
+ system_class=True,
+ unchecked=False,
+ static='static' in match.group('prefix'),
+ java_class_name='',
+ return_type=match.group('return_type').replace('.', '/'),
+ name=match.group('name'),
+ params=JniParams.Parse(match.group('params').replace('.', '/')))]
+ re_constructor = re.compile('.*? public ' +
+ self.fully_qualified_class.replace('/', '.') +
+ '\((?P<params>.*?)\)')
+ for content in contents[2:]:
+ match = re.match(re_constructor, content)
+ if not match:
+ continue
+ self.called_by_natives += [CalledByNative(
+ system_class=True,
+ unchecked=False,
+ static=False,
+ java_class_name='',
+ return_type=self.fully_qualified_class,
+ name='Constructor',
+ params=JniParams.Parse(match.group('params').replace('.', '/')),
+ is_constructor=True)]
+ self.called_by_natives = MangleCalledByNatives(self.called_by_natives)
+ self.inl_header_file_generator = InlHeaderFileGenerator(
+ self.namespace, self.fully_qualified_class, [], self.called_by_natives)
+
+ def GetContent(self):
+ return self.inl_header_file_generator.GetContent()
+
+ @staticmethod
+ def CreateFromClass(class_file, namespace):
+ class_name = os.path.splitext(os.path.basename(class_file))[0]
+ p = subprocess.Popen(args=['javap', class_name],
+ cwd=os.path.dirname(class_file),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout, _ = p.communicate()
+ jni_from_javap = JNIFromJavaP(stdout.split('\n'), namespace)
+ return jni_from_javap
+
+
+class JNIFromJavaSource(object):
+ """Uses the given java source file to generate the JNI header file."""
+
+ def __init__(self, contents, fully_qualified_class):
+ contents = self._RemoveComments(contents)
+ JniParams.SetFullyQualifiedClass(fully_qualified_class)
+ JniParams.ExtractImportsAndInnerClasses(contents)
+ jni_namespace = ExtractJNINamespace(contents)
+ natives = ExtractNatives(contents)
+ called_by_natives = ExtractCalledByNatives(contents)
+ if len(natives) == 0 and len(called_by_natives) == 0:
+ raise SyntaxError('Unable to find any JNI methods for %s.' %
+ fully_qualified_class)
+ inl_header_file_generator = InlHeaderFileGenerator(
+ jni_namespace, fully_qualified_class, natives, called_by_natives)
+ self.content = inl_header_file_generator.GetContent()
+
+ def _RemoveComments(self, contents):
+ # We need to support both inline and block comments, and we need to handle
+ # strings that contain '//' or '/*'. Rather than trying to do all that with
+ # regexps, we just pipe the contents through the C preprocessor. We tell cpp
+ # the file has already been preprocessed, so it just removes comments and
+ # doesn't try to parse #include, #pragma etc.
+ #
+ # TODO(husky): This is a bit hacky. It would be cleaner to use a real Java
+ # parser. Maybe we could ditch JNIFromJavaSource and just always use
+ # JNIFromJavaP; or maybe we could rewrite this script in Java and use APT.
+ # http://code.google.com/p/chromium/issues/detail?id=138941
+ p = subprocess.Popen(args=['cpp', '-fpreprocessed'],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout, _ = p.communicate(contents)
+ return stdout
+
+ def GetContent(self):
+ return self.content
+
+ @staticmethod
+ def CreateFromFile(java_file_name):
+ contents = file(java_file_name).read()
+ fully_qualified_class = ExtractFullyQualifiedJavaClassName(java_file_name,
+ contents)
+ return JNIFromJavaSource(contents, fully_qualified_class)
+
+
+class InlHeaderFileGenerator(object):
+ """Generates an inline header file for JNI integration."""
+
+ def __init__(self, namespace, fully_qualified_class, natives,
+ called_by_natives):
+ self.namespace = namespace
+ self.fully_qualified_class = fully_qualified_class
+ self.class_name = self.fully_qualified_class.split('/')[-1]
+ self.natives = natives
+ self.called_by_natives = called_by_natives
+ self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI'
+
+ def GetContent(self):
+ """Returns the content of the JNI binding file."""
+ template = Template("""\
+// Copyright (c) 2012 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.
+
+
+// This file is autogenerated by
+// ${SCRIPT_NAME}
+// For
+// ${FULLY_QUALIFIED_CLASS}
+
+#ifndef ${HEADER_GUARD}
+#define ${HEADER_GUARD}
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+using base::android::ScopedJavaLocalRef;
+
+// Step 1: forward declarations.
+namespace {
+$CLASS_PATH_DEFINITIONS
+} // namespace
+
+$OPEN_NAMESPACE
+$FORWARD_DECLARATIONS
+
+// Step 2: method stubs.
+$METHOD_STUBS
+
+// Step 3: RegisterNatives.
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+$REGISTER_NATIVES_IMPL
+ return true;
+}
+$CLOSE_NAMESPACE
+#endif // ${HEADER_GUARD}
+""")
+ script_components = os.path.abspath(sys.argv[0]).split(os.path.sep)
+ base_index = script_components.index('base')
+ script_name = os.sep.join(script_components[base_index:])
+ values = {
+ 'SCRIPT_NAME': script_name,
+ 'FULLY_QUALIFIED_CLASS': self.fully_qualified_class,
+ 'CLASS_PATH_DEFINITIONS': self.GetClassPathDefinitionsString(),
+ 'FORWARD_DECLARATIONS': self.GetForwardDeclarationsString(),
+ 'METHOD_STUBS': self.GetMethodStubsString(),
+ 'OPEN_NAMESPACE': self.GetOpenNamespaceString(),
+ 'REGISTER_NATIVES_IMPL': self.GetRegisterNativesImplString(),
+ 'CLOSE_NAMESPACE': self.GetCloseNamespaceString(),
+ 'HEADER_GUARD': self.header_guard,
+ }
+ return WrapOutput(template.substitute(values))
+
+ def GetClassPathDefinitionsString(self):
+ ret = []
+ ret += [self.GetClassPathDefinitions()]
+ return '\n'.join(ret)
+
+ def GetForwardDeclarationsString(self):
+ ret = []
+ for native in self.natives:
+ if native.type != 'method':
+ ret += [self.GetForwardDeclaration(native)]
+ return '\n'.join(ret)
+
+ def GetMethodStubsString(self):
+ ret = []
+ for native in self.natives:
+ if native.type == 'method':
+ ret += [self.GetNativeMethodStub(native)]
+ for called_by_native in self.called_by_natives:
+ ret += [self.GetCalledByNativeMethodStub(called_by_native)]
+ return '\n'.join(ret)
+
+ def GetKMethodsString(self, clazz):
+ ret = []
+ for native in self.natives:
+ if (native.java_class_name == clazz or
+ (not native.java_class_name and clazz == self.class_name)):
+ ret += [self.GetKMethodArrayEntry(native)]
+ return '\n'.join(ret)
+
+ def GetRegisterNativesImplString(self):
+ """Returns the implementation for RegisterNatives."""
+ template = Template("""\
+ static const JNINativeMethod kMethods${JAVA_CLASS}[] = {
+${KMETHODS}
+ };
+ const int kMethods${JAVA_CLASS}Size = arraysize(kMethods${JAVA_CLASS});
+
+ if (env->RegisterNatives(g_${JAVA_CLASS}_clazz,
+ kMethods${JAVA_CLASS},
+ kMethods${JAVA_CLASS}Size) < 0) {
+ LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
+ return false;
+ }
+""")
+ ret = [self.GetFindClasses()]
+ all_classes = self.GetUniqueClasses(self.natives)
+ all_classes[self.class_name] = self.fully_qualified_class
+ for clazz in all_classes:
+ kmethods = self.GetKMethodsString(clazz)
+ if kmethods:
+ values = {'JAVA_CLASS': clazz,
+ 'KMETHODS': kmethods}
+ ret += [template.substitute(values)]
+ if not ret: return ''
+ return '\n' + '\n'.join(ret)
+
+ def GetOpenNamespaceString(self):
+ if self.namespace:
+ all_namespaces = ['namespace %s {' % ns
+ for ns in self.namespace.split('::')]
+ return '\n'.join(all_namespaces)
+ return ''
+
+ def GetCloseNamespaceString(self):
+ if self.namespace:
+ all_namespaces = ['} // namespace %s' % ns
+ for ns in self.namespace.split('::')]
+ all_namespaces.reverse()
+ return '\n'.join(all_namespaces) + '\n'
+ return ''
+
+ def GetJNIFirstParam(self, native):
+ ret = []
+ if native.type == 'method':
+ ret = ['jobject obj']
+ elif native.type == 'function':
+ if native.static:
+ ret = ['jclass clazz']
+ else:
+ ret = ['jobject obj']
+ return ret
+
+ def GetParamsInDeclaration(self, native):
+ """Returns the params for the stub declaration.
+
+ Args:
+ native: the native dictionary describing the method.
+
+ Returns:
+ A string containing the params.
+ """
+ return ',\n '.join(self.GetJNIFirstParam(native) +
+ [JavaDataTypeToC(param.datatype) + ' ' +
+ param.name
+ for param in native.params])
+
+ def GetCalledByNativeParamsInDeclaration(self, called_by_native):
+ return ',\n '.join([JavaDataTypeToC(param.datatype) + ' ' +
+ param.name
+ for param in called_by_native.params])
+
+ def GetForwardDeclaration(self, native):
+ template = Template("""
+static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS});
+""")
+ values = {'RETURN': JavaDataTypeToC(native.return_type),
+ 'NAME': native.name,
+ 'PARAMS': self.GetParamsInDeclaration(native)}
+ return template.substitute(values)
+
+ def GetNativeMethodStub(self, native):
+ """Returns stubs for native methods."""
+ template = Template("""\
+static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) {
+ DCHECK(${PARAM0_NAME}) << "${NAME}";
+ ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME});
+ return native->${NAME}(env, obj${PARAMS_IN_CALL})${POST_CALL};
+}
+""")
+ params_for_call = ', '.join(p.name for p in native.params[1:])
+ if params_for_call:
+ params_for_call = ', ' + params_for_call
+
+ return_type = JavaDataTypeToC(native.return_type)
+ if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
+ scoped_return_type = 'ScopedJavaLocalRef<' + return_type + '>'
+ post_call = '.Release()'
+ else:
+ scoped_return_type = return_type
+ post_call = ''
+ values = {
+ 'RETURN': return_type,
+ 'SCOPED_RETURN': scoped_return_type,
+ 'NAME': native.name,
+ 'PARAMS_IN_DECLARATION': self.GetParamsInDeclaration(native),
+ 'PARAM0_NAME': native.params[0].name,
+ 'P0_TYPE': native.p0_type,
+ 'PARAMS_IN_CALL': params_for_call,
+ 'POST_CALL': post_call
+ }
+ return template.substitute(values)
+
+ def GetCalledByNativeMethodStub(self, called_by_native):
+ """Returns a string."""
+ function_signature_template = Template("""\
+static ${RETURN_TYPE} Java_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}(\
+JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION})""")
+ function_header_template = Template("""\
+${FUNCTION_SIGNATURE} {""")
+ function_header_with_unused_template = Template("""\
+${FUNCTION_SIGNATURE} __attribute__ ((unused));
+${FUNCTION_SIGNATURE} {""")
+ template = Template("""
+static base::subtle::AtomicWord g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = 0;
+${FUNCTION_HEADER}
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_${JAVA_CLASS}_clazz);
+ jmethodID method_id =
+ ${GET_METHOD_ID_IMPL}
+ ${RETURN_DECLARATION}
+ ${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL},
+ method_id${PARAMS_IN_CALL})${POST_CALL};
+ ${CHECK_EXCEPTION}
+ ${RETURN_CLAUSE}
+}""")
+ if called_by_native.static or called_by_native.is_constructor:
+ first_param_in_declaration = ''
+ first_param_in_call = ('g_%s_clazz' %
+ (called_by_native.java_class_name or
+ self.class_name))
+ else:
+ first_param_in_declaration = ', jobject obj'
+ first_param_in_call = 'obj'
+ params_in_declaration = self.GetCalledByNativeParamsInDeclaration(
+ called_by_native)
+ if params_in_declaration:
+ params_in_declaration = ', ' + params_in_declaration
+ params_for_call = ', '.join(param.name
+ for param in called_by_native.params)
+ if params_for_call:
+ params_for_call = ', ' + params_for_call
+ pre_call = ''
+ post_call = ''
+ if called_by_native.static_cast:
+ pre_call = 'static_cast<%s>(' % called_by_native.static_cast
+ post_call = ')'
+ check_exception = ''
+ if not called_by_native.unchecked:
+ check_exception = 'base::android::CheckException(env);'
+ return_type = JavaDataTypeToC(called_by_native.return_type)
+ return_declaration = ''
+ return_clause = ''
+ if return_type != 'void':
+ pre_call = ' ' + pre_call
+ return_declaration = return_type + ' ret ='
+ if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
+ return_type = 'ScopedJavaLocalRef<' + return_type + '>'
+ return_clause = 'return ' + return_type + '(env, ret);'
+ else:
+ return_clause = 'return ret;'
+ values = {
+ 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
+ 'METHOD': called_by_native.name,
+ 'RETURN_TYPE': return_type,
+ 'RETURN_DECLARATION': return_declaration,
+ 'RETURN_CLAUSE': return_clause,
+ 'FIRST_PARAM_IN_DECLARATION': first_param_in_declaration,
+ 'PARAMS_IN_DECLARATION': params_in_declaration,
+ 'STATIC': 'Static' if called_by_native.static else '',
+ 'PRE_CALL': pre_call,
+ 'POST_CALL': post_call,
+ 'ENV_CALL': called_by_native.env_call,
+ 'FIRST_PARAM_IN_CALL': first_param_in_call,
+ 'PARAMS_IN_CALL': params_for_call,
+ 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
+ 'CHECK_EXCEPTION': check_exception,
+ 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native)
+ }
+ values['FUNCTION_SIGNATURE'] = (
+ function_signature_template.substitute(values))
+ if called_by_native.system_class:
+ values['FUNCTION_HEADER'] = (
+ function_header_with_unused_template.substitute(values))
+ else:
+ values['FUNCTION_HEADER'] = function_header_template.substitute(values)
+ return template.substitute(values)
+
+ def GetKMethodArrayEntry(self, native):
+ template = Template("""\
+ { "native${NAME}", ${JNI_SIGNATURE}, reinterpret_cast<void*>(${NAME}) },""")
+ values = {'NAME': native.name,
+ 'JNI_SIGNATURE': JniParams.Signature(native.params,
+ native.return_type,
+ True)}
+ return template.substitute(values)
+
+ def GetUniqueClasses(self, origin):
+ ret = {self.class_name: self.fully_qualified_class}
+ for entry in origin:
+ class_name = self.class_name
+ jni_class_path = self.fully_qualified_class
+ if entry.java_class_name:
+ class_name = entry.java_class_name
+ jni_class_path = self.fully_qualified_class + '$' + class_name
+ ret[class_name] = jni_class_path
+ return ret
+
+ def GetClassPathDefinitions(self):
+ """Returns the ClassPath constants."""
+ ret = []
+ template = Template("""\
+const char k${JAVA_CLASS}ClassPath[] = "${JNI_CLASS_PATH}";""")
+ native_classes = self.GetUniqueClasses(self.natives)
+ called_by_native_classes = self.GetUniqueClasses(self.called_by_natives)
+ all_classes = native_classes
+ all_classes.update(called_by_native_classes)
+ for clazz in all_classes:
+ values = {
+ 'JAVA_CLASS': clazz,
+ 'JNI_CLASS_PATH': JniParams.RemapClassName(all_classes[clazz]),
+ }
+ ret += [template.substitute(values)]
+ ret += ''
+ for clazz in called_by_native_classes:
+ template = Template("""\
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_${JAVA_CLASS}_clazz = NULL;""")
+ values = {
+ 'JAVA_CLASS': clazz,
+ }
+ ret += [template.substitute(values)]
+ return '\n'.join(ret)
+
+ def GetFindClasses(self):
+ """Returns the imlementation of FindClass for all known classes."""
+ template = Template("""\
+ g_${JAVA_CLASS}_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetClass(env, k${JAVA_CLASS}ClassPath).obj()));""")
+ ret = []
+ for clazz in self.GetUniqueClasses(self.called_by_natives):
+ values = {'JAVA_CLASS': clazz}
+ ret += [template.substitute(values)]
+ return '\n'.join(ret)
+
+ def GetMethodIDImpl(self, called_by_native):
+ """Returns the implementation of GetMethodID."""
+ template = Template("""\
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_${STATIC}>(
+ env, g_${JAVA_CLASS}_clazz,
+ "${JNI_NAME}",
+ ${JNI_SIGNATURE},
+ &g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME});
+""")
+ jni_name = called_by_native.name
+ jni_return_type = called_by_native.return_type
+ if called_by_native.is_constructor:
+ jni_name = '<init>'
+ jni_return_type = 'void'
+ values = {
+ 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
+ 'JNI_NAME': jni_name,
+ 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name,
+ 'STATIC': 'STATIC' if called_by_native.static else 'INSTANCE',
+ 'JNI_SIGNATURE': JniParams.Signature(called_by_native.params,
+ jni_return_type,
+ True)
+ }
+ return template.substitute(values)
+
+
+def WrapOutput(output):
+ ret = []
+ for line in output.splitlines():
+ # Do not wrap lines under 80 characters or preprocessor directives.
+ if len(line) < 80 or line.lstrip()[:1] == '#':
+ stripped = line.rstrip()
+ if len(ret) == 0 or len(ret[-1]) or len(stripped):
+ ret.append(stripped)
+ else:
+ first_line_indent = ' ' * (len(line) - len(line.lstrip()))
+ subsequent_indent = first_line_indent + ' ' * 4
+ if line.startswith('//'):
+ subsequent_indent = '//' + subsequent_indent
+ wrapper = textwrap.TextWrapper(width=80,
+ subsequent_indent=subsequent_indent,
+ break_long_words=False)
+ ret += [wrapped.rstrip() for wrapped in wrapper.wrap(line)]
+ ret += ['']
+ return '\n'.join(ret)
+
+
+def ExtractJarInputFile(jar_file, input_file, out_dir):
+ """Extracts input file from jar and returns the filename.
+
+ The input file is extracted to the same directory that the generated jni
+ headers will be placed in. This is passed as an argument to script.
+
+ Args:
+ jar_file: the jar file containing the input files to extract.
+ input_files: the list of files to extract from the jar file.
+ out_dir: the name of the directories to extract to.
+
+ Returns:
+ the name of extracted input file.
+ """
+ jar_file = zipfile.ZipFile(jar_file)
+
+ out_dir = os.path.join(out_dir, os.path.dirname(input_file))
+ try:
+ os.makedirs(out_dir)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise
+ extracted_file_name = os.path.join(out_dir, os.path.basename(input_file))
+ with open(extracted_file_name, 'w') as outfile:
+ outfile.write(jar_file.read(input_file))
+
+ return extracted_file_name
+
+
+def GenerateJNIHeader(input_file, output_file, namespace, skip_if_same):
+ try:
+ if os.path.splitext(input_file)[1] == '.class':
+ jni_from_javap = JNIFromJavaP.CreateFromClass(input_file, namespace)
+ content = jni_from_javap.GetContent()
+ else:
+ jni_from_java_source = JNIFromJavaSource.CreateFromFile(input_file)
+ content = jni_from_java_source.GetContent()
+ except ParseError, e:
+ print e
+ sys.exit(1)
+ if output_file:
+ if not os.path.exists(os.path.dirname(os.path.abspath(output_file))):
+ os.makedirs(os.path.dirname(os.path.abspath(output_file)))
+ if skip_if_same and os.path.exists(output_file):
+ with file(output_file, 'r') as f:
+ existing_content = f.read()
+ if existing_content == content:
+ return
+ with file(output_file, 'w') as f:
+ f.write(content)
+ else:
+ print output
+
+
+def main(argv):
+ usage = """usage: %prog [OPTIONS]
+This script will parse the given java source code extracting the native
+declarations and print the header file to stdout (or a file).
+See SampleForTests.java for more details.
+ """
+ option_parser = optparse.OptionParser(usage=usage)
+ option_parser.add_option('-j', dest='jar_file',
+ help='Extract the list of input files from'
+ ' a specified jar file.'
+ ' Uses javap to extract the methods from a'
+ ' pre-compiled class. --input should point'
+ ' to pre-compiled Java .class files.')
+ option_parser.add_option('-n', dest='namespace',
+ help='Uses as a namespace in the generated header,'
+ ' instead of the javap class name.')
+ option_parser.add_option('--input_file',
+ help='Single input file name. The output file name '
+ 'will be derived from it. Must be used with '
+ '--output_dir.')
+ option_parser.add_option('--output_dir',
+ help='The output directory. Must be used with '
+ '--input')
+ option_parser.add_option('--optimize_generation', type="int",
+ default=0, help='Whether we should optimize JNI '
+ 'generation by not regenerating files if they have '
+ 'not changed.')
+ option_parser.add_option('--jarjar',
+ help='Path to optional jarjar rules file.')
+ options, args = option_parser.parse_args(argv)
+ if options.jar_file:
+ input_file = ExtractJarInputFile(options.jar_file, options.input_file,
+ options.output_dir)
+ else:
+ input_file = options.input_file
+ output_file = None
+ if options.output_dir:
+ root_name = os.path.splitext(os.path.basename(input_file))[0]
+ output_file = os.path.join(options.output_dir, root_name) + '_jni.h'
+ if options.jarjar:
+ with open(options.jarjar) as f:
+ JniParams.SetJarJarMappings(f.read())
+ GenerateJNIHeader(input_file, output_file, options.namespace,
+ options.optimize_generation)
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/chromium/base/android/jni_generator/jni_generator_tests.py b/chromium/base/android/jni_generator/jni_generator_tests.py
new file mode 100755
index 00000000000..f008f394cec
--- /dev/null
+++ b/chromium/base/android/jni_generator/jni_generator_tests.py
@@ -0,0 +1,2084 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 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.
+
+"""Tests for jni_generator.py.
+
+This test suite contains various tests for the JNI generator.
+It exercises the low-level parser all the way up to the
+code generator and ensures the output matches a golden
+file.
+"""
+
+import difflib
+import os
+import sys
+import unittest
+import jni_generator
+from jni_generator import CalledByNative, JniParams, NativeMethod, Param
+
+
+class TestGenerator(unittest.TestCase):
+ def assertObjEquals(self, first, second):
+ dict_first = first.__dict__
+ dict_second = second.__dict__
+ self.assertEquals(dict_first.keys(), dict_second.keys())
+ for key, value in dict_first.iteritems():
+ if (type(value) is list and len(value) and
+ isinstance(type(value[0]), object)):
+ self.assertListEquals(value, second.__getattribute__(key))
+ else:
+ actual = second.__getattribute__(key)
+ self.assertEquals(value, actual,
+ 'Key ' + key + ': ' + str(value) + '!=' + str(actual))
+
+ def assertListEquals(self, first, second):
+ self.assertEquals(len(first), len(second))
+ for i in xrange(len(first)):
+ if isinstance(first[i], object):
+ self.assertObjEquals(first[i], second[i])
+ else:
+ self.assertEquals(first[i], second[i])
+
+ def assertTextEquals(self, golden_text, generated_text):
+ stripped_golden = [l.strip() for l in golden_text.split('\n')]
+ stripped_generated = [l.strip() for l in generated_text.split('\n')]
+ if stripped_golden != stripped_generated:
+ print self.id()
+ for line in difflib.context_diff(stripped_golden, stripped_generated):
+ print line
+ print '\n\nGenerated'
+ print '=' * 80
+ print generated_text
+ print '=' * 80
+ self.fail('Golden text mismatch')
+
+ def testNatives(self):
+ test_data = """"
+ interface OnFrameAvailableListener {}
+ private native int nativeInit();
+ private native void nativeDestroy(int nativeChromeBrowserProvider);
+ private native long nativeAddBookmark(
+ int nativeChromeBrowserProvider,
+ String url, String title, boolean isFolder, long parentId);
+ private static native String nativeGetDomainAndRegistry(String url);
+ private static native void nativeCreateHistoricalTabFromState(
+ byte[] state, int tab_index);
+ private native byte[] nativeGetStateAsByteArray(View view);
+ private static native String[] nativeGetAutofillProfileGUIDs();
+ private native void nativeSetRecognitionResults(
+ int sessionId, String[] results);
+ private native long nativeAddBookmarkFromAPI(
+ int nativeChromeBrowserProvider,
+ String url, Long created, Boolean isBookmark,
+ Long date, byte[] favicon, String title, Integer visits);
+ native int nativeFindAll(String find);
+ private static native OnFrameAvailableListener nativeGetInnerClass();
+ private native Bitmap nativeQueryBitmap(
+ int nativeChromeBrowserProvider,
+ String[] projection, String selection,
+ String[] selectionArgs, String sortOrder);
+ private native void nativeGotOrientation(
+ int nativeDataFetcherImplAndroid,
+ double alpha, double beta, double gamma);
+ """
+ jni_generator.JniParams.ExtractImportsAndInnerClasses(test_data)
+ natives = jni_generator.ExtractNatives(test_data)
+ golden_natives = [
+ NativeMethod(return_type='int', static=False,
+ name='Init',
+ params=[],
+ java_class_name=None,
+ type='function'),
+ NativeMethod(return_type='void', static=False, name='Destroy',
+ params=[Param(datatype='int',
+ name='nativeChromeBrowserProvider')],
+ java_class_name=None,
+ type='method',
+ p0_type='ChromeBrowserProvider'),
+ NativeMethod(return_type='long', static=False, name='AddBookmark',
+ params=[Param(datatype='int',
+ name='nativeChromeBrowserProvider'),
+ Param(datatype='String',
+ name='url'),
+ Param(datatype='String',
+ name='title'),
+ Param(datatype='boolean',
+ name='isFolder'),
+ Param(datatype='long',
+ name='parentId')],
+ java_class_name=None,
+ type='method',
+ p0_type='ChromeBrowserProvider'),
+ NativeMethod(return_type='String', static=True,
+ name='GetDomainAndRegistry',
+ params=[Param(datatype='String',
+ name='url')],
+ java_class_name=None,
+ type='function'),
+ NativeMethod(return_type='void', static=True,
+ name='CreateHistoricalTabFromState',
+ params=[Param(datatype='byte[]',
+ name='state'),
+ Param(datatype='int',
+ name='tab_index')],
+ java_class_name=None,
+ type='function'),
+ NativeMethod(return_type='byte[]', static=False,
+ name='GetStateAsByteArray',
+ params=[Param(datatype='View', name='view')],
+ java_class_name=None,
+ type='function'),
+ NativeMethod(return_type='String[]', static=True,
+ name='GetAutofillProfileGUIDs', params=[],
+ java_class_name=None,
+ type='function'),
+ NativeMethod(return_type='void', static=False,
+ name='SetRecognitionResults',
+ params=[Param(datatype='int', name='sessionId'),
+ Param(datatype='String[]', name='results')],
+ java_class_name=None,
+ type='function'),
+ NativeMethod(return_type='long', static=False,
+ name='AddBookmarkFromAPI',
+ params=[Param(datatype='int',
+ name='nativeChromeBrowserProvider'),
+ Param(datatype='String',
+ name='url'),
+ Param(datatype='Long',
+ name='created'),
+ Param(datatype='Boolean',
+ name='isBookmark'),
+ Param(datatype='Long',
+ name='date'),
+ Param(datatype='byte[]',
+ name='favicon'),
+ Param(datatype='String',
+ name='title'),
+ Param(datatype='Integer',
+ name='visits')],
+ java_class_name=None,
+ type='method',
+ p0_type='ChromeBrowserProvider'),
+ NativeMethod(return_type='int', static=False,
+ name='FindAll',
+ params=[Param(datatype='String',
+ name='find')],
+ java_class_name=None,
+ type='function'),
+ NativeMethod(return_type='OnFrameAvailableListener', static=True,
+ name='GetInnerClass',
+ params=[],
+ java_class_name=None,
+ type='function'),
+ NativeMethod(return_type='Bitmap',
+ static=False,
+ name='QueryBitmap',
+ params=[Param(datatype='int',
+ name='nativeChromeBrowserProvider'),
+ Param(datatype='String[]',
+ name='projection'),
+ Param(datatype='String',
+ name='selection'),
+ Param(datatype='String[]',
+ name='selectionArgs'),
+ Param(datatype='String',
+ name='sortOrder'),
+ ],
+ java_class_name=None,
+ type='method',
+ p0_type='ChromeBrowserProvider'),
+ NativeMethod(return_type='void', static=False,
+ name='GotOrientation',
+ params=[Param(datatype='int',
+ name='nativeDataFetcherImplAndroid'),
+ Param(datatype='double',
+ name='alpha'),
+ Param(datatype='double',
+ name='beta'),
+ Param(datatype='double',
+ name='gamma'),
+ ],
+ java_class_name=None,
+ type='method',
+ p0_type='content::DataFetcherImplAndroid'),
+ ]
+ self.assertListEquals(golden_natives, natives)
+ h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
+ natives, [])
+ golden_content = """\
+// Copyright (c) 2012 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.
+
+// This file is autogenerated by
+// base/android/jni_generator/jni_generator_tests.py
+// For
+// org/chromium/TestJni
+
+#ifndef org_chromium_TestJni_JNI
+#define org_chromium_TestJni_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+using base::android::ScopedJavaLocalRef;
+
+// Step 1: forward declarations.
+namespace {
+const char kTestJniClassPath[] = "org/chromium/TestJni";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_TestJni_clazz = NULL;
+} // namespace
+
+static jint Init(JNIEnv* env, jobject obj);
+
+static jstring GetDomainAndRegistry(JNIEnv* env, jclass clazz,
+ jstring url);
+
+static void CreateHistoricalTabFromState(JNIEnv* env, jclass clazz,
+ jbyteArray state,
+ jint tab_index);
+
+static jbyteArray GetStateAsByteArray(JNIEnv* env, jobject obj,
+ jobject view);
+
+static jobjectArray GetAutofillProfileGUIDs(JNIEnv* env, jclass clazz);
+
+static void SetRecognitionResults(JNIEnv* env, jobject obj,
+ jint sessionId,
+ jobjectArray results);
+
+static jint FindAll(JNIEnv* env, jobject obj,
+ jstring find);
+
+static jobject GetInnerClass(JNIEnv* env, jclass clazz);
+
+// Step 2: method stubs.
+static void Destroy(JNIEnv* env, jobject obj,
+ jint nativeChromeBrowserProvider) {
+ DCHECK(nativeChromeBrowserProvider) << "Destroy";
+ ChromeBrowserProvider* native =
+ reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider);
+ return native->Destroy(env, obj);
+}
+
+static jlong AddBookmark(JNIEnv* env, jobject obj,
+ jint nativeChromeBrowserProvider,
+ jstring url,
+ jstring title,
+ jboolean isFolder,
+ jlong parentId) {
+ DCHECK(nativeChromeBrowserProvider) << "AddBookmark";
+ ChromeBrowserProvider* native =
+ reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider);
+ return native->AddBookmark(env, obj, url, title, isFolder, parentId);
+}
+
+static jlong AddBookmarkFromAPI(JNIEnv* env, jobject obj,
+ jint nativeChromeBrowserProvider,
+ jstring url,
+ jobject created,
+ jobject isBookmark,
+ jobject date,
+ jbyteArray favicon,
+ jstring title,
+ jobject visits) {
+ DCHECK(nativeChromeBrowserProvider) << "AddBookmarkFromAPI";
+ ChromeBrowserProvider* native =
+ reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider);
+ return native->AddBookmarkFromAPI(env, obj, url, created, isBookmark, date,
+ favicon, title, visits);
+}
+
+static jobject QueryBitmap(JNIEnv* env, jobject obj,
+ jint nativeChromeBrowserProvider,
+ jobjectArray projection,
+ jstring selection,
+ jobjectArray selectionArgs,
+ jstring sortOrder) {
+ DCHECK(nativeChromeBrowserProvider) << "QueryBitmap";
+ ChromeBrowserProvider* native =
+ reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider);
+ return native->QueryBitmap(env, obj, projection, selection, selectionArgs,
+ sortOrder).Release();
+}
+
+static void GotOrientation(JNIEnv* env, jobject obj,
+ jint nativeDataFetcherImplAndroid,
+ jdouble alpha,
+ jdouble beta,
+ jdouble gamma) {
+ DCHECK(nativeDataFetcherImplAndroid) << "GotOrientation";
+ DataFetcherImplAndroid* native =
+ reinterpret_cast<DataFetcherImplAndroid*>(nativeDataFetcherImplAndroid);
+ return native->GotOrientation(env, obj, alpha, beta, gamma);
+}
+
+// Step 3: RegisterNatives.
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+
+ g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetClass(env, kTestJniClassPath).obj()));
+ static const JNINativeMethod kMethodsTestJni[] = {
+ { "nativeInit",
+"("
+")"
+"I", reinterpret_cast<void*>(Init) },
+ { "nativeDestroy",
+"("
+"I"
+")"
+"V", reinterpret_cast<void*>(Destroy) },
+ { "nativeAddBookmark",
+"("
+"I"
+"Ljava/lang/String;"
+"Ljava/lang/String;"
+"Z"
+"J"
+")"
+"J", reinterpret_cast<void*>(AddBookmark) },
+ { "nativeGetDomainAndRegistry",
+"("
+"Ljava/lang/String;"
+")"
+"Ljava/lang/String;", reinterpret_cast<void*>(GetDomainAndRegistry) },
+ { "nativeCreateHistoricalTabFromState",
+"("
+"[B"
+"I"
+")"
+"V", reinterpret_cast<void*>(CreateHistoricalTabFromState) },
+ { "nativeGetStateAsByteArray",
+"("
+"Landroid/view/View;"
+")"
+"[B", reinterpret_cast<void*>(GetStateAsByteArray) },
+ { "nativeGetAutofillProfileGUIDs",
+"("
+")"
+"[Ljava/lang/String;", reinterpret_cast<void*>(GetAutofillProfileGUIDs) },
+ { "nativeSetRecognitionResults",
+"("
+"I"
+"[Ljava/lang/String;"
+")"
+"V", reinterpret_cast<void*>(SetRecognitionResults) },
+ { "nativeAddBookmarkFromAPI",
+"("
+"I"
+"Ljava/lang/String;"
+"Ljava/lang/Long;"
+"Ljava/lang/Boolean;"
+"Ljava/lang/Long;"
+"[B"
+"Ljava/lang/String;"
+"Ljava/lang/Integer;"
+")"
+"J", reinterpret_cast<void*>(AddBookmarkFromAPI) },
+ { "nativeFindAll",
+"("
+"Ljava/lang/String;"
+")"
+"I", reinterpret_cast<void*>(FindAll) },
+ { "nativeGetInnerClass",
+"("
+")"
+"Lorg/chromium/example/jni_generator/SampleForTests$OnFrameAvailableListener;",
+ reinterpret_cast<void*>(GetInnerClass) },
+ { "nativeQueryBitmap",
+"("
+"I"
+"[Ljava/lang/String;"
+"Ljava/lang/String;"
+"[Ljava/lang/String;"
+"Ljava/lang/String;"
+")"
+"Landroid/graphics/Bitmap;", reinterpret_cast<void*>(QueryBitmap) },
+ { "nativeGotOrientation",
+"("
+"I"
+"D"
+"D"
+"D"
+")"
+"V", reinterpret_cast<void*>(GotOrientation) },
+ };
+ const int kMethodsTestJniSize = arraysize(kMethodsTestJni);
+
+ if (env->RegisterNatives(g_TestJni_clazz,
+ kMethodsTestJni,
+ kMethodsTestJniSize) < 0) {
+ LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
+ return false;
+ }
+
+ return true;
+}
+
+#endif // org_chromium_TestJni_JNI
+"""
+ self.assertTextEquals(golden_content, h.GetContent())
+
+ def testInnerClassNatives(self):
+ test_data = """
+ class MyInnerClass {
+ @NativeCall("MyInnerClass")
+ private native int nativeInit();
+ }
+ """
+ natives = jni_generator.ExtractNatives(test_data)
+ golden_natives = [
+ NativeMethod(return_type='int', static=False,
+ name='Init', params=[],
+ java_class_name='MyInnerClass',
+ type='function')
+ ]
+ self.assertListEquals(golden_natives, natives)
+ h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
+ natives, [])
+ golden_content = """\
+// Copyright (c) 2012 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.
+
+// This file is autogenerated by
+// base/android/jni_generator/jni_generator_tests.py
+// For
+// org/chromium/TestJni
+
+#ifndef org_chromium_TestJni_JNI
+#define org_chromium_TestJni_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+using base::android::ScopedJavaLocalRef;
+
+// Step 1: forward declarations.
+namespace {
+const char kTestJniClassPath[] = "org/chromium/TestJni";
+const char kMyInnerClassClassPath[] = "org/chromium/TestJni$MyInnerClass";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_TestJni_clazz = NULL;
+} // namespace
+
+static jint Init(JNIEnv* env, jobject obj);
+
+// Step 2: method stubs.
+
+// Step 3: RegisterNatives.
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+
+ g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetClass(env, kTestJniClassPath).obj()));
+ static const JNINativeMethod kMethodsMyInnerClass[] = {
+ { "nativeInit",
+"("
+")"
+"I", reinterpret_cast<void*>(Init) },
+ };
+ const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass);
+
+ if (env->RegisterNatives(g_MyInnerClass_clazz,
+ kMethodsMyInnerClass,
+ kMethodsMyInnerClassSize) < 0) {
+ LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
+ return false;
+ }
+
+ return true;
+}
+
+#endif // org_chromium_TestJni_JNI
+"""
+ self.assertTextEquals(golden_content, h.GetContent())
+
+ def testInnerClassNativesMultiple(self):
+ test_data = """
+ class MyInnerClass {
+ @NativeCall("MyInnerClass")
+ private native int nativeInit();
+ }
+ class MyOtherInnerClass {
+ @NativeCall("MyOtherInnerClass")
+ private native int nativeInit();
+ }
+ """
+ natives = jni_generator.ExtractNatives(test_data)
+ golden_natives = [
+ NativeMethod(return_type='int', static=False,
+ name='Init', params=[],
+ java_class_name='MyInnerClass',
+ type='function'),
+ NativeMethod(return_type='int', static=False,
+ name='Init', params=[],
+ java_class_name='MyOtherInnerClass',
+ type='function')
+ ]
+ self.assertListEquals(golden_natives, natives)
+ h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
+ natives, [])
+ golden_content = """\
+// Copyright (c) 2012 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.
+
+// This file is autogenerated by
+// base/android/jni_generator/jni_generator_tests.py
+// For
+// org/chromium/TestJni
+
+#ifndef org_chromium_TestJni_JNI
+#define org_chromium_TestJni_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+using base::android::ScopedJavaLocalRef;
+
+// Step 1: forward declarations.
+namespace {
+const char kMyOtherInnerClassClassPath[] =
+ "org/chromium/TestJni$MyOtherInnerClass";
+const char kTestJniClassPath[] = "org/chromium/TestJni";
+const char kMyInnerClassClassPath[] = "org/chromium/TestJni$MyInnerClass";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_TestJni_clazz = NULL;
+} // namespace
+
+static jint Init(JNIEnv* env, jobject obj);
+
+static jint Init(JNIEnv* env, jobject obj);
+
+// Step 2: method stubs.
+
+// Step 3: RegisterNatives.
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+
+ g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetClass(env, kTestJniClassPath).obj()));
+ static const JNINativeMethod kMethodsMyOtherInnerClass[] = {
+ { "nativeInit",
+"("
+")"
+"I", reinterpret_cast<void*>(Init) },
+ };
+ const int kMethodsMyOtherInnerClassSize =
+ arraysize(kMethodsMyOtherInnerClass);
+
+ if (env->RegisterNatives(g_MyOtherInnerClass_clazz,
+ kMethodsMyOtherInnerClass,
+ kMethodsMyOtherInnerClassSize) < 0) {
+ LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
+ return false;
+ }
+
+ static const JNINativeMethod kMethodsMyInnerClass[] = {
+ { "nativeInit",
+"("
+")"
+"I", reinterpret_cast<void*>(Init) },
+ };
+ const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass);
+
+ if (env->RegisterNatives(g_MyInnerClass_clazz,
+ kMethodsMyInnerClass,
+ kMethodsMyInnerClassSize) < 0) {
+ LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
+ return false;
+ }
+
+ return true;
+}
+
+#endif // org_chromium_TestJni_JNI
+"""
+ self.assertTextEquals(golden_content, h.GetContent())
+
+ def testInnerClassNativesBothInnerAndOuter(self):
+ test_data = """
+ class MyOuterClass {
+ private native int nativeInit();
+ class MyOtherInnerClass {
+ @NativeCall("MyOtherInnerClass")
+ private native int nativeInit();
+ }
+ }
+ """
+ natives = jni_generator.ExtractNatives(test_data)
+ golden_natives = [
+ NativeMethod(return_type='int', static=False,
+ name='Init', params=[],
+ java_class_name=None,
+ type='function'),
+ NativeMethod(return_type='int', static=False,
+ name='Init', params=[],
+ java_class_name='MyOtherInnerClass',
+ type='function')
+ ]
+ self.assertListEquals(golden_natives, natives)
+ h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
+ natives, [])
+ golden_content = """\
+// Copyright (c) 2012 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.
+
+// This file is autogenerated by
+// base/android/jni_generator/jni_generator_tests.py
+// For
+// org/chromium/TestJni
+
+#ifndef org_chromium_TestJni_JNI
+#define org_chromium_TestJni_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+using base::android::ScopedJavaLocalRef;
+
+// Step 1: forward declarations.
+namespace {
+const char kMyOtherInnerClassClassPath[] =
+ "org/chromium/TestJni$MyOtherInnerClass";
+const char kTestJniClassPath[] = "org/chromium/TestJni";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_TestJni_clazz = NULL;
+} // namespace
+
+static jint Init(JNIEnv* env, jobject obj);
+
+static jint Init(JNIEnv* env, jobject obj);
+
+// Step 2: method stubs.
+
+// Step 3: RegisterNatives.
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+
+ g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetClass(env, kTestJniClassPath).obj()));
+ static const JNINativeMethod kMethodsMyOtherInnerClass[] = {
+ { "nativeInit",
+"("
+")"
+"I", reinterpret_cast<void*>(Init) },
+ };
+ const int kMethodsMyOtherInnerClassSize =
+ arraysize(kMethodsMyOtherInnerClass);
+
+ if (env->RegisterNatives(g_MyOtherInnerClass_clazz,
+ kMethodsMyOtherInnerClass,
+ kMethodsMyOtherInnerClassSize) < 0) {
+ LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
+ return false;
+ }
+
+ static const JNINativeMethod kMethodsTestJni[] = {
+ { "nativeInit",
+"("
+")"
+"I", reinterpret_cast<void*>(Init) },
+ };
+ const int kMethodsTestJniSize = arraysize(kMethodsTestJni);
+
+ if (env->RegisterNatives(g_TestJni_clazz,
+ kMethodsTestJni,
+ kMethodsTestJniSize) < 0) {
+ LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
+ return false;
+ }
+
+ return true;
+}
+
+#endif // org_chromium_TestJni_JNI
+"""
+ self.assertTextEquals(golden_content, h.GetContent())
+
+ def testCalledByNatives(self):
+ test_data = """"
+ import android.graphics.Bitmap;
+ import android.view.View;
+ import java.io.InputStream;
+ import java.util.List;
+
+ class InnerClass {}
+
+ @CalledByNative
+ InnerClass showConfirmInfoBar(int nativeInfoBar,
+ String buttonOk, String buttonCancel, String title, Bitmap icon) {
+ InfoBar infobar = new ConfirmInfoBar(nativeInfoBar, mContext,
+ buttonOk, buttonCancel,
+ title, icon);
+ return infobar;
+ }
+ @CalledByNative
+ InnerClass showAutoLoginInfoBar(int nativeInfoBar,
+ String realm, String account, String args) {
+ AutoLoginInfoBar infobar = new AutoLoginInfoBar(nativeInfoBar, mContext,
+ realm, account, args);
+ if (infobar.displayedAccountCount() == 0)
+ infobar = null;
+ return infobar;
+ }
+ @CalledByNative("InfoBar")
+ void dismiss();
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private static boolean shouldShowAutoLogin(View view,
+ String realm, String account, String args) {
+ AccountManagerContainer accountManagerContainer =
+ new AccountManagerContainer((Activity)contentView.getContext(),
+ realm, account, args);
+ String[] logins = accountManagerContainer.getAccountLogins(null);
+ return logins.length != 0;
+ }
+ @CalledByNative
+ static InputStream openUrl(String url) {
+ return null;
+ }
+ @CalledByNative
+ private void activateHardwareAcceleration(final boolean activated,
+ final int iPid, final int iType,
+ final int iPrimaryID, final int iSecondaryID) {
+ if (!activated) {
+ return
+ }
+ }
+ @CalledByNativeUnchecked
+ private void uncheckedCall(int iParam);
+
+ @CalledByNative
+ public byte[] returnByteArray();
+
+ @CalledByNative
+ public boolean[] returnBooleanArray();
+
+ @CalledByNative
+ public char[] returnCharArray();
+
+ @CalledByNative
+ public short[] returnShortArray();
+
+ @CalledByNative
+ public int[] returnIntArray();
+
+ @CalledByNative
+ public long[] returnLongArray();
+
+ @CalledByNative
+ public double[] returnDoubleArray();
+
+ @CalledByNative
+ public Object[] returnObjectArray();
+
+ @CalledByNative
+ public byte[][] returnArrayOfByteArray();
+
+ @CalledByNative
+ public Bitmap.CompressFormat getCompressFormat();
+
+ @CalledByNative
+ public List<Bitmap.CompressFormat> getCompressFormatList();
+ """
+ jni_generator.JniParams.SetFullyQualifiedClass('org/chromium/Foo')
+ jni_generator.JniParams.ExtractImportsAndInnerClasses(test_data)
+ called_by_natives = jni_generator.ExtractCalledByNatives(test_data)
+ golden_called_by_natives = [
+ CalledByNative(
+ return_type='InnerClass',
+ system_class=False,
+ static=False,
+ name='showConfirmInfoBar',
+ method_id_var_name='showConfirmInfoBar',
+ java_class_name='',
+ params=[Param(datatype='int', name='nativeInfoBar'),
+ Param(datatype='String', name='buttonOk'),
+ Param(datatype='String', name='buttonCancel'),
+ Param(datatype='String', name='title'),
+ Param(datatype='Bitmap', name='icon')],
+ env_call=('Object', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='InnerClass',
+ system_class=False,
+ static=False,
+ name='showAutoLoginInfoBar',
+ method_id_var_name='showAutoLoginInfoBar',
+ java_class_name='',
+ params=[Param(datatype='int', name='nativeInfoBar'),
+ Param(datatype='String', name='realm'),
+ Param(datatype='String', name='account'),
+ Param(datatype='String', name='args')],
+ env_call=('Object', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='void',
+ system_class=False,
+ static=False,
+ name='dismiss',
+ method_id_var_name='dismiss',
+ java_class_name='InfoBar',
+ params=[],
+ env_call=('Void', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='boolean',
+ system_class=False,
+ static=True,
+ name='shouldShowAutoLogin',
+ method_id_var_name='shouldShowAutoLogin',
+ java_class_name='',
+ params=[Param(datatype='View', name='view'),
+ Param(datatype='String', name='realm'),
+ Param(datatype='String', name='account'),
+ Param(datatype='String', name='args')],
+ env_call=('Boolean', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='InputStream',
+ system_class=False,
+ static=True,
+ name='openUrl',
+ method_id_var_name='openUrl',
+ java_class_name='',
+ params=[Param(datatype='String', name='url')],
+ env_call=('Object', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='void',
+ system_class=False,
+ static=False,
+ name='activateHardwareAcceleration',
+ method_id_var_name='activateHardwareAcceleration',
+ java_class_name='',
+ params=[Param(datatype='boolean', name='activated'),
+ Param(datatype='int', name='iPid'),
+ Param(datatype='int', name='iType'),
+ Param(datatype='int', name='iPrimaryID'),
+ Param(datatype='int', name='iSecondaryID'),
+ ],
+ env_call=('Void', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='void',
+ system_class=False,
+ static=False,
+ name='uncheckedCall',
+ method_id_var_name='uncheckedCall',
+ java_class_name='',
+ params=[Param(datatype='int', name='iParam')],
+ env_call=('Void', ''),
+ unchecked=True,
+ ),
+ CalledByNative(
+ return_type='byte[]',
+ system_class=False,
+ static=False,
+ name='returnByteArray',
+ method_id_var_name='returnByteArray',
+ java_class_name='',
+ params=[],
+ env_call=('Void', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='boolean[]',
+ system_class=False,
+ static=False,
+ name='returnBooleanArray',
+ method_id_var_name='returnBooleanArray',
+ java_class_name='',
+ params=[],
+ env_call=('Void', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='char[]',
+ system_class=False,
+ static=False,
+ name='returnCharArray',
+ method_id_var_name='returnCharArray',
+ java_class_name='',
+ params=[],
+ env_call=('Void', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='short[]',
+ system_class=False,
+ static=False,
+ name='returnShortArray',
+ method_id_var_name='returnShortArray',
+ java_class_name='',
+ params=[],
+ env_call=('Void', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='int[]',
+ system_class=False,
+ static=False,
+ name='returnIntArray',
+ method_id_var_name='returnIntArray',
+ java_class_name='',
+ params=[],
+ env_call=('Void', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='long[]',
+ system_class=False,
+ static=False,
+ name='returnLongArray',
+ method_id_var_name='returnLongArray',
+ java_class_name='',
+ params=[],
+ env_call=('Void', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='double[]',
+ system_class=False,
+ static=False,
+ name='returnDoubleArray',
+ method_id_var_name='returnDoubleArray',
+ java_class_name='',
+ params=[],
+ env_call=('Void', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='Object[]',
+ system_class=False,
+ static=False,
+ name='returnObjectArray',
+ method_id_var_name='returnObjectArray',
+ java_class_name='',
+ params=[],
+ env_call=('Void', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='byte[][]',
+ system_class=False,
+ static=False,
+ name='returnArrayOfByteArray',
+ method_id_var_name='returnArrayOfByteArray',
+ java_class_name='',
+ params=[],
+ env_call=('Void', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='Bitmap.CompressFormat',
+ system_class=False,
+ static=False,
+ name='getCompressFormat',
+ method_id_var_name='getCompressFormat',
+ java_class_name='',
+ params=[],
+ env_call=('Void', ''),
+ unchecked=False,
+ ),
+ CalledByNative(
+ return_type='List<Bitmap.CompressFormat>',
+ system_class=False,
+ static=False,
+ name='getCompressFormatList',
+ method_id_var_name='getCompressFormatList',
+ java_class_name='',
+ params=[],
+ env_call=('Void', ''),
+ unchecked=False,
+ ),
+ ]
+ self.assertListEquals(golden_called_by_natives, called_by_natives)
+ h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
+ [], called_by_natives)
+ golden_content = """\
+// Copyright (c) 2012 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.
+
+// This file is autogenerated by
+// base/android/jni_generator/jni_generator_tests.py
+// For
+// org/chromium/TestJni
+
+#ifndef org_chromium_TestJni_JNI
+#define org_chromium_TestJni_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+using base::android::ScopedJavaLocalRef;
+
+// Step 1: forward declarations.
+namespace {
+const char kTestJniClassPath[] = "org/chromium/TestJni";
+const char kInfoBarClassPath[] = "org/chromium/TestJni$InfoBar";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_TestJni_clazz = NULL;
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_InfoBar_clazz = NULL;
+} // namespace
+
+// Step 2: method stubs.
+
+static base::subtle::AtomicWord g_TestJni_showConfirmInfoBar = 0;
+static ScopedJavaLocalRef<jobject> Java_TestJni_showConfirmInfoBar(JNIEnv* env,
+ jobject obj, jint nativeInfoBar,
+ jstring buttonOk,
+ jstring buttonCancel,
+ jstring title,
+ jobject icon) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_TestJni_clazz,
+ "showConfirmInfoBar",
+
+"("
+"I"
+"Ljava/lang/String;"
+"Ljava/lang/String;"
+"Ljava/lang/String;"
+"Landroid/graphics/Bitmap;"
+")"
+"Lorg/chromium/Foo$InnerClass;",
+ &g_TestJni_showConfirmInfoBar);
+
+ jobject ret =
+ env->CallObjectMethod(obj,
+ method_id, nativeInfoBar, buttonOk, buttonCancel, title, icon);
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+static base::subtle::AtomicWord g_TestJni_showAutoLoginInfoBar = 0;
+static ScopedJavaLocalRef<jobject> Java_TestJni_showAutoLoginInfoBar(JNIEnv*
+ env, jobject obj, jint nativeInfoBar,
+ jstring realm,
+ jstring account,
+ jstring args) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_TestJni_clazz,
+ "showAutoLoginInfoBar",
+
+"("
+"I"
+"Ljava/lang/String;"
+"Ljava/lang/String;"
+"Ljava/lang/String;"
+")"
+"Lorg/chromium/Foo$InnerClass;",
+ &g_TestJni_showAutoLoginInfoBar);
+
+ jobject ret =
+ env->CallObjectMethod(obj,
+ method_id, nativeInfoBar, realm, account, args);
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+static base::subtle::AtomicWord g_InfoBar_dismiss = 0;
+static void Java_InfoBar_dismiss(JNIEnv* env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InfoBar_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InfoBar_clazz,
+ "dismiss",
+
+"("
+")"
+"V",
+ &g_InfoBar_dismiss);
+
+ env->CallVoidMethod(obj,
+ method_id);
+ base::android::CheckException(env);
+
+}
+
+static base::subtle::AtomicWord g_TestJni_shouldShowAutoLogin = 0;
+static jboolean Java_TestJni_shouldShowAutoLogin(JNIEnv* env, jobject view,
+ jstring realm,
+ jstring account,
+ jstring args) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_STATIC>(
+ env, g_TestJni_clazz,
+ "shouldShowAutoLogin",
+
+"("
+"Landroid/view/View;"
+"Ljava/lang/String;"
+"Ljava/lang/String;"
+"Ljava/lang/String;"
+")"
+"Z",
+ &g_TestJni_shouldShowAutoLogin);
+
+ jboolean ret =
+ env->CallStaticBooleanMethod(g_TestJni_clazz,
+ method_id, view, realm, account, args);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_TestJni_openUrl = 0;
+static ScopedJavaLocalRef<jobject> Java_TestJni_openUrl(JNIEnv* env, jstring
+ url) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_STATIC>(
+ env, g_TestJni_clazz,
+ "openUrl",
+
+"("
+"Ljava/lang/String;"
+")"
+"Ljava/io/InputStream;",
+ &g_TestJni_openUrl);
+
+ jobject ret =
+ env->CallStaticObjectMethod(g_TestJni_clazz,
+ method_id, url);
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+static base::subtle::AtomicWord g_TestJni_activateHardwareAcceleration = 0;
+static void Java_TestJni_activateHardwareAcceleration(JNIEnv* env, jobject obj,
+ jboolean activated,
+ jint iPid,
+ jint iType,
+ jint iPrimaryID,
+ jint iSecondaryID) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_TestJni_clazz,
+ "activateHardwareAcceleration",
+
+"("
+"Z"
+"I"
+"I"
+"I"
+"I"
+")"
+"V",
+ &g_TestJni_activateHardwareAcceleration);
+
+ env->CallVoidMethod(obj,
+ method_id, activated, iPid, iType, iPrimaryID, iSecondaryID);
+ base::android::CheckException(env);
+
+}
+
+static base::subtle::AtomicWord g_TestJni_uncheckedCall = 0;
+static void Java_TestJni_uncheckedCall(JNIEnv* env, jobject obj, jint iParam) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_TestJni_clazz,
+ "uncheckedCall",
+
+"("
+"I"
+")"
+"V",
+ &g_TestJni_uncheckedCall);
+
+ env->CallVoidMethod(obj,
+ method_id, iParam);
+
+}
+
+static base::subtle::AtomicWord g_TestJni_returnByteArray = 0;
+static ScopedJavaLocalRef<jbyteArray> Java_TestJni_returnByteArray(JNIEnv* env,
+ jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_TestJni_clazz,
+ "returnByteArray",
+
+"("
+")"
+"[B",
+ &g_TestJni_returnByteArray);
+
+ jbyteArray ret =
+ static_cast<jbyteArray>(env->CallObjectMethod(obj,
+ method_id));
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jbyteArray>(env, ret);
+}
+
+static base::subtle::AtomicWord g_TestJni_returnBooleanArray = 0;
+static ScopedJavaLocalRef<jbooleanArray> Java_TestJni_returnBooleanArray(JNIEnv*
+ env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_TestJni_clazz,
+ "returnBooleanArray",
+
+"("
+")"
+"[Z",
+ &g_TestJni_returnBooleanArray);
+
+ jbooleanArray ret =
+ static_cast<jbooleanArray>(env->CallObjectMethod(obj,
+ method_id));
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jbooleanArray>(env, ret);
+}
+
+static base::subtle::AtomicWord g_TestJni_returnCharArray = 0;
+static ScopedJavaLocalRef<jcharArray> Java_TestJni_returnCharArray(JNIEnv* env,
+ jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_TestJni_clazz,
+ "returnCharArray",
+
+"("
+")"
+"[C",
+ &g_TestJni_returnCharArray);
+
+ jcharArray ret =
+ static_cast<jcharArray>(env->CallObjectMethod(obj,
+ method_id));
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jcharArray>(env, ret);
+}
+
+static base::subtle::AtomicWord g_TestJni_returnShortArray = 0;
+static ScopedJavaLocalRef<jshortArray> Java_TestJni_returnShortArray(JNIEnv*
+ env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_TestJni_clazz,
+ "returnShortArray",
+
+"("
+")"
+"[S",
+ &g_TestJni_returnShortArray);
+
+ jshortArray ret =
+ static_cast<jshortArray>(env->CallObjectMethod(obj,
+ method_id));
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jshortArray>(env, ret);
+}
+
+static base::subtle::AtomicWord g_TestJni_returnIntArray = 0;
+static ScopedJavaLocalRef<jintArray> Java_TestJni_returnIntArray(JNIEnv* env,
+ jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_TestJni_clazz,
+ "returnIntArray",
+
+"("
+")"
+"[I",
+ &g_TestJni_returnIntArray);
+
+ jintArray ret =
+ static_cast<jintArray>(env->CallObjectMethod(obj,
+ method_id));
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jintArray>(env, ret);
+}
+
+static base::subtle::AtomicWord g_TestJni_returnLongArray = 0;
+static ScopedJavaLocalRef<jlongArray> Java_TestJni_returnLongArray(JNIEnv* env,
+ jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_TestJni_clazz,
+ "returnLongArray",
+
+"("
+")"
+"[J",
+ &g_TestJni_returnLongArray);
+
+ jlongArray ret =
+ static_cast<jlongArray>(env->CallObjectMethod(obj,
+ method_id));
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jlongArray>(env, ret);
+}
+
+static base::subtle::AtomicWord g_TestJni_returnDoubleArray = 0;
+static ScopedJavaLocalRef<jdoubleArray> Java_TestJni_returnDoubleArray(JNIEnv*
+ env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_TestJni_clazz,
+ "returnDoubleArray",
+
+"("
+")"
+"[D",
+ &g_TestJni_returnDoubleArray);
+
+ jdoubleArray ret =
+ static_cast<jdoubleArray>(env->CallObjectMethod(obj,
+ method_id));
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jdoubleArray>(env, ret);
+}
+
+static base::subtle::AtomicWord g_TestJni_returnObjectArray = 0;
+static ScopedJavaLocalRef<jobjectArray> Java_TestJni_returnObjectArray(JNIEnv*
+ env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_TestJni_clazz,
+ "returnObjectArray",
+
+"("
+")"
+"[Ljava/lang/Object;",
+ &g_TestJni_returnObjectArray);
+
+ jobjectArray ret =
+ static_cast<jobjectArray>(env->CallObjectMethod(obj,
+ method_id));
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jobjectArray>(env, ret);
+}
+
+static base::subtle::AtomicWord g_TestJni_returnArrayOfByteArray = 0;
+static ScopedJavaLocalRef<jobjectArray>
+ Java_TestJni_returnArrayOfByteArray(JNIEnv* env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_TestJni_clazz,
+ "returnArrayOfByteArray",
+
+"("
+")"
+"[[B",
+ &g_TestJni_returnArrayOfByteArray);
+
+ jobjectArray ret =
+ static_cast<jobjectArray>(env->CallObjectMethod(obj,
+ method_id));
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jobjectArray>(env, ret);
+}
+
+static base::subtle::AtomicWord g_TestJni_getCompressFormat = 0;
+static ScopedJavaLocalRef<jobject> Java_TestJni_getCompressFormat(JNIEnv* env,
+ jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_TestJni_clazz,
+ "getCompressFormat",
+
+"("
+")"
+"Landroid/graphics/Bitmap$CompressFormat;",
+ &g_TestJni_getCompressFormat);
+
+ jobject ret =
+ env->CallObjectMethod(obj,
+ method_id);
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+static base::subtle::AtomicWord g_TestJni_getCompressFormatList = 0;
+static ScopedJavaLocalRef<jobject> Java_TestJni_getCompressFormatList(JNIEnv*
+ env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_TestJni_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_TestJni_clazz,
+ "getCompressFormatList",
+
+"("
+")"
+"Ljava/util/List;",
+ &g_TestJni_getCompressFormatList);
+
+ jobject ret =
+ env->CallObjectMethod(obj,
+ method_id);
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+// Step 3: RegisterNatives.
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+
+ g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetClass(env, kTestJniClassPath).obj()));
+ g_InfoBar_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetClass(env, kInfoBarClassPath).obj()));
+ return true;
+}
+
+#endif // org_chromium_TestJni_JNI
+"""
+ self.assertTextEquals(golden_content, h.GetContent())
+
+ def testCalledByNativeParseError(self):
+ try:
+ jni_generator.ExtractCalledByNatives("""
+@CalledByNative
+public static int foo(); // This one is fine
+
+@CalledByNative
+scooby doo
+""")
+ self.fail('Expected a ParseError')
+ except jni_generator.ParseError, e:
+ self.assertEquals(('@CalledByNative', 'scooby doo'), e.context_lines)
+
+ def testFullyQualifiedClassName(self):
+ contents = """
+// Copyright (c) 2010 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.content.browser;
+
+import org.chromium.base.BuildInfo;
+"""
+ self.assertEquals('org/chromium/content/browser/Foo',
+ jni_generator.ExtractFullyQualifiedJavaClassName(
+ 'org/chromium/content/browser/Foo.java', contents))
+ self.assertEquals('org/chromium/content/browser/Foo',
+ jni_generator.ExtractFullyQualifiedJavaClassName(
+ 'frameworks/Foo.java', contents))
+ self.assertRaises(SyntaxError,
+ jni_generator.ExtractFullyQualifiedJavaClassName,
+ 'com/foo/Bar', 'no PACKAGE line')
+
+ def testMethodNameMangling(self):
+ self.assertEquals('closeV',
+ jni_generator.GetMangledMethodName('close', [], 'void'))
+ self.assertEquals('readI_AB_I_I',
+ jni_generator.GetMangledMethodName('read',
+ [Param(name='p1',
+ datatype='byte[]'),
+ Param(name='p2',
+ datatype='int'),
+ Param(name='p3',
+ datatype='int'),],
+ 'int'))
+ self.assertEquals('openJIIS_JLS',
+ jni_generator.GetMangledMethodName('open',
+ [Param(name='p1',
+ datatype='java/lang/String'),],
+ 'java/io/InputStream'))
+
+ def testFromJavaP(self):
+ contents = """
+public abstract class java.io.InputStream extends java.lang.Object
+ implements java.io.Closeable{
+ public java.io.InputStream();
+ public int available() throws java.io.IOException;
+ public void close() throws java.io.IOException;
+ public void mark(int);
+ public boolean markSupported();
+ public abstract int read() throws java.io.IOException;
+ public int read(byte[]) throws java.io.IOException;
+ public int read(byte[], int, int) throws java.io.IOException;
+ public synchronized void reset() throws java.io.IOException;
+ public long skip(long) throws java.io.IOException;
+}
+"""
+ jni_from_javap = jni_generator.JNIFromJavaP(contents.split('\n'), None)
+ self.assertEquals(10, len(jni_from_javap.called_by_natives))
+ golden_content = """\
+// Copyright (c) 2012 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.
+
+// This file is autogenerated by
+// base/android/jni_generator/jni_generator_tests.py
+// For
+// java/io/InputStream
+
+#ifndef java_io_InputStream_JNI
+#define java_io_InputStream_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+using base::android::ScopedJavaLocalRef;
+
+// Step 1: forward declarations.
+namespace {
+const char kInputStreamClassPath[] = "java/io/InputStream";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_InputStream_clazz = NULL;
+} // namespace
+
+namespace JNI_InputStream {
+
+// Step 2: method stubs.
+
+static base::subtle::AtomicWord g_InputStream_available = 0;
+static jint Java_InputStream_available(JNIEnv* env, jobject obj) __attribute__
+ ((unused));
+static jint Java_InputStream_available(JNIEnv* env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "available",
+
+"("
+")"
+"I",
+ &g_InputStream_available);
+
+ jint ret =
+ env->CallIntMethod(obj,
+ method_id);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_InputStream_close = 0;
+static void Java_InputStream_close(JNIEnv* env, jobject obj) __attribute__
+ ((unused));
+static void Java_InputStream_close(JNIEnv* env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "close",
+
+"("
+")"
+"V",
+ &g_InputStream_close);
+
+ env->CallVoidMethod(obj,
+ method_id);
+ base::android::CheckException(env);
+
+}
+
+static base::subtle::AtomicWord g_InputStream_mark = 0;
+static void Java_InputStream_mark(JNIEnv* env, jobject obj, jint p0)
+ __attribute__ ((unused));
+static void Java_InputStream_mark(JNIEnv* env, jobject obj, jint p0) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "mark",
+
+"("
+"I"
+")"
+"V",
+ &g_InputStream_mark);
+
+ env->CallVoidMethod(obj,
+ method_id, p0);
+ base::android::CheckException(env);
+
+}
+
+static base::subtle::AtomicWord g_InputStream_markSupported = 0;
+static jboolean Java_InputStream_markSupported(JNIEnv* env, jobject obj)
+ __attribute__ ((unused));
+static jboolean Java_InputStream_markSupported(JNIEnv* env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "markSupported",
+
+"("
+")"
+"Z",
+ &g_InputStream_markSupported);
+
+ jboolean ret =
+ env->CallBooleanMethod(obj,
+ method_id);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_InputStream_readI = 0;
+static jint Java_InputStream_readI(JNIEnv* env, jobject obj) __attribute__
+ ((unused));
+static jint Java_InputStream_readI(JNIEnv* env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "read",
+
+"("
+")"
+"I",
+ &g_InputStream_readI);
+
+ jint ret =
+ env->CallIntMethod(obj,
+ method_id);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_InputStream_readI_AB = 0;
+static jint Java_InputStream_readI_AB(JNIEnv* env, jobject obj, jbyteArray p0)
+ __attribute__ ((unused));
+static jint Java_InputStream_readI_AB(JNIEnv* env, jobject obj, jbyteArray p0) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "read",
+
+"("
+"[B"
+")"
+"I",
+ &g_InputStream_readI_AB);
+
+ jint ret =
+ env->CallIntMethod(obj,
+ method_id, p0);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_InputStream_readI_AB_I_I = 0;
+static jint Java_InputStream_readI_AB_I_I(JNIEnv* env, jobject obj, jbyteArray
+ p0,
+ jint p1,
+ jint p2) __attribute__ ((unused));
+static jint Java_InputStream_readI_AB_I_I(JNIEnv* env, jobject obj, jbyteArray
+ p0,
+ jint p1,
+ jint p2) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "read",
+
+"("
+"[B"
+"I"
+"I"
+")"
+"I",
+ &g_InputStream_readI_AB_I_I);
+
+ jint ret =
+ env->CallIntMethod(obj,
+ method_id, p0, p1, p2);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_InputStream_reset = 0;
+static void Java_InputStream_reset(JNIEnv* env, jobject obj) __attribute__
+ ((unused));
+static void Java_InputStream_reset(JNIEnv* env, jobject obj) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "reset",
+
+"("
+")"
+"V",
+ &g_InputStream_reset);
+
+ env->CallVoidMethod(obj,
+ method_id);
+ base::android::CheckException(env);
+
+}
+
+static base::subtle::AtomicWord g_InputStream_skip = 0;
+static jlong Java_InputStream_skip(JNIEnv* env, jobject obj, jlong p0)
+ __attribute__ ((unused));
+static jlong Java_InputStream_skip(JNIEnv* env, jobject obj, jlong p0) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "skip",
+
+"("
+"J"
+")"
+"J",
+ &g_InputStream_skip);
+
+ jlong ret =
+ env->CallLongMethod(obj,
+ method_id, p0);
+ base::android::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_InputStream_Constructor = 0;
+static ScopedJavaLocalRef<jobject> Java_InputStream_Constructor(JNIEnv* env)
+ __attribute__ ((unused));
+static ScopedJavaLocalRef<jobject> Java_InputStream_Constructor(JNIEnv* env) {
+ /* Must call RegisterNativesImpl() */
+ DCHECK(g_InputStream_clazz);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, g_InputStream_clazz,
+ "<init>",
+
+"("
+")"
+"V",
+ &g_InputStream_Constructor);
+
+ jobject ret =
+ env->NewObject(g_InputStream_clazz,
+ method_id);
+ base::android::CheckException(env);
+ return ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+// Step 3: RegisterNatives.
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+
+ g_InputStream_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetClass(env, kInputStreamClassPath).obj()));
+ return true;
+}
+} // namespace JNI_InputStream
+
+#endif // java_io_InputStream_JNI
+"""
+ self.assertTextEquals(golden_content, jni_from_javap.GetContent())
+
+ def testREForNatives(self):
+ # We should not match "native SyncSetupFlow" inside the comment.
+ test_data = """
+ /**
+ * Invoked when the setup process is complete so we can disconnect from the
+ * native-side SyncSetupFlowHandler.
+ */
+ public void destroy() {
+ Log.v(TAG, "Destroying native SyncSetupFlow");
+ if (mNativeSyncSetupFlow != 0) {
+ nativeSyncSetupEnded(mNativeSyncSetupFlow);
+ mNativeSyncSetupFlow = 0;
+ }
+ }
+ private native void nativeSyncSetupEnded(
+ int nativeAndroidSyncSetupFlowHandler);
+ """
+ jni_from_java = jni_generator.JNIFromJavaSource(test_data, 'foo/bar')
+
+ def testRaisesOnNonJNIMethod(self):
+ test_data = """
+ class MyInnerClass {
+ private int Foo(int p0) {
+ }
+ }
+ """
+ self.assertRaises(SyntaxError,
+ jni_generator.JNIFromJavaSource,
+ test_data, 'foo/bar')
+
+ def testJniSelfDocumentingExample(self):
+ script_dir = os.path.dirname(sys.argv[0])
+ content = file(os.path.join(script_dir,
+ 'java/src/org/chromium/example/jni_generator/SampleForTests.java')
+ ).read()
+ golden_content = file(os.path.join(script_dir,
+ 'golden_sample_for_tests_jni.h')).read()
+ jni_from_java = jni_generator.JNIFromJavaSource(
+ content, 'org/chromium/example/jni_generator/SampleForTests')
+ self.assertTextEquals(golden_content, jni_from_java.GetContent())
+
+ def testNoWrappingPreprocessorLines(self):
+ test_data = """
+ package com.google.lookhowextremelylongiam.snarf.icankeepthisupallday;
+
+ class ReallyLongClassNamesAreAllTheRage {
+ private static native int nativeTest();
+ }
+ """
+ jni_from_java = jni_generator.JNIFromJavaSource(
+ test_data, ('com/google/lookhowextremelylongiam/snarf/'
+ 'icankeepthisupallday/ReallyLongClassNamesAreAllTheRage'))
+ jni_lines = jni_from_java.GetContent().split('\n')
+ line = filter(lambda line: line.lstrip().startswith('#ifndef'),
+ jni_lines)[0]
+ self.assertTrue(len(line) > 80,
+ ('Expected #ifndef line to be > 80 chars: ', line))
+
+ def testJarJarRemapping(self):
+ test_data = """
+ package org.chromium.example.jni_generator;
+
+ import org.chromium.example2.Test;
+
+ class Example {
+ private static native void nativeTest(Test t);
+ }
+ """
+ jni_generator.JniParams.SetJarJarMappings(
+ """rule org.chromium.example.** com.test.@1
+ rule org.chromium.example2.** org.test2.@0""")
+ jni_from_java = jni_generator.JNIFromJavaSource(
+ test_data, 'org/chromium/example/jni_generator/Example')
+ jni_generator.JniParams.SetJarJarMappings('')
+ golden_content = """\
+// Copyright (c) 2012 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.
+
+// This file is autogenerated by
+// base/android/jni_generator/jni_generator_tests.py
+// For
+// org/chromium/example/jni_generator/Example
+
+#ifndef org_chromium_example_jni_generator_Example_JNI
+#define org_chromium_example_jni_generator_Example_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+using base::android::ScopedJavaLocalRef;
+
+// Step 1: forward declarations.
+namespace {
+const char kExampleClassPath[] = "com/test/jni_generator/Example";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+jclass g_Example_clazz = NULL;
+} // namespace
+
+static void Test(JNIEnv* env, jclass clazz,
+ jobject t);
+
+// Step 2: method stubs.
+
+// Step 3: RegisterNatives.
+
+static bool RegisterNativesImpl(JNIEnv* env) {
+
+ g_Example_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
+ base::android::GetClass(env, kExampleClassPath).obj()));
+ static const JNINativeMethod kMethodsExample[] = {
+ { "nativeTest",
+"("
+"Lorg/test2/org/chromium/example2/Test;"
+")"
+"V", reinterpret_cast<void*>(Test) },
+ };
+ const int kMethodsExampleSize = arraysize(kMethodsExample);
+
+ if (env->RegisterNatives(g_Example_clazz,
+ kMethodsExample,
+ kMethodsExampleSize) < 0) {
+ LOG(ERROR) << "RegisterNatives failed in " << __FILE__;
+ return false;
+ }
+
+ return true;
+}
+
+#endif // org_chromium_example_jni_generator_Example_JNI
+"""
+ self.assertTextEquals(golden_content, jni_from_java.GetContent())
+
+ def testImports(self):
+ import_header = """
+// Copyright (c) 2012 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.content.app;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.SurfaceTexture;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Surface;
+
+import java.util.ArrayList;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+import org.chromium.content.app.ContentMain;
+import org.chromium.content.browser.SandboxedProcessConnection;
+import org.chromium.content.common.ISandboxedProcessCallback;
+import org.chromium.content.common.ISandboxedProcessService;
+import org.chromium.content.common.WillNotRaise.AnException;
+import org.chromium.content.common.WillRaise.AnException;
+
+import static org.chromium.Bar.Zoo;
+
+class Foo {
+ public static class BookmarkNode implements Parcelable {
+ }
+ public interface PasswordListObserver {
+ }
+}
+ """
+ jni_generator.JniParams.SetFullyQualifiedClass(
+ 'org/chromium/content/app/Foo')
+ jni_generator.JniParams.ExtractImportsAndInnerClasses(import_header)
+ self.assertTrue('Lorg/chromium/content/common/ISandboxedProcessService' in
+ jni_generator.JniParams._imports)
+ self.assertTrue('Lorg/chromium/Bar/Zoo' in
+ jni_generator.JniParams._imports)
+ self.assertTrue('Lorg/chromium/content/app/Foo$BookmarkNode' in
+ jni_generator.JniParams._inner_classes)
+ self.assertTrue('Lorg/chromium/content/app/Foo$PasswordListObserver' in
+ jni_generator.JniParams._inner_classes)
+ self.assertEquals('Lorg/chromium/content/app/ContentMain$Inner;',
+ jni_generator.JniParams.JavaToJni('ContentMain.Inner'))
+ self.assertRaises(SyntaxError,
+ jni_generator.JniParams.JavaToJni,
+ 'AnException')
+
+ def testJniParamsJavaToJni(self):
+ self.assertTextEquals('I', JniParams.JavaToJni('int'))
+ self.assertTextEquals('[B', JniParams.JavaToJni('byte[]'))
+ self.assertTextEquals(
+ '[Ljava/nio/ByteBuffer;', JniParams.JavaToJni('java/nio/ByteBuffer[]'))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/base/android/jni_generator/sample_for_tests.cc b/chromium/base/android/jni_generator/sample_for_tests.cc
new file mode 100644
index 00000000000..30b99406350
--- /dev/null
+++ b/chromium/base/android/jni_generator/sample_for_tests.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 2012 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/jni_generator/sample_for_tests.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "jni/SampleForTests_jni.h"
+
+
+using base::android::AttachCurrentThread;
+using base::android::ScopedJavaLocalRef;
+
+namespace base {
+namespace android {
+
+jdouble CPPClass::InnerClass::MethodOtherP0(JNIEnv* env, jobject obj) {
+ return 0.0;
+}
+
+CPPClass::CPPClass() {
+}
+
+CPPClass::~CPPClass() {
+}
+
+void CPPClass::Destroy(JNIEnv* env, jobject obj) {
+ delete this;
+}
+
+jint CPPClass::Method(JNIEnv* env, jobject obj) {
+ return 0;
+}
+
+void CPPClass::AddStructB(JNIEnv* env, jobject obj, jobject structb) {
+ long key = Java_InnerStructB_getKey(env, structb);
+ std::string value = ConvertJavaStringToUTF8(
+ env, Java_InnerStructB_getValue(env, structb).obj());
+ map_[key] = value;
+}
+
+void CPPClass::IterateAndDoSomethingWithStructB(JNIEnv* env, jobject obj) {
+ // Iterate over the elements and do something with them.
+ for (std::map<long, std::string>::const_iterator it = map_.begin();
+ it != map_.end(); ++it) {
+ long key = it->first;
+ std::string value = it->second;
+ }
+ map_.clear();
+}
+
+// Static free functions declared and called directly from java.
+static jint Init(JNIEnv* env, jobject obj, jstring param) {
+ return 0;
+}
+
+static jdouble GetDoubleFunction(JNIEnv*, jobject) {
+ return 0;
+}
+
+static jfloat GetFloatFunction(JNIEnv*, jclass) {
+ return 0;
+}
+
+static void SetNonPODDatatype(JNIEnv*, jobject, jobject) {
+}
+
+static jobject GetNonPODDatatype(JNIEnv*, jobject) {
+ return NULL;
+}
+
+static jint InnerFunction(JNIEnv*, jclass) {
+ return 0;
+}
+
+} // namespace android
+} // namespace base
+
+int main() {
+ // On a regular application, you'd call AttachCurrentThread(). This sample is
+ // not yet linking with all the libraries.
+ JNIEnv* env = /* AttachCurrentThread() */ NULL;
+
+ // This is how you call a java static method from C++.
+ bool foo = base::android::Java_SampleForTests_staticJavaMethod(env);
+
+ // This is how you call a java method from C++. Note that you must have
+ // obtained the jobject somehow.
+ jobject my_java_object = NULL;
+ int bar = base::android::Java_SampleForTests_javaMethod(
+ env, my_java_object, 1, 2);
+
+ for (int i = 0; i < 10; ++i) {
+ // Creates a "struct" that will then be used by the java side.
+ ScopedJavaLocalRef<jobject> struct_a =
+ base::android::Java_InnerStructA_create(
+ env, 0, 1,
+ base::android::ConvertUTF8ToJavaString(env, "test").obj());
+ base::android::Java_SampleForTests_addStructA(
+ env, my_java_object, struct_a.obj());
+ }
+ base::android::Java_SampleForTests_iterateAndDoSomething(env, my_java_object);
+ return 0;
+}
diff --git a/chromium/base/android/jni_generator/sample_for_tests.h b/chromium/base/android/jni_generator/sample_for_tests.h
new file mode 100644
index 00000000000..278008b2df7
--- /dev/null
+++ b/chromium/base/android/jni_generator/sample_for_tests.h
@@ -0,0 +1,46 @@
+// Copyright (c) 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 <jni.h>
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace base {
+namespace android {
+
+// This file is used to:
+// - document the best practices and guidelines on JNI usage.
+// - ensure sample_for_tests_jni.h compiles and the functions declared in it
+// as expected.
+//
+// All methods are called directly from Java. See more documentation in
+// SampleForTests.java.
+class CPPClass {
+ public:
+ CPPClass();
+ ~CPPClass();
+
+ class InnerClass {
+ public:
+ jdouble MethodOtherP0(JNIEnv* env, jobject obj);
+ };
+
+ void Destroy(JNIEnv* env, jobject obj);
+
+ jint Method(JNIEnv* env, jobject obj);
+
+ void AddStructB(JNIEnv* env, jobject obj, jobject structb);
+
+ void IterateAndDoSomethingWithStructB(JNIEnv* env, jobject obj);
+
+ private:
+ std::map<long, std::string> map_;
+
+ DISALLOW_COPY_AND_ASSIGN(CPPClass);
+};
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/jni_helper.cc b/chromium/base/android/jni_helper.cc
new file mode 100644
index 00000000000..fcb610e1884
--- /dev/null
+++ b/chromium/base/android/jni_helper.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 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/jni_helper.h"
+
+#include "base/android/jni_android.h"
+#include "base/logging.h"
+
+using base::android::AttachCurrentThread;
+
+JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef()
+ : obj_(NULL) {
+}
+
+JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef(
+ const JavaObjectWeakGlobalRef& orig)
+ : obj_(NULL) {
+ Assign(orig);
+}
+
+JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef(JNIEnv* env, jobject obj)
+ : obj_(env->NewWeakGlobalRef(obj)) {
+ DCHECK(obj_);
+}
+
+JavaObjectWeakGlobalRef::~JavaObjectWeakGlobalRef() {
+ reset();
+}
+
+void JavaObjectWeakGlobalRef::operator=(const JavaObjectWeakGlobalRef& rhs) {
+ Assign(rhs);
+}
+
+void JavaObjectWeakGlobalRef::reset() {
+ if (obj_) {
+ AttachCurrentThread()->DeleteWeakGlobalRef(obj_);
+ obj_ = NULL;
+ }
+}
+
+base::android::ScopedJavaLocalRef<jobject>
+ JavaObjectWeakGlobalRef::get(JNIEnv* env) const {
+ return GetRealObject(env, obj_);
+}
+
+base::android::ScopedJavaLocalRef<jobject> GetRealObject(
+ JNIEnv* env, jweak obj) {
+ jobject real = NULL;
+ if (obj) {
+ real = env->NewLocalRef(obj);
+ if (!real)
+ DLOG(ERROR) << "The real object has been deleted!";
+ }
+ return base::android::ScopedJavaLocalRef<jobject>(env, real);
+}
+
+void JavaObjectWeakGlobalRef::Assign(const JavaObjectWeakGlobalRef& other) {
+ if (&other == this)
+ return;
+
+ JNIEnv* env = AttachCurrentThread();
+ if (obj_)
+ env->DeleteWeakGlobalRef(obj_);
+
+ obj_ = other.obj_ ? env->NewWeakGlobalRef(other.obj_) : NULL;
+}
diff --git a/chromium/base/android/jni_helper.h b/chromium/base/android/jni_helper.h
new file mode 100644
index 00000000000..895bf95a9d1
--- /dev/null
+++ b/chromium/base/android/jni_helper.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 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_ANDROID_JNI_HELPER_H_
+#define BASE_ANDROID_JNI_HELPER_H_
+
+#include <jni.h>
+
+#include "base/base_export.h"
+#include "base/android/scoped_java_ref.h"
+
+// Manages WeakGlobalRef lifecycle.
+// This class is not thread-safe w.r.t. get() and reset(). Multiple threads may
+// safely use get() concurrently, but if the user calls reset() (or of course,
+// calls the destructor) they'll need to provide their own synchronization.
+class BASE_EXPORT JavaObjectWeakGlobalRef {
+ public:
+ JavaObjectWeakGlobalRef();
+ JavaObjectWeakGlobalRef(const JavaObjectWeakGlobalRef& orig);
+ JavaObjectWeakGlobalRef(JNIEnv* env, jobject obj);
+ virtual ~JavaObjectWeakGlobalRef();
+
+ void operator=(const JavaObjectWeakGlobalRef& rhs);
+
+ base::android::ScopedJavaLocalRef<jobject> get(JNIEnv* env) const;
+
+ void reset();
+
+ private:
+ void Assign(const JavaObjectWeakGlobalRef& rhs);
+
+ jweak obj_;
+};
+
+// Get the real object stored in the weak reference returned as a
+// ScopedJavaLocalRef.
+BASE_EXPORT base::android::ScopedJavaLocalRef<jobject> GetRealObject(
+ JNIEnv* env, jweak obj);
+
+#endif // BASE_ANDROID_JNI_HELPER_H_
diff --git a/chromium/base/android/jni_registrar.cc b/chromium/base/android/jni_registrar.cc
new file mode 100644
index 00000000000..0f32089d18c
--- /dev/null
+++ b/chromium/base/android/jni_registrar.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 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/jni_registrar.h"
+
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "base/android/jni_android.h"
+
+namespace base {
+namespace android {
+
+bool RegisterNativeMethods(JNIEnv* env,
+ const RegistrationMethod* method,
+ size_t count) {
+ TRACE_EVENT0("startup", "base_android::RegisterNativeMethods")
+ const RegistrationMethod* end = method + count;
+ while (method != end) {
+ if (!method->func(env)) {
+ DLOG(ERROR) << method->name << " failed registration!";
+ return false;
+ }
+ method++;
+ }
+ return true;
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/jni_registrar.h b/chromium/base/android/jni_registrar.h
new file mode 100644
index 00000000000..849d07f9392
--- /dev/null
+++ b/chromium/base/android/jni_registrar.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2012 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_ANDROID_JNI_REGISTRAR_H_
+#define BASE_ANDROID_JNI_REGISTRAR_H_
+
+#include <jni.h>
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+namespace android {
+
+struct RegistrationMethod;
+
+// Registers the JNI bindings for the specified |method| definition containing
+// |count| elements. Returns whether the registration of the given methods
+// succeeded.
+BASE_EXPORT bool RegisterNativeMethods(JNIEnv* env,
+ const RegistrationMethod* method,
+ size_t count);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_JNI_REGISTRAR_H_
diff --git a/chromium/base/android/jni_string.cc b/chromium/base/android/jni_string.cc
new file mode 100644
index 00000000000..d25fed822bc
--- /dev/null
+++ b/chromium/base/android/jni_string.cc
@@ -0,0 +1,99 @@
+// Copyright (c) 2012 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/jni_string.h"
+
+#include "base/android/jni_android.h"
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace {
+
+// Internal version that does not use a scoped local pointer.
+jstring ConvertUTF16ToJavaStringImpl(JNIEnv* env,
+ const base::StringPiece16& str) {
+ jstring result = env->NewString(str.data(), str.length());
+ base::android::CheckException(env);
+ return result;
+}
+
+}
+
+namespace base {
+namespace android {
+
+void ConvertJavaStringToUTF8(JNIEnv* env, jstring str, std::string* result) {
+ if (!str) {
+ LOG(WARNING) << "ConvertJavaStringToUTF8 called with null string.";
+ result->clear();
+ return;
+ }
+ // JNI's GetStringUTFChars() returns strings in Java "modified" UTF8, so
+ // instead get the String in UTF16 and convert using chromium's conversion
+ // function that yields plain (non Java-modified) UTF8.
+ const jchar* chars = env->GetStringChars(str, NULL);
+ DCHECK(chars);
+ UTF16ToUTF8(chars, env->GetStringLength(str), result);
+ env->ReleaseStringChars(str, chars);
+ CheckException(env);
+}
+
+std::string ConvertJavaStringToUTF8(JNIEnv* env, jstring str) {
+ std::string result;
+ ConvertJavaStringToUTF8(env, str, &result);
+ return result;
+}
+
+std::string ConvertJavaStringToUTF8(const JavaRef<jstring>& str) {
+ return ConvertJavaStringToUTF8(AttachCurrentThread(), str.obj());
+}
+
+ScopedJavaLocalRef<jstring> ConvertUTF8ToJavaString(
+ JNIEnv* env,
+ const base::StringPiece& str) {
+ // JNI's NewStringUTF expects "modified" UTF8 so instead create the string
+ // via our own UTF16 conversion utility.
+ // Further, Dalvik requires the string passed into NewStringUTF() to come from
+ // a trusted source. We can't guarantee that all UTF8 will be sanitized before
+ // it gets here, so constructing via UTF16 side-steps this issue.
+ // (Dalvik stores strings internally as UTF16 anyway, so there shouldn't be
+ // a significant performance hit by doing it this way).
+ return ScopedJavaLocalRef<jstring>(env, ConvertUTF16ToJavaStringImpl(
+ env, UTF8ToUTF16(str)));
+}
+
+void ConvertJavaStringToUTF16(JNIEnv* env, jstring str, string16* result) {
+ if (!str) {
+ LOG(WARNING) << "ConvertJavaStringToUTF16 called with null string.";
+ result->clear();
+ return;
+ }
+ const jchar* chars = env->GetStringChars(str, NULL);
+ DCHECK(chars);
+ // GetStringChars isn't required to NULL-terminate the strings
+ // it returns, so the length must be explicitly checked.
+ result->assign(chars, env->GetStringLength(str));
+ env->ReleaseStringChars(str, chars);
+ CheckException(env);
+}
+
+string16 ConvertJavaStringToUTF16(JNIEnv* env, jstring str) {
+ string16 result;
+ ConvertJavaStringToUTF16(env, str, &result);
+ return result;
+}
+
+string16 ConvertJavaStringToUTF16(const JavaRef<jstring>& str) {
+ return ConvertJavaStringToUTF16(AttachCurrentThread(), str.obj());
+}
+
+ScopedJavaLocalRef<jstring> ConvertUTF16ToJavaString(
+ JNIEnv* env,
+ const base::StringPiece16& str) {
+ return ScopedJavaLocalRef<jstring>(env,
+ ConvertUTF16ToJavaStringImpl(env, str));
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/jni_string.h b/chromium/base/android/jni_string.h
new file mode 100644
index 00000000000..89af5b0a897
--- /dev/null
+++ b/chromium/base/android/jni_string.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 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_ANDROID_JNI_STRING_H_
+#define BASE_ANDROID_JNI_STRING_H_
+
+#include <jni.h>
+#include <string>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/base_export.h"
+#include "base/strings/string_piece.h"
+
+namespace base {
+namespace android {
+
+// Convert a Java string to UTF8. Returns a std string.
+BASE_EXPORT void ConvertJavaStringToUTF8(JNIEnv* env,
+ jstring str,
+ std::string* result);
+BASE_EXPORT std::string ConvertJavaStringToUTF8(JNIEnv* env, jstring str);
+BASE_EXPORT std::string ConvertJavaStringToUTF8(const JavaRef<jstring>& str);
+
+// Convert a std string to Java string.
+BASE_EXPORT ScopedJavaLocalRef<jstring> ConvertUTF8ToJavaString(
+ JNIEnv* env,
+ const base::StringPiece& str);
+
+// Convert a Java string to UTF16. Returns a string16.
+BASE_EXPORT void ConvertJavaStringToUTF16(JNIEnv* env,
+ jstring str,
+ string16* result);
+BASE_EXPORT string16 ConvertJavaStringToUTF16(JNIEnv* env, jstring str);
+BASE_EXPORT string16 ConvertJavaStringToUTF16(const JavaRef<jstring>& str);
+
+// Convert a string16 to a Java string.
+BASE_EXPORT ScopedJavaLocalRef<jstring> ConvertUTF16ToJavaString(
+ JNIEnv* env,
+ const base::StringPiece16& str);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_JNI_STRING_H_
diff --git a/chromium/base/android/jni_string_unittest.cc b/chromium/base/android/jni_string_unittest.cc
new file mode 100644
index 00000000000..abd0683170e
--- /dev/null
+++ b/chromium/base/android/jni_string_unittest.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 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/jni_string.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace android {
+
+TEST(JniString, BasicConversionsUTF8) {
+ const std::string kSimpleString = "SimpleTest8";
+ JNIEnv* env = AttachCurrentThread();
+ std::string result =
+ ConvertJavaStringToUTF8(ConvertUTF8ToJavaString(env, kSimpleString));
+ EXPECT_EQ(kSimpleString, result);
+}
+
+TEST(JniString, BasicConversionsUTF16) {
+ const string16 kSimpleString = UTF8ToUTF16("SimpleTest16");
+ JNIEnv* env = AttachCurrentThread();
+ string16 result =
+ ConvertJavaStringToUTF16(ConvertUTF16ToJavaString(env, kSimpleString));
+ EXPECT_EQ(kSimpleString, result);
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/memory_pressure_listener_android.cc b/chromium/base/android/memory_pressure_listener_android.cc
new file mode 100644
index 00000000000..80c07bcf9c1
--- /dev/null
+++ b/chromium/base/android/memory_pressure_listener_android.cc
@@ -0,0 +1,31 @@
+// 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/memory_pressure_listener_android.h"
+
+#include "base/memory/memory_pressure_listener.h"
+#include "jni/MemoryPressureListener_jni.h"
+
+// Defined and called by JNI.
+static void OnMemoryPressure(
+ JNIEnv* env, jclass clazz, jint memory_pressure_level) {
+ base::MemoryPressureListener::NotifyMemoryPressure(
+ static_cast<base::MemoryPressureListener::MemoryPressureLevel>(
+ memory_pressure_level));
+}
+
+namespace base {
+namespace android {
+
+bool MemoryPressureListenerAndroid::Register(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+void MemoryPressureListenerAndroid::RegisterSystemCallback(JNIEnv* env) {
+ Java_MemoryPressureListener_registerSystemCallback(
+ env, GetApplicationContext());
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/memory_pressure_listener_android.h b/chromium/base/android/memory_pressure_listener_android.h
new file mode 100644
index 00000000000..eed8dbb5756
--- /dev/null
+++ b/chromium/base/android/memory_pressure_listener_android.h
@@ -0,0 +1,30 @@
+// 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_ANDROID_MEMORY_PRESSURE_LISTENER_ANDROID_H_
+#define BASE_ANDROID_MEMORY_PRESSURE_LISTENER_ANDROID_H_
+
+#include "base/android/jni_android.h"
+
+namespace base {
+namespace android {
+
+// Implements the C++ counter part of MemoryPressureListener.java
+class BASE_EXPORT MemoryPressureListenerAndroid {
+ public:
+ static bool Register(JNIEnv* env);
+
+ static void RegisterSystemCallback(JNIEnv* env);
+
+ // Called by JNI.
+ static void OnMemoryPressure(int memory_pressure_type);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MemoryPressureListenerAndroid);
+};
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_MEMORY_PRESSURE_LISTENER_ANDROID_H_
diff --git a/chromium/base/android/path_service_android.cc b/chromium/base/android/path_service_android.cc
new file mode 100644
index 00000000000..18ca70c8abd
--- /dev/null
+++ b/chromium/base/android/path_service_android.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 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/path_service_android.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "jni/PathService_jni.h"
+
+namespace base {
+namespace android {
+
+void Override(JNIEnv* env, jclass clazz, jint what, jstring path) {
+ FilePath file_path(ConvertJavaStringToUTF8(env, path));
+ PathService::Override(what, file_path);
+}
+
+bool RegisterPathService(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/path_service_android.h b/chromium/base/android/path_service_android.h
new file mode 100644
index 00000000000..26040f97beb
--- /dev/null
+++ b/chromium/base/android/path_service_android.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2012 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_ANDROID_PATH_SERVICE_ANDROID_H_
+#define BASE_ANDROID_PATH_SERVICE_ANDROID_H_
+
+#include <jni.h>
+
+namespace base {
+namespace android {
+
+bool RegisterPathService(JNIEnv* env);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_PATH_SERVICE_ANDROID_H_
diff --git a/chromium/base/android/path_utils.cc b/chromium/base/android/path_utils.cc
new file mode 100644
index 00000000000..5737227f38f
--- /dev/null
+++ b/chromium/base/android/path_utils.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 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/path_utils.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/files/file_path.h"
+
+#include "jni/PathUtils_jni.h"
+
+namespace base {
+namespace android {
+
+bool GetDataDirectory(FilePath* result) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> path =
+ Java_PathUtils_getDataDirectory(env, GetApplicationContext());
+ FilePath data_path(ConvertJavaStringToUTF8(path));
+ *result = data_path;
+ return true;
+}
+
+bool GetCacheDirectory(FilePath* result) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> path =
+ Java_PathUtils_getCacheDirectory(env, GetApplicationContext());
+ FilePath cache_path(ConvertJavaStringToUTF8(path));
+ *result = cache_path;
+ return true;
+}
+
+bool GetDownloadsDirectory(FilePath* result) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> path =
+ Java_PathUtils_getDownloadsDirectory(env, GetApplicationContext());
+ FilePath downloads_path(ConvertJavaStringToUTF8(path));
+ *result = downloads_path;
+ return true;
+}
+
+bool GetNativeLibraryDirectory(FilePath* result) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> path =
+ Java_PathUtils_getNativeLibraryDirectory(env, GetApplicationContext());
+ FilePath library_path(ConvertJavaStringToUTF8(path));
+ *result = library_path;
+ return true;
+}
+
+bool GetExternalStorageDirectory(FilePath* result) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> path =
+ Java_PathUtils_getExternalStorageDirectory(env);
+ FilePath storage_path(ConvertJavaStringToUTF8(path));
+ *result = storage_path;
+ return true;
+}
+
+bool RegisterPathUtils(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/path_utils.h b/chromium/base/android/path_utils.h
new file mode 100644
index 00000000000..60f8a79b1e2
--- /dev/null
+++ b/chromium/base/android/path_utils.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2012 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_ANDROID_PATH_UTILS_H_
+#define BASE_ANDROID_PATH_UTILS_H_
+
+#include <jni.h>
+
+#include "base/base_export.h"
+
+namespace base {
+
+class FilePath;
+
+namespace android {
+
+// Retrieves the absolute path to the data directory of the current
+// application. The result is placed in the FilePath pointed to by 'result'.
+// This method is dedicated for base_paths_android.c, Using
+// PathService::Get(base::DIR_ANDROID_APP_DATA, ...) gets the data dir.
+BASE_EXPORT bool GetDataDirectory(FilePath* result);
+
+// Retrieves the absolute path to the cache directory. The result is placed in
+// the FilePath pointed to by 'result'. This method is dedicated for
+// base_paths_android.c, Using PathService::Get(base::DIR_CACHE, ...) gets the
+// cache dir.
+BASE_EXPORT bool GetCacheDirectory(FilePath* result);
+
+// Retrieves the path to the public downloads directory. The result is placed
+// in the FilePath pointed to by 'result'.
+BASE_EXPORT bool GetDownloadsDirectory(FilePath* result);
+
+// Retrieves the path to the native JNI libraries via
+// ApplicationInfo.nativeLibraryDir on the Java side. The result is placed in
+// the FilePath pointed to by 'result'.
+BASE_EXPORT bool GetNativeLibraryDirectory(FilePath* result);
+
+// Retrieves the absolute path to the external storage directory. The result
+// is placed in the FilePath pointed to by 'result'.
+BASE_EXPORT bool GetExternalStorageDirectory(FilePath* result);
+
+bool RegisterPathUtils(JNIEnv* env);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_PATH_UTILS_H_
diff --git a/chromium/base/android/path_utils_unittest.cc b/chromium/base/android/path_utils_unittest.cc
new file mode 100644
index 00000000000..c4c12fea133
--- /dev/null
+++ b/chromium/base/android/path_utils_unittest.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2012 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/path_utils.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace android {
+
+typedef testing::Test PathUtilsTest;
+
+TEST_F(PathUtilsTest, TestGetDataDirectory) {
+ // The string comes from the Java side and depends on the APK
+ // we are running in. Assumes that we are packaged in
+ // org.chromium.native_test
+ FilePath path;
+ GetDataDirectory(&path);
+ EXPECT_STREQ("/data/data/org.chromium.native_test/app_chrome",
+ path.value().c_str());
+}
+
+TEST_F(PathUtilsTest, TestGetCacheDirectory) {
+ // The string comes from the Java side and depends on the APK
+ // we are running in. Assumes that we are packaged in
+ // org.chromium.native_test
+ FilePath path;
+ GetCacheDirectory(&path);
+ EXPECT_STREQ("/data/data/org.chromium.native_test/cache",
+ path.value().c_str());
+}
+
+TEST_F(PathUtilsTest, TestGetNativeLibraryDirectory) {
+ // The string comes from the Java side and depends on the APK
+ // we are running in. Assumes that the directory contains
+ // the base tests shared object.
+ FilePath path;
+ GetNativeLibraryDirectory(&path);
+ EXPECT_TRUE(base::PathExists(path.Append(("libbase_unittests.so"))));
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/scoped_java_ref.cc b/chromium/base/android/scoped_java_ref.cc
new file mode 100644
index 00000000000..21b466e9584
--- /dev/null
+++ b/chromium/base/android/scoped_java_ref.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 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/scoped_java_ref.h"
+
+#include "base/android/jni_android.h"
+#include "base/logging.h"
+
+namespace base {
+namespace android {
+
+JavaRef<jobject>::JavaRef() : obj_(NULL) {}
+
+JavaRef<jobject>::JavaRef(JNIEnv* env, jobject obj) : obj_(obj) {
+ if (obj) {
+ DCHECK(env && env->GetObjectRefType(obj) == JNILocalRefType);
+ }
+}
+
+JavaRef<jobject>::~JavaRef() {
+}
+
+JNIEnv* JavaRef<jobject>::SetNewLocalRef(JNIEnv* env, jobject obj) {
+ if (!env) {
+ env = AttachCurrentThread();
+ } else {
+ DCHECK_EQ(env, AttachCurrentThread()); // Is |env| on correct thread.
+ }
+ if (obj)
+ obj = env->NewLocalRef(obj);
+ if (obj_)
+ env->DeleteLocalRef(obj_);
+ obj_ = obj;
+ return env;
+}
+
+void JavaRef<jobject>::SetNewGlobalRef(JNIEnv* env, jobject obj) {
+ if (!env) {
+ env = AttachCurrentThread();
+ } else {
+ DCHECK_EQ(env, AttachCurrentThread()); // Is |env| on correct thread.
+ }
+ if (obj)
+ obj = env->NewGlobalRef(obj);
+ if (obj_)
+ env->DeleteGlobalRef(obj_);
+ obj_ = obj;
+}
+
+void JavaRef<jobject>::ResetLocalRef(JNIEnv* env) {
+ if (obj_) {
+ DCHECK_EQ(env, AttachCurrentThread()); // Is |env| on correct thread.
+ env->DeleteLocalRef(obj_);
+ obj_ = NULL;
+ }
+}
+
+void JavaRef<jobject>::ResetGlobalRef() {
+ if (obj_) {
+ AttachCurrentThread()->DeleteGlobalRef(obj_);
+ obj_ = NULL;
+ }
+}
+
+jobject JavaRef<jobject>::ReleaseInternal() {
+ jobject obj = obj_;
+ obj_ = NULL;
+ return obj;
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/scoped_java_ref.h b/chromium/base/android/scoped_java_ref.h
new file mode 100644
index 00000000000..a5d71e2d23b
--- /dev/null
+++ b/chromium/base/android/scoped_java_ref.h
@@ -0,0 +1,198 @@
+// Copyright (c) 2012 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_ANDROID_SCOPED_JAVA_REF_H_
+#define BASE_ANDROID_SCOPED_JAVA_REF_H_
+
+#include <jni.h>
+#include <stddef.h>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+namespace android {
+
+// Forward declare the generic java reference template class.
+template<typename T> class JavaRef;
+
+// Template specialization of JavaRef, which acts as the base class for all
+// other JavaRef<> template types. This allows you to e.g. pass
+// ScopedJavaLocalRef<jstring> into a function taking const JavaRef<jobject>&
+template<>
+class BASE_EXPORT JavaRef<jobject> {
+ public:
+ jobject obj() const { return obj_; }
+
+ bool is_null() const { return obj_ == NULL; }
+
+ protected:
+ // Initializes a NULL reference.
+ JavaRef();
+
+ // Takes ownership of the |obj| reference passed; requires it to be a local
+ // reference type.
+ JavaRef(JNIEnv* env, jobject obj);
+
+ ~JavaRef();
+
+ // The following are implementation detail convenience methods, for
+ // use by the sub-classes.
+ JNIEnv* SetNewLocalRef(JNIEnv* env, jobject obj);
+ void SetNewGlobalRef(JNIEnv* env, jobject obj);
+ void ResetLocalRef(JNIEnv* env);
+ void ResetGlobalRef();
+ jobject ReleaseInternal();
+
+ private:
+ jobject obj_;
+
+ DISALLOW_COPY_AND_ASSIGN(JavaRef);
+};
+
+// Generic base class for ScopedJavaLocalRef and ScopedJavaGlobalRef. Useful
+// for allowing functions to accept a reference without having to mandate
+// whether it is a local or global type.
+template<typename T>
+class JavaRef : public JavaRef<jobject> {
+ public:
+ T obj() const { return static_cast<T>(JavaRef<jobject>::obj()); }
+
+ protected:
+ JavaRef() {}
+ ~JavaRef() {}
+
+ JavaRef(JNIEnv* env, T obj) : JavaRef<jobject>(env, obj) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(JavaRef);
+};
+
+// Holds a local reference to a Java object. The local reference is scoped
+// to the lifetime of this object.
+// Instances of this class may hold onto any JNIEnv passed into it until
+// destroyed. Therefore, since a JNIEnv is only suitable for use on a single
+// thread, objects of this class must be created, used, and destroyed, on a
+// single thread.
+// Therefore, this class should only be used as a stack-based object and from a
+// single thread. If you wish to have the reference outlive the current
+// callstack (e.g. as a class member) or you wish to pass it across threads,
+// use a ScopedJavaGlobalRef instead.
+template<typename T>
+class ScopedJavaLocalRef : public JavaRef<T> {
+ public:
+ ScopedJavaLocalRef() : env_(NULL) {}
+
+ // Non-explicit copy constructor, to allow ScopedJavaLocalRef to be returned
+ // by value as this is the normal usage pattern.
+ ScopedJavaLocalRef(const ScopedJavaLocalRef<T>& other)
+ : env_(other.env_) {
+ this->SetNewLocalRef(env_, other.obj());
+ }
+
+ template<typename U>
+ explicit ScopedJavaLocalRef(const U& other)
+ : env_(NULL) {
+ this->Reset(other);
+ }
+
+ // Assumes that |obj| is a local reference to a Java object and takes
+ // ownership of this local reference.
+ ScopedJavaLocalRef(JNIEnv* env, T obj) : JavaRef<T>(env, obj), env_(env) {}
+
+ ~ScopedJavaLocalRef() {
+ this->Reset();
+ }
+
+ // Overloaded assignment operator defined for consistency with the implicit
+ // copy constructor.
+ void operator=(const ScopedJavaLocalRef<T>& other) {
+ this->Reset(other);
+ }
+
+ void Reset() {
+ this->ResetLocalRef(env_);
+ }
+
+ template<typename U>
+ void Reset(const ScopedJavaLocalRef<U>& other) {
+ // We can copy over env_ here as |other| instance must be from the same
+ // thread as |this| local ref. (See class comment for multi-threading
+ // limitations, and alternatives).
+ this->Reset(other.env_, other.obj());
+ }
+
+ template<typename U>
+ void Reset(const U& other) {
+ // If |env_| was not yet set (is still NULL) it will be attached to the
+ // current thread in SetNewLocalRef().
+ this->Reset(env_, other.obj());
+ }
+
+ template<typename U>
+ void Reset(JNIEnv* env, U obj) {
+ implicit_cast<T>(obj); // Ensure U is assignable to T
+ env_ = this->SetNewLocalRef(env, obj);
+ }
+
+ // Releases the local reference to the caller. The caller *must* delete the
+ // local reference when it is done with it.
+ T Release() {
+ return static_cast<T>(this->ReleaseInternal());
+ }
+
+ private:
+ // This class is only good for use on the thread it was created on so
+ // it's safe to cache the non-threadsafe JNIEnv* inside this object.
+ JNIEnv* env_;
+};
+
+// Holds a global reference to a Java object. The global reference is scoped
+// to the lifetime of this object. This class does not hold onto any JNIEnv*
+// passed to it, hence it is safe to use across threads (within the constraints
+// imposed by the underlying Java object that it references).
+template<typename T>
+class ScopedJavaGlobalRef : public JavaRef<T> {
+ public:
+ ScopedJavaGlobalRef() {}
+
+ explicit ScopedJavaGlobalRef(const ScopedJavaGlobalRef<T>& other) {
+ this->Reset(other);
+ }
+
+ template<typename U>
+ explicit ScopedJavaGlobalRef(const U& other) {
+ this->Reset(other);
+ }
+
+ ~ScopedJavaGlobalRef() {
+ this->Reset();
+ }
+
+ void Reset() {
+ this->ResetGlobalRef();
+ }
+
+ template<typename U>
+ void Reset(const U& other) {
+ this->Reset(NULL, other.obj());
+ }
+
+ template<typename U>
+ void Reset(JNIEnv* env, U obj) {
+ implicit_cast<T>(obj); // Ensure U is assignable to T
+ this->SetNewGlobalRef(env, obj);
+ }
+
+ // Releases the global reference to the caller. The caller *must* delete the
+ // global reference when it is done with it.
+ T Release() {
+ return static_cast<T>(this->ReleaseInternal());
+ }
+};
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_SCOPED_JAVA_REF_H_
diff --git a/chromium/base/android/scoped_java_ref_unittest.cc b/chromium/base/android/scoped_java_ref_unittest.cc
new file mode 100644
index 00000000000..36f253c4e99
--- /dev/null
+++ b/chromium/base/android/scoped_java_ref_unittest.cc
@@ -0,0 +1,122 @@
+// Copyright (c) 2012 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/scoped_java_ref.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace android {
+
+namespace {
+int g_local_refs = 0;
+int g_global_refs = 0;
+
+const JNINativeInterface* g_previous_functions;
+
+jobject NewGlobalRef(JNIEnv* env, jobject obj) {
+ ++g_global_refs;
+ return g_previous_functions->NewGlobalRef(env, obj);
+}
+
+void DeleteGlobalRef(JNIEnv* env, jobject obj) {
+ --g_global_refs;
+ return g_previous_functions->DeleteGlobalRef(env, obj);
+}
+
+jobject NewLocalRef(JNIEnv* env, jobject obj) {
+ ++g_local_refs;
+ return g_previous_functions->NewLocalRef(env, obj);
+}
+
+void DeleteLocalRef(JNIEnv* env, jobject obj) {
+ --g_local_refs;
+ return g_previous_functions->DeleteLocalRef(env, obj);
+}
+} // namespace
+
+class ScopedJavaRefTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ g_local_refs = 0;
+ g_global_refs = 0;
+ JNIEnv* env = AttachCurrentThread();
+ g_previous_functions = env->functions;
+ hooked_functions = *g_previous_functions;
+ env->functions = &hooked_functions;
+ // We inject our own functions in JNINativeInterface so we can keep track
+ // of the reference counting ourselves.
+ hooked_functions.NewGlobalRef = &NewGlobalRef;
+ hooked_functions.DeleteGlobalRef = &DeleteGlobalRef;
+ hooked_functions.NewLocalRef = &NewLocalRef;
+ hooked_functions.DeleteLocalRef = &DeleteLocalRef;
+ }
+
+ virtual void TearDown() {
+ JNIEnv* env = AttachCurrentThread();
+ env->functions = g_previous_functions;
+ }
+ // From JellyBean release, the instance of this struct provided in JNIEnv is
+ // read-only, so we deep copy it to allow individual functions to be hooked.
+ JNINativeInterface hooked_functions;
+};
+
+// The main purpose of this is testing the various conversions compile.
+TEST_F(ScopedJavaRefTest, Conversions) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, "string");
+ ScopedJavaGlobalRef<jstring> global(str);
+ {
+ ScopedJavaGlobalRef<jobject> global_obj(str);
+ ScopedJavaLocalRef<jobject> local_obj(global);
+ const JavaRef<jobject>& obj_ref1(str);
+ const JavaRef<jobject>& obj_ref2(global);
+ EXPECT_TRUE(env->IsSameObject(obj_ref1.obj(), obj_ref2.obj()));
+ EXPECT_TRUE(env->IsSameObject(global_obj.obj(), obj_ref2.obj()));
+ }
+ global.Reset(str);
+ const JavaRef<jstring>& str_ref = str;
+ EXPECT_EQ("string", ConvertJavaStringToUTF8(str_ref));
+ str.Reset();
+}
+
+TEST_F(ScopedJavaRefTest, RefCounts) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> str;
+ // The ConvertJavaStringToUTF8 below creates a new string that would normally
+ // return a local ref. We simulate that by starting the g_local_refs count at
+ // 1.
+ g_local_refs = 1;
+ str.Reset(ConvertUTF8ToJavaString(env, "string"));
+ EXPECT_EQ(1, g_local_refs);
+ EXPECT_EQ(0, g_global_refs);
+ {
+ ScopedJavaGlobalRef<jstring> global_str(str);
+ ScopedJavaGlobalRef<jobject> global_obj(global_str);
+ EXPECT_EQ(1, g_local_refs);
+ EXPECT_EQ(2, g_global_refs);
+
+ ScopedJavaLocalRef<jstring> str2(env, str.Release());
+ EXPECT_EQ(1, g_local_refs);
+ {
+ ScopedJavaLocalRef<jstring> str3(str2);
+ EXPECT_EQ(2, g_local_refs);
+ }
+ EXPECT_EQ(1, g_local_refs);
+ str2.Reset();
+ EXPECT_EQ(0, g_local_refs);
+ global_str.Reset();
+ EXPECT_EQ(1, g_global_refs);
+ ScopedJavaGlobalRef<jobject> global_obj2(global_obj);
+ EXPECT_EQ(2, g_global_refs);
+ }
+
+ EXPECT_EQ(0, g_local_refs);
+ EXPECT_EQ(0, g_global_refs);
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/sys_utils.cc b/chromium/base/android/sys_utils.cc
new file mode 100644
index 00000000000..c23125390da
--- /dev/null
+++ b/chromium/base/android/sys_utils.cc
@@ -0,0 +1,31 @@
+// 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/sys_utils.h"
+
+#include "base/sys_info.h"
+#include "jni/SysUtils_jni.h"
+
+const int64 kLowEndMemoryThreshold = 1024 * 1024 * 512; // 512 mb.
+
+// Defined and called by JNI
+static jboolean IsLowEndDevice(JNIEnv* env, jclass clazz) {
+ return base::android::SysUtils::IsLowEndDevice();
+}
+
+namespace base {
+namespace android {
+
+bool SysUtils::Register(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+bool SysUtils::IsLowEndDevice() {
+ return SysInfo::AmountOfPhysicalMemory() <= kLowEndMemoryThreshold;
+}
+
+SysUtils::SysUtils() { }
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/sys_utils.h b/chromium/base/android/sys_utils.h
new file mode 100644
index 00000000000..78122ff572d
--- /dev/null
+++ b/chromium/base/android/sys_utils.h
@@ -0,0 +1,26 @@
+// 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_ANDROID_SYS_UTILS_H_
+#define BASE_ANDROID_SYS_UTILS_H_
+
+#include "base/android/jni_android.h"
+
+namespace base {
+namespace android {
+
+class BASE_EXPORT SysUtils {
+ public:
+ static bool Register(JNIEnv* env);
+
+ static bool IsLowEndDevice();
+
+ private:
+ SysUtils();
+};
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_SYS_UTILS_H_
diff --git a/chromium/base/android/thread_utils.h b/chromium/base/android/thread_utils.h
new file mode 100644
index 00000000000..cbe65f08134
--- /dev/null
+++ b/chromium/base/android/thread_utils.h
@@ -0,0 +1,16 @@
+// Copyright (c) 2012 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_ANDROID_THREAD_UTILS_H_
+#define BASE_ANDROID_THREAD_UTILS_H_
+
+#include "base/android/jni_android.h"
+
+namespace base {
+
+bool RegisterThreadUtils(JNIEnv* env);
+
+} // namespace base
+
+#endif // BASE_ANDROID_THREAD_UTILS_H_