diff options
34 files changed, 1669 insertions, 75 deletions
diff --git a/base/android/java/base.xml b/base/android/java/base.xml index 2da1441..4bb3387 100644 --- a/base/android/java/base.xml +++ b/base/android/java/base.xml @@ -10,7 +10,7 @@ <property name="sdk.version" value="${env.ANDROID_SDK_VERSION}"/> <property name="src" location="."/> <property name="dist" location="dist"/> - <property name="out.dir" location="${PRODUCT_DIR}"/> + <property name="out.dir" location="${PRODUCT_DIR}/lib.java"/> <!-- TODO(jrg): establish a standard for the intermediate java directories. Settle on a standard once ant/jar build files like this are androidified --> diff --git a/build/android/adb_install_content_shell b/build/android/adb_install_content_shell index b4d8052..d1a8ee9 100755 --- a/build/android/adb_install_content_shell +++ b/build/android/adb_install_content_shell @@ -4,4 +4,6 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# install -r doesn't always work? Try uninstalling first. +adb uninstall org.chromium.content_shell adb install -r ${CHROME_SRC}/out/Release/content_shell/ContentShell-debug.apk diff --git a/build/android/envsetup.sh b/build/android/envsetup.sh index ead8fdf..dcf51ee 100755 --- a/build/android/envsetup.sh +++ b/build/android/envsetup.sh @@ -62,6 +62,14 @@ case "${TARGET_PRODUCT-full}" in return 1 esac +# If we are building NDK/SDK, and in the upstream (open source) tree, +# define a special variable for bringup purposes. +case "${ANDROID_BUILD_TOP-undefined}" in + "undefined") + DEFINES+=" android_upstream_bringup=1" + ;; +esac + toolchain_path="${ANDROID_NDK_ROOT}/toolchains/${toolchain_arch}/prebuilt/" export ANDROID_TOOLCHAIN="${toolchain_path}/${toolchain_dir}/bin/" diff --git a/build/android/gdb_content_shell b/build/android/gdb_content_shell index 54a705d..94c0962 100755 --- a/build/android/gdb_content_shell +++ b/build/android/gdb_content_shell @@ -72,8 +72,9 @@ fi # gdb commands cmdfile=$(mktemp /tmp/gdb_android_XXXXXXXX) cat >$cmdfile<<EOF -set solib-absolute-prefix null +# set solib-absolute-prefix null set solib-search-path ${shared_lib_dir} +file ${app_process} target remote :4321 EOF @@ -85,5 +86,6 @@ else echo Using $gdb fi -${gdb} -x $cmdfile $* $app_process +# ${gdb} -x $cmdfile $* $app_process +${gdb} -x $cmdfile $* rm $cmdfile diff --git a/build/common.gypi b/build/common.gypi index 7e999a7..b79ae1b 100644 --- a/build/common.gypi +++ b/build/common.gypi @@ -47,6 +47,11 @@ # Enable inclusion of touch-optimized resources. # TODO(joi): Rename to enable_touch_assets. 'enable_metro%': 0, + + # Is this change part of the android upstream bringup? + # Allows us to *temporarily* disable certain things for + # staging. Only set to 1 in a GYP_DEFINES. + 'android_upstream_bringup%': 0, }, # Copy conditionally-set variables out one scope. 'chromeos%': '<(chromeos)', @@ -58,6 +63,7 @@ 'enable_hidpi%': '<(enable_hidpi)', 'enable_touch_ui%': '<(enable_touch_ui)', 'enable_metro%': '<(enable_metro)', + 'android_upstream_bringup%': '<(android_upstream_bringup)', # Compute the architecture that we're building on. 'conditions': [ @@ -2387,6 +2393,9 @@ '-lm', ], 'conditions': [ + ['android_upstream_bringup==1', { + 'defines': ['ANDROID_UPSTREAM_BRINGUP=1',], + }], ['android_build_type==0', { 'ldflags': [ '--sysroot=<(android_ndk_sysroot)', diff --git a/build/java.gypi b/build/java.gypi index bb60a8f..c05981d 100644 --- a/build/java.gypi +++ b/build/java.gypi @@ -25,7 +25,7 @@ # base/android/java/org/chromium/base/Bar.java # # Finally, the generated jar-file will be: -# <(PRODUCT_DIR)/chromium_base.jar +# <(PRODUCT_DIR)/lib.java/chromium_base.jar # # TODO(yfriedman): The "finally" statement isn't entirely true yet, as we don't # auto-generate the ant file yet. @@ -40,7 +40,7 @@ '<!@(find <(java_in_dir) -name "*.java")' ], 'outputs': [ - '<(PRODUCT_DIR)/chromium_<(package_name).jar', + '<(PRODUCT_DIR)/lib.java/chromium_<(package_name).jar', ], 'action': [ 'ant', diff --git a/content/app/content_main_runner.cc b/content/app/content_main_runner.cc index f87bac4..02d9b44 100644 --- a/content/app/content_main_runner.cc +++ b/content/app/content_main_runner.cc @@ -79,7 +79,7 @@ extern int PpapiBrokerMain(const content::MainFunctionParams&); extern int RendererMain(const content::MainFunctionParams&); extern int WorkerMain(const content::MainFunctionParams&); extern int UtilityMain(const content::MainFunctionParams&); -#if defined(OS_POSIX) && !defined(OS_MACOSX) +#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) namespace content { extern int ZygoteMain(const content::MainFunctionParams&, content::ZygoteForkDelegate* forkdelegate); @@ -194,7 +194,7 @@ struct MainFunction { int (*function)(const content::MainFunctionParams&); }; -#if defined(OS_POSIX) && !defined(OS_MACOSX) +#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) // On platforms that use the zygote, we have a special subset of // subprocesses that are launched via the zygote. This function // fills in some process-launching bits around ZygoteMain(). @@ -293,7 +293,7 @@ int RunNamedProcessTypeMain( } } -#if defined(OS_POSIX) && !defined(OS_MACOSX) +#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) // Zygote startup is special -- see RunZygote comments above // for why we don't use ZygoteMain directly. if (process_type == switches::kZygoteProcess) @@ -352,6 +352,7 @@ static void ReleaseFreeMemoryThunk() { virtual int Initialize(int argc, const char** argv, content::ContentMainDelegate* delegate) OVERRIDE { + // NOTE(willchan): One might ask why this call is done here rather than in // process_util_linux.cc with the definition of // EnableTerminationOnOutOfMemory(). That's because base shouldn't have a @@ -367,17 +368,24 @@ static void ReleaseFreeMemoryThunk() { base::allocator::SetReleaseFreeMemoryFunction(ReleaseFreeMemoryThunk); #endif + // On Android, + // - setlocale() is not supported. + // - We do not override the signal handlers so that we can get + // stack trace when crashing. + // - The ipc_fd is passed through the Java service. + // Thus, these are all disabled. #if !defined(OS_ANDROID) // Set C library locale to make sure CommandLine can parse argument values // in correct encoding. setlocale(LC_ALL, ""); -#endif SetupSignalHandlers(); base::GlobalDescriptors* g_fds = base::GlobalDescriptors::GetInstance(); g_fds->Set(kPrimaryIPCChannel, kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor); +#endif + #if defined(OS_LINUX) || defined(OS_OPENBSD) g_fds->Set(kCrashDumpSignal, kCrashDumpSignal + base::GlobalDescriptors::kBaseDescriptor); @@ -391,8 +399,11 @@ static void ReleaseFreeMemoryThunk() { base::EnableTerminationOnHeapCorruption(); base::EnableTerminationOnOutOfMemory(); + // On Android, AtExitManager is set up when library is loaded. +#if !defined(OS_ANDROID) // The exit manager is in charge of calling the dtors of singleton objects. exit_manager_.reset(new base::AtExitManager); +#endif #if defined(OS_MACOSX) // We need this pool for all the objects created before we get to the @@ -402,7 +413,11 @@ static void ReleaseFreeMemoryThunk() { autorelease_pool_.reset(new base::mac::ScopedNSAutoreleasePool()); #endif + // On Android, the command line is initialized when library is loaded. + // (But *is* initialized here for content shell bringup) +#if !defined(OS_ANDROID) || defined(ANDROID_UPSTREAM_BRINGUP) CommandLine::Init(argc, argv); +#endif int exit_code; if (delegate && delegate->BasicStartupComplete(&exit_code)) @@ -473,6 +488,12 @@ static void ReleaseFreeMemoryThunk() { ui::RegisterPathProvider(); content::RegisterPathProvider(); + + // TODO(jrg): "up to here" is how far we get without crashing on + // content shell bringup. +#if defined(ANDROID_UPSTREAM_BRINGUP) + return 0; +#endif content::RegisterContentSchemes(true); CHECK(icu_util::Initialize()); diff --git a/content/browser/android/command_line.cc b/content/browser/android/command_line.cc new file mode 100644 index 0000000..1883b07f --- /dev/null +++ b/content/browser/android/command_line.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 "content/browser/android/command_line.h" + +#include "base/android/jni_string.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "jni/command_line_jni.h" +#include "content/browser/android/jni_helper.h" + +using base::android::ConvertJavaStringToUTF8; + +namespace { + +void AppendJavaStringArrayToCommandLine(JNIEnv* env, + jobjectArray array, + bool includes_program) { + CommandLine::StringVector vec; + if (array) + ConvertJavaArrayOfStringsToVectorOfStrings(env, array, &vec); + if (!includes_program) + vec.insert(vec.begin(), ""); + CommandLine extra_command_line(vec); + CommandLine::ForCurrentProcess()->AppendArguments(extra_command_line, + includes_program); +} + +} // namespace + +static void Reset(JNIEnv* env, jclass clazz) { + CommandLine::Reset(); +} + +static jboolean HasSwitch(JNIEnv* env, jclass clazz, jstring jswitch) { + std::string switch_string(ConvertJavaStringToUTF8(env, jswitch)); + return CommandLine::ForCurrentProcess()->HasSwitch(switch_string); +} + +static jstring GetSwitchValue(JNIEnv* env, jclass clazz, jstring jswitch) { + std::string switch_string(ConvertJavaStringToUTF8(env, jswitch)); + std::string value(CommandLine::ForCurrentProcess()->GetSwitchValueNative( + switch_string)); + if (value.empty()) + return 0; + // OK to release, JNI binding. + return base::android::ConvertUTF8ToJavaString(env, value).Release(); +} + +static void AppendSwitch(JNIEnv* env, jclass clazz, jstring jswitch) { + std::string switch_string(ConvertJavaStringToUTF8(env, jswitch)); + CommandLine::ForCurrentProcess()->AppendSwitch(switch_string); +} + +static void AppendSwitchWithValue(JNIEnv* env, jclass clazz, + jstring jswitch, jstring jvalue) { + std::string switch_string(ConvertJavaStringToUTF8(env, jswitch)); + std::string value_string (ConvertJavaStringToUTF8(env, jvalue)); + CommandLine::ForCurrentProcess()->AppendSwitchASCII(switch_string, + value_string); +} + +static void AppendSwitchesAndArguments(JNIEnv* env, jclass clazz, + jobjectArray array) { + AppendJavaStringArrayToCommandLine(env, array, false); +} + +void InitNativeCommandLineFromJavaArray(JNIEnv* env, jobjectArray array) { + // TODO(port): Make an overload of Init() that takes StringVector rather than + // have to round-trip via AppendArguments. + CommandLine::Init(0, NULL); + AppendJavaStringArrayToCommandLine(env, array, true); +} + +bool RegisterCommandLine(JNIEnv* env) { + return RegisterNativesImpl(env); +} diff --git a/content/browser/android/command_line.h b/content/browser/android/command_line.h new file mode 100644 index 0000000..5e44080 --- /dev/null +++ b/content/browser/android/command_line.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 CONTENT_BROWSER_ANDROID_COMMAND_LINE_H_ +#define CONTENT_BROWSER_ANDROID_COMMAND_LINE_H_ +#pragma once + +#include <jni.h> + +// Appends all strings in the given array as flags to the Chrome command line. +void InitNativeCommandLineFromJavaArray(JNIEnv* env, + jobjectArray init_command_line); + +// JNI registration boilerplate. +bool RegisterCommandLine(JNIEnv* env); + +#endif // CONTENT_BROWSER_ANDROID_COMMAND_LINE_H_ diff --git a/content/browser/android/content_jni_registrar.cc b/content/browser/android/content_jni_registrar.cc new file mode 100644 index 0000000..c32cc50 --- /dev/null +++ b/content/browser/android/content_jni_registrar.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 "content/browser/android/content_jni_registrar.h" + +#include "base/android/jni_android.h" +#include "base/android/jni_registrar.h" +#include "content/browser/android/command_line.h" +#include "content/browser/android/trace_event_binding.h" + +namespace content { +namespace android { + +base::android::RegistrationMethod kContentRegisteredMethods[] = { + { "CommandLine", RegisterCommandLine }, + { "TraceEvent", RegisterTraceEvent }, +}; + +bool RegisterJni(JNIEnv* env) { + return RegisterNativeMethods(env, kContentRegisteredMethods, + arraysize(kContentRegisteredMethods)); +} + +} // namespace android +} // namespace content diff --git a/content/browser/android/content_jni_registrar.h b/content/browser/android/content_jni_registrar.h new file mode 100644 index 0000000..ce41c63 --- /dev/null +++ b/content/browser/android/content_jni_registrar.h @@ -0,0 +1,19 @@ +// 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 CONTENT_BROWSER_ANDROID_CONTENT_JNI_REGISTRAR_H_ +#define CONTENT_BROWSER_ANDROID_CONTENT_JNI_REGISTRAR_H_ + +#include <jni.h> + +namespace content { +namespace android { + +// Register all JNI bindings necessary for content. +bool RegisterJni(JNIEnv* env); + +} // namespace android +} // namespace content + +#endif // CONTENT_BROWSER_ANDROID_CONTENT_JNI_REGISTRAR_H_ diff --git a/content/browser/android/jni_helper.cc b/content/browser/android/jni_helper.cc new file mode 100644 index 0000000..bd7bce6 --- /dev/null +++ b/content/browser/android/jni_helper.cc @@ -0,0 +1,158 @@ +// 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 "content/browser/android/jni_helper.h" + +#include <android/bitmap.h> +#include <vector> + +#include "base/android/jni_android.h" +#include "base/android/jni_helper.h" +#include "base/android/jni_string.h" +#include "base/debug/trace_event.h" +#include "base/logging.h" +#include "jni/jni_helper_jni.h" +#include "third_party/skia/include/core/SkBitmap.h" + +using base::android::AttachCurrentThread; +using base::android::CheckException; +using base::android::GetStaticMethodID; +using base::android::GetClass; +using base::android::GetMethodID; +using base::android::ScopedJavaLocalRef; + +AutoLocalFrame::AutoLocalFrame(int capacity) { + AttachCurrentThread()->PushLocalFrame(capacity); +} + +AutoLocalFrame::~AutoLocalFrame() { + AttachCurrentThread()->PopLocalFrame(NULL); +} + +AutoLockJavaBitmap::AutoLockJavaBitmap(jobject bitmap) : + bitmap_(bitmap), + pixels_(NULL) { + int err = AndroidBitmap_lockPixels(AttachCurrentThread(), bitmap_, &pixels_); + DCHECK(!err); + DCHECK(pixels_); +} + +AutoLockJavaBitmap::~AutoLockJavaBitmap() { + // TODO(aelias): Add AndroidBitmap_notifyPixelsChanged() call here + // once it's added to the NDK. Using hardware bitmaps will + // be broken until this is fixed. + int err = AndroidBitmap_unlockPixels(AttachCurrentThread(), bitmap_); + DCHECK(!err); +} + +gfx::Size AutoLockJavaBitmap::size() const { + AndroidBitmapInfo info; + int err = AndroidBitmap_getInfo(AttachCurrentThread(), bitmap_, &info); + DCHECK(!err); + return gfx::Size(info.width, info.height); +} + +int AutoLockJavaBitmap::format() const { + AndroidBitmapInfo info; + int err = AndroidBitmap_getInfo(AttachCurrentThread(), bitmap_, &info); + DCHECK(!err); + return info.format; +} + +uint32_t AutoLockJavaBitmap::stride() const { + AndroidBitmapInfo info; + int err = AndroidBitmap_getInfo(AttachCurrentThread(), bitmap_, &info); + DCHECK(!err); + return info.stride; +} + +void PrintJavaStackTrace() { + JNIEnv* env = AttachCurrentThread(); + + ScopedJavaLocalRef<jclass> throwable_clazz = + GetClass(env, "java/lang/Throwable"); + jmethodID throwable_constructor = + GetMethodID(env, throwable_clazz, "<init>", "()V"); + + ScopedJavaLocalRef<jobject> throwable_object(env, + env->NewObject(throwable_clazz.obj(), throwable_constructor)); + DCHECK(!throwable_object.is_null()); + jmethodID printstacktrace = + GetMethodID(env, throwable_clazz, "printStackTrace", "()V"); + + env->CallVoidMethod(throwable_object.obj(), printstacktrace); + CheckException(env); +} + +void ConvertJavaArrayOfStringsToVectorOfStrings( + JNIEnv* env, + jobjectArray jstrings, + std::vector<std::string>* vec) { + vec->clear(); + jsize length = env->GetArrayLength(jstrings); + for (jsize i = 0; i < length; ++i) { + jstring item = static_cast<jstring>( + env->GetObjectArrayElement(jstrings, i)); + vec->push_back(base::android::ConvertJavaStringToUTF8(env, item)); + env->DeleteLocalRef(item); + } +} + +ScopedJavaLocalRef<jobject> CreateJavaBitmap(const gfx::Size& size, bool keep) { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> bitmap = + Java_JNIHelper_createJavaBitmap(env, size.width(), size.height(), keep); + CheckException(env); + return bitmap; +} + +void DeleteJavaBitmap(jobject bitmap) { + JNIEnv* env = AttachCurrentThread(); + Java_JNIHelper_deleteJavaBitmap(env, bitmap); + CheckException(env); +} + +void PaintJavaBitmapToJavaBitmap(jobject source_bitmap, + const gfx::Rect& source_rect, + jobject dest_bitmap, + const gfx::Rect& dest_rect) { + TRACE_EVENT0("jni", "PaintJavaBitmapToJavaBitmap"); + JNIEnv* env = AttachCurrentThread(); + + Java_JNIHelper_paintJavaBitmapToJavaBitmap(env, + source_bitmap, + source_rect.x(), + source_rect.y(), + source_rect.right(), + source_rect.bottom(), + dest_bitmap, + dest_rect.x(), + dest_rect.y(), + dest_rect.right(), + dest_rect.bottom()); + CheckException(env); +} + +ScopedJavaLocalRef<jobject> ConvertToJavaBitmap(const SkBitmap* skbitmap) { + TRACE_EVENT0("jni", "ConvertToJavaBitmap"); + DCHECK(skbitmap); + DCHECK_EQ(skbitmap->bytesPerPixel(), 4); + + // Create a temporary java bitmap. + ScopedJavaLocalRef<jobject> jbitmap = + CreateJavaBitmap(gfx::Size(skbitmap->width(), skbitmap->height()), false); + + // Lock and copy the pixels from the skbitmap. + SkAutoLockPixels src_lock(*skbitmap); + AutoLockJavaBitmap dst_lock(jbitmap.obj()); + void* src_pixels = skbitmap->getPixels(); + void* dst_pixels = dst_lock.pixels(); + memcpy(dst_pixels, src_pixels, skbitmap->getSize()); + + return jbitmap; +} + +bool RegisterJniHelper(JNIEnv* env) { + return RegisterNativesImpl(env); +} diff --git a/content/browser/android/jni_helper.h b/content/browser/android/jni_helper.h new file mode 100644 index 0000000..c7fa569 --- /dev/null +++ b/content/browser/android/jni_helper.h @@ -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. + +#ifndef CONTENT_BROWSER_ANDROID_JNI_HELPER_H_ +#define CONTENT_BROWSER_ANDROID_JNI_HELPER_H_ + +#include <jni.h> +#include <string> +#include <vector> + +#include "base/android/scoped_java_ref.h" +#include "base/string16.h" +#include "ui/gfx/rect.h" + +class SkBitmap; +namespace gfx { +class Size; +} + +// Auto creator/destructor for a JNI frame for local references. This allows +// safely having more than 16 local references, and avoids calls to +// DeleteLocalRef. +// This should be created on the stack. +class AutoLocalFrame { + public: + AutoLocalFrame(int capacity); + ~AutoLocalFrame(); + + private: + DISALLOW_COPY_AND_ASSIGN(AutoLocalFrame); +}; + +// Create this object on the stack to obtain the pixels of a Java Bitmap and +// automatically release them on destruction. +// Similar to SkAutoLockPixels, except that it operates on a Java Bitmap +// object via the Android NDK. +class AutoLockJavaBitmap { + public: + AutoLockJavaBitmap(jobject bitmap); + ~AutoLockJavaBitmap(); + + void* pixels() const { return pixels_; } + gfx::Size size() const; + // Formats are in android/bitmap.h; e.g. ANDROID_BITMAP_FORMAT_RGBA_8888 */ + int format() const; + uint32_t stride() const; + + private: + jobject bitmap_; + void* pixels_; + + DISALLOW_COPY_AND_ASSIGN(AutoLockJavaBitmap); +}; + +// Tell Java to write its current stack trace into Android logs. Note that the +// trace will stop at the entry point into C++ code. +void PrintJavaStackTrace(); + +// Fills in the given vector<string> from a java String[]. +void ConvertJavaArrayOfStringsToVectorOfStrings( + JNIEnv* env, + jobjectArray jstrings, + std::vector<std::string>* vec); + +// Helper method to create an Android Java Bitmap object. You can use the +// AutoLockJavaBitmap class to manipulate its pixels, and pass it back up +// to Java code for drawing. Returns a JNI local reference to the bitmap. +// If 'keep' is true, then a reference will be kept in a static Java data +// structure to prevent GC. In that case, you must call DeleteJavaBitmap() to +// garbage collect it. +base::android::ScopedJavaLocalRef<jobject> CreateJavaBitmap( + const gfx::Size& size, bool keep); +void DeleteJavaBitmap(jobject bitmap); + +// Paint one java bitmap into another. Scale if needed. +void PaintJavaBitmapToJavaBitmap(jobject sourceBitmap, + const gfx::Rect& sourceRect, + jobject destBitmap, + const gfx::Rect& destRect); + +// Copy the Chromium Skia bitmap into a new Java bitmap. Useful for small UI +// bitmaps originating from WebKit that we want to manipulate in Java (such as +// favicons). Due to the extra copy, should be avoided for large or frequently +// used bitmaps. Returns a local reference to the new bitmap. +base::android::ScopedJavaLocalRef<jobject> ConvertToJavaBitmap( + const SkBitmap* skbitmap); + +// Registers our JNI methods. +bool RegisterJniHelper(JNIEnv* env); + +#endif // CONTENT_BROWSER_ANDROID_JNI_HELPER_H_ diff --git a/content/browser/android/library_loader_hooks.cc b/content/browser/android/library_loader_hooks.cc index 3f2315e..17ebbd4 100644 --- a/content/browser/android/library_loader_hooks.cc +++ b/content/browser/android/library_loader_hooks.cc @@ -5,8 +5,10 @@ #include "content/public/browser/android_library_loader_hooks.h" #include "base/android/base_jni_registrar.h" +#include "base/android/jni_registrar.h" #include "base/android/jni_android.h" #include "base/android/jni_string.h" +#include "base/at_exit.h" #include "base/command_line.h" #include "base/debug/trace_event.h" #include "base/file_path.h" @@ -16,14 +18,21 @@ #include "base/string_util.h" #include "base/tracked_objects.h" #include "content/public/common/content_switches.h" +#include "content/browser/android/command_line.h" +#include "content/browser/android/content_jni_registrar.h" #include "media/base/android/media_jni_registrar.h" #include "net/android/net_jni_registrar.h" +namespace { +base::AtExitManager* g_at_exit_manager = NULL; +} + jboolean LibraryLoaderEntryHook(JNIEnv* env, jclass clazz, jobjectArray init_command_line) { - - // TODO(tedchoc): Initialize the native command line from the java array - // passed in. + // We need the Chrome AtExitManager to be created before we do any tracing or + // logging. + g_at_exit_manager = new base::AtExitManager(); + InitNativeCommandLineFromJavaArray(env, init_command_line); CommandLine* command_line = CommandLine::ForCurrentProcess(); @@ -54,12 +63,22 @@ jboolean LibraryLoaderEntryHook(JNIEnv* env, jclass clazz, if (!net::android::RegisterJni(env)) return JNI_FALSE; + if (!content::android::RegisterJni(env)) + return JNI_FALSE; + if (!media::RegisterJni(env)) return JNI_FALSE; return JNI_TRUE; } +void LibraryLoaderExitHook() { + if (g_at_exit_manager) { + delete g_at_exit_manager; + g_at_exit_manager = NULL; + } +} + bool RegisterLibraryLoaderEntryHook(JNIEnv* env) { // TODO(bulach): use the jni generator once we move jni_helper methods here. const JNINativeMethod kMethods[] = { @@ -67,9 +86,8 @@ bool RegisterLibraryLoaderEntryHook(JNIEnv* env) { reinterpret_cast<void*>(LibraryLoaderEntryHook) }, }; const int kMethodsSize = arraysize(kMethods); - // TODO(tedchoc): Upstream LibraryLoader.java and replace path to make this - // work. - const char kLibraryLoaderPath[] = ""; + const char kLibraryLoaderPath[] = + "org/chromium/content/browser/LibraryLoader"; base::android::ScopedJavaLocalRef<jclass> clazz = base::android::GetClass(env, kLibraryLoaderPath); diff --git a/content/browser/android/trace_event_binding.cc b/content/browser/android/trace_event_binding.cc new file mode 100644 index 0000000..7129946 --- /dev/null +++ b/content/browser/android/trace_event_binding.cc @@ -0,0 +1,93 @@ +// 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 "content/browser/android/trace_event_binding.h" + +#include <jni.h> + +#include <set> + +#include "base/debug/trace_event.h" +#include "base/lazy_instance.h" +#include "jni/trace_event_jni.h" + +namespace { + +const char kJavaCategory[] = "Java"; + +// Boilerplate for safely converting Java data to TRACE_EVENT data. +class TraceEventDataConverter { + public: + TraceEventDataConverter(JNIEnv* env, + jstring jname, + jstring jarg) + : env_(env), + jname_(jname), + jarg_(jarg), + name_(env->GetStringUTFChars(jname, NULL)), + arg_(jarg ? env->GetStringUTFChars(jarg, NULL) : NULL) { + } + ~TraceEventDataConverter() { + env_->ReleaseStringUTFChars(jname_, name_); + if (jarg_) + env_->ReleaseStringUTFChars(jarg_, arg_); + } + + // Return saves values to pass to TRACE_EVENT macros. + const char* name() { return name_; } + const char* arg_name() { return arg_ ? "arg" : NULL; } + const char* arg() { return arg_; } + + private: + JNIEnv* env_; + jstring jname_; + jstring jarg_; + const char* name_; + const char* arg_; + + DISALLOW_COPY_AND_ASSIGN(TraceEventDataConverter); +}; + +} // namespace + +static jboolean TraceEnabled(JNIEnv* env, jclass clazz) { + return base::debug::TraceLog::GetInstance()->IsEnabled(); +} + +static void Instant(JNIEnv* env, jclass clazz, + jstring jname, jstring jarg) { + TraceEventDataConverter converter(env, jname, jarg); + if (converter.arg()) { + TRACE_EVENT_COPY_INSTANT1(kJavaCategory, converter.name(), + converter.arg_name(), converter.arg()); + } else { + TRACE_EVENT_COPY_INSTANT0(kJavaCategory, converter.name()); + } +} + +static void Begin(JNIEnv* env, jclass clazz, + jstring jname, jstring jarg) { + TraceEventDataConverter converter(env, jname, jarg); + if (converter.arg()) { + TRACE_EVENT_COPY_BEGIN1(kJavaCategory, converter.name(), + converter.arg_name(), converter.arg()); + } else { + TRACE_EVENT_COPY_BEGIN0(kJavaCategory, converter.name()); + } +} + +static void End(JNIEnv* env, jclass clazz, + jstring jname, jstring jarg) { + TraceEventDataConverter converter(env, jname, jarg); + if (converter.arg()) { + TRACE_EVENT_COPY_END1(kJavaCategory, converter.name(), + converter.arg_name(), converter.arg()); + } else { + TRACE_EVENT_COPY_END0(kJavaCategory, converter.name()); + } +} + +bool RegisterTraceEvent(JNIEnv* env) { + return RegisterNativesImpl(env); +} diff --git a/content/browser/android/trace_event_binding.h b/content/browser/android/trace_event_binding.h new file mode 100644 index 0000000..3d54e92 --- /dev/null +++ b/content/browser/android/trace_event_binding.h @@ -0,0 +1,13 @@ +// 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 CONTENT_BROWSER_ANDROID_TRACE_EVENT_H_ +#define CONTENT_BROWSER_ANDROID_TRACE_EVENT_H_ +#pragma once + +#include <jni.h> + +extern bool RegisterTraceEvent(JNIEnv* env); + +#endif // CONTENT_BROWSER_ANDROID_TRACE_EVENT_H_ diff --git a/content/content.gyp b/content/content.gyp index b05a6e7..ce006f1 100644 --- a/content/content.gyp +++ b/content/content.gyp @@ -200,30 +200,20 @@ ['OS == "android"', { 'targets': [ { - 'target_name': 'content_jni_headers', - 'type': 'none', - 'variables': { - 'java_sources': [ - '../content/public/android/java/org/chromium/content/browser/LocationProvider.java', - ], - 'jni_headers': [ - '<(SHARED_INTERMEDIATE_DIR)/content/jni/location_provider_jni.h', - ], - }, - 'includes': [ '../build/jni_generator.gypi' ], - }, - { 'target_name': 'content_java', 'type': 'none', + 'dependencies': ['../base/base.gyp:base_java'], 'variables': { 'package_name': 'content', 'java_in_dir': '../content/public/android/java', }, - 'dependencies': [ - '../base/base.gyp:base_java', - ], 'includes': [ '../build/java.gypi' ], }, + { + 'target_name': 'content_jni_headers', + 'type': 'none', + 'includes': [ 'content_jni.gypi' ], + }, ], }], # OS == "android" ], diff --git a/content/content_browser.gypi b/content/content_browser.gypi index bc44c69..a6df267 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -182,7 +182,15 @@ 'browser/accessibility/browser_accessibility_state_impl.h', 'browser/accessibility/browser_accessibility_win.cc', 'browser/accessibility/browser_accessibility_win.h', + 'browser/android/command_line.cc', + 'browser/android/command_line.h', + 'browser/android/content_jni_registrar.cc', + 'browser/android/content_jni_registrar.h', + 'browser/android/jni_helper.cc', + 'browser/android/jni_helper.h', 'browser/android/library_loader_hooks.cc', + 'browser/android/trace_event_binding.cc', + 'browser/android/trace_event_binding.h', 'browser/appcache/appcache_dispatcher_host.cc', 'browser/appcache/appcache_dispatcher_host.h', 'browser/appcache/appcache_frontend_proxy.cc', @@ -821,15 +829,15 @@ 'content.gyp:content_jni_headers', 'content.gyp:content_java', ], + 'include_dirs': [ + '<(SHARED_INTERMEDIATE_DIR)/content', + ], 'sources!': [ 'browser/geolocation/network_location_provider.cc', 'browser/geolocation/network_location_provider.h', 'browser/geolocation/network_location_request.cc', 'browser/geolocation/network_location_request.h', ], - 'include_dirs': [ - '<(SHARED_INTERMEDIATE_DIR)/content', - ], }], ['OS=="mac"', { 'sources': [ diff --git a/content/content_jni.gypi b/content/content_jni.gypi new file mode 100644 index 0000000..dc6849b --- /dev/null +++ b/content/content_jni.gypi @@ -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. + +{ + # TODO(jrg): Update this action and other jni generators to only + # require specifying the java directory and generate the rest. + # TODO(jrg): when doing the above, make sure we support multiple + # output directories (e.g. browser/jni and common/jni if needed). + 'variables': { + 'java_sources': [ + 'public/android/java/org/chromium/content/browser/CommandLine.java', + 'public/android/java/org/chromium/content/browser/JNIHelper.java', + 'public/android/java/org/chromium/content/browser/LibraryLoader.java', + 'public/android/java/org/chromium/content/browser/LocationProvider.java', + 'public/android/java/org/chromium/content/browser/TraceEvent.java', + ], + 'jni_headers': [ + '<(SHARED_INTERMEDIATE_DIR)/content/jni/command_line_jni.h', + '<(SHARED_INTERMEDIATE_DIR)/content/jni/jni_helper_jni.h', + '<(SHARED_INTERMEDIATE_DIR)/content/jni/library_loader_jni.h', + '<(SHARED_INTERMEDIATE_DIR)/content/jni/location_provider_jni.h', + '<(SHARED_INTERMEDIATE_DIR)/content/jni/trace_event_jni.h', + ], + }, + 'includes': [ '../build/jni_generator.gypi' ], +} diff --git a/content/content_shell.gypi b/content/content_shell.gypi index a33508b..4a67c23 100644 --- a/content/content_shell.gypi +++ b/content/content_shell.gypi @@ -523,13 +523,25 @@ 'actions': [ { 'action_name': 'copy_base_jar', - 'inputs': ['<(PRODUCT_DIR)/chromium_base.jar'], + 'inputs': ['<(PRODUCT_DIR)/lib.java/chromium_base.jar'], 'outputs': ['<(PRODUCT_DIR)/content_shell/java/libs/chromium_base.jar'], 'action': ['cp', '<@(_inputs)', '<@(_outputs)'], }, { + 'action_name': 'copy_net_jar', + 'inputs': ['<(PRODUCT_DIR)/lib.java/chromium_net.jar'], + 'outputs': ['<(PRODUCT_DIR)/content_shell/java/libs/chromium_net.jar'], + 'action': ['cp', '<@(_inputs)', '<@(_outputs)'], + }, + { + 'action_name': 'copy_media_jar', + 'inputs': ['<(PRODUCT_DIR)/lib.java/chromium_media.jar'], + 'outputs': ['<(PRODUCT_DIR)/content_shell/java/libs/chromium_media.jar'], + 'action': ['cp', '<@(_inputs)', '<@(_outputs)'], + }, + { 'action_name': 'copy_content_jar', - 'inputs': ['<(PRODUCT_DIR)/chromium_content.jar'], + 'inputs': ['<(PRODUCT_DIR)/lib.java/chromium_content.jar'], 'outputs': ['<(PRODUCT_DIR)/content_shell/java/libs/chromium_content.jar'], 'action': ['cp', '<@(_inputs)', '<@(_outputs)'], }, @@ -553,6 +565,8 @@ '<!@(find shell/android/java -name "*.java")', '<!@(find shell/android/res -name "*")', '<(PRODUCT_DIR)/content_shell/java/libs/chromium_base.jar', + '<(PRODUCT_DIR)/content_shell/java/libs/chromium_net.jar', + '<(PRODUCT_DIR)/content_shell/java/libs/chromium_media.jar', '<(PRODUCT_DIR)/content_shell/java/libs/chromium_content.jar', '<(PRODUCT_DIR)/content_shell/libs/armeabi/libcontent_shell_content_view.so', ], diff --git a/content/plugin/webplugin_proxy.cc b/content/plugin/webplugin_proxy.cc index ee1c721..5a60afc9 100644 --- a/content/plugin/webplugin_proxy.cc +++ b/content/plugin/webplugin_proxy.cc @@ -636,6 +636,16 @@ void WebPluginProxy::SetWindowlessBuffers( } } +#elif defined(OS_ANDROID) + +void WebPluginProxy::SetWindowlessBuffers( + const TransportDIB::Handle& windowless_buffer0, + const TransportDIB::Handle& windowless_buffer1, + const TransportDIB::Handle& background_buffer, + const gfx::Rect& window_rect) { + NOTIMPLEMENTED(); +} + #endif void WebPluginProxy::CancelDocumentLoad() { diff --git a/content/ppapi_plugin/ppapi_webkitplatformsupport_impl.cc b/content/ppapi_plugin/ppapi_webkitplatformsupport_impl.cc index 56ee6a5..2f526fd 100644 --- a/content/ppapi_plugin/ppapi_webkitplatformsupport_impl.cc +++ b/content/ppapi_plugin/ppapi_webkitplatformsupport_impl.cc @@ -20,9 +20,12 @@ #elif defined(OS_MACOSX) #include "third_party/WebKit/Source/WebKit/chromium/public/platform/mac/WebSandboxSupport.h" #elif defined(OS_POSIX) +#if !defined(OS_ANDROID) #include "content/common/child_process_sandbox_support_impl_linux.h" +#endif #include "third_party/WebKit/Source/WebKit/chromium/public/platform/linux/WebFontFamily.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/linux/WebSandboxSupport.h" + #endif using WebKit::WebSandboxSupport; @@ -82,6 +85,24 @@ bool PpapiWebKitPlatformSupportImpl::SandboxSupport::loadFont( return false; } +#elif defined(OS_ANDROID) + +// TODO(jrg): resolve (and implement?) PPAPI SandboxSupport for Android. + +void +PpapiWebKitPlatformSupportImpl::SandboxSupport::getFontFamilyForCharacters( + const WebUChar* characters, + size_t num_characters, + const char* preferred_locale, + WebKit::WebFontFamily* family) { + NOTIMPLEMENTED(); +} + +void PpapiWebKitPlatformSupportImpl::SandboxSupport::getRenderStyleForStrike( + const char* family, int sizeAndStyle, WebKit::WebFontRenderStyle* out) { + NOTIMPLEMENTED(); +} + #elif defined(OS_POSIX) void diff --git a/content/public/android/java/content.xml b/content/public/android/java/content.xml index 435cbab..533accd 100644 --- a/content/public/android/java/content.xml +++ b/content/public/android/java/content.xml @@ -12,10 +12,8 @@ <property name="sdk.dir" location="${env.ANDROID_SDK_ROOT}"/> <property name="sdk.version" value="${env.ANDROID_SDK_VERSION}"/> <property name="src" location="."/> - <property name="out.dir" location="${PRODUCT_DIR}"/> + <property name="out.dir" location="${PRODUCT_DIR}/lib.java"/> <property name="classes.dir" location="${out.dir}/java/${PACKAGE_NAME}"/> - <!-- TODO(tedchoc): Move out of the main out/ dir ... needs to sync with - java.gypi output --> <property name="jar.dir" location="${out.dir}"/> <condition property="location.base" @@ -39,8 +37,8 @@ <!-- Compile the java code from ${src} into ${classes.dir} --> <javac srcdir="${src}" destdir="${classes.dir}"> <classpath> - <path location="${location.base}/android.jar"/> - <path location="${PRODUCT_DIR}/chromium_base.jar"/> + <pathelement path="${location.base}/android.jar" /> + <pathelement path="${jar.dir}/chromium_base.jar" /> </classpath> </javac> </target> diff --git a/content/public/android/java/org/chromium/content/browser/CommandLine.java b/content/public/android/java/org/chromium/content/browser/CommandLine.java new file mode 100644 index 0000000..932fd25 --- /dev/null +++ b/content/public/android/java/org/chromium/content/browser/CommandLine.java @@ -0,0 +1,467 @@ +// 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.browser; + +import android.util.Log; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.concurrent.atomic.AtomicReference; + +// Java mirror of Chrome command-line utilities (e.g. class CommandLine from base/command_line.h). +// Command line program adb_command_line can be used to set the Chrome command line: +// adb shell "echo chrome --my-param > /data/local/chrome-command-line" + +// TODO(jrg), TODO(tedchoc): some of these command line options are +// specific to chrome, not content. transition them to a different +// location. + +public abstract class CommandLine { + // Switches used from Java. Please continue switch style used Chrome where options-have-hypens + // and are_not_split_with_underscores. + + // Block onCreate() of Chrome until a Java debugger is attached. + public static final String WAIT_FOR_JAVA_DEBUGGER = "wait-for-java-debugger"; + + // Disable instant? + public static final String DISABLE_INSTANT = "disable-instant"; + + // Should we force-enable the "hardware acceleration" preference? + public static final String HARDWARE_ACCELERATION = "hardware-acceleration"; + + // Give an extra account name for the x-auto-login spinner, for testing. + public static final String AUTO_LOGIN_EXTRA_ACCOUNT = "auto-login-extra-account"; + + // Enable exact match x-auto-login? Disabled for now (see + // AccountManagerContainer.java). + public static final String EXACT_MATCH_AUTO_LOGIN = "exact-match-auto-login"; + + // Should we show the NTP cache menu items? + public static final String CACHED_NTP_MENU = "cached-ntp-menu"; + + // Should we use a preload web view container for the ntp? + public static final String PRELOAD_WEBVIEW_CONTAINER = "preload-webview-container"; + + // Should thumbnail bitmaps for the thumbnail cache be full size? + public static final String FULL_SIZE_THUMBNAILS = "full-size-thumbnails"; + + // Should thumbnail bitmaps for the thumbnail cache be a compromise size? + public static final String COMPROMISE_SIZE_THUMBNAILS = "compromise-size-thumbnails"; + + // Should thumbnail bitmaps for the thumbnail cache be small (1/4 size)? + // Uses less memory and is faster but has a significant quality reduction. + public static final String SMALL_SIZE_THUMBNAILS = "small-size-thumbnails"; + + // Should thumbnail bitmaps be generated at 16-bit? 32-bit? + public static final String SIXTEEN_BIT_THUMBNAILS = "sixteen-bit-thumbnails"; + public static final String THIRTYTWO_BIT_THUMBNAILS = "thirtytwo-bit-thumbnails"; + + // How many thumbnails should we allow in the cache (per tab stack)? + public static final String THUMBNAILS = "thumbnails"; + + // How many "approximated" thumbnails should we allow in the cache + // (per tab stack)? These take very low memory but have poor quality. + public static final String APPROXIMATION_THUMBNAILS = "approximation-thumbnails"; + + // What scaling to use for the approximation? Integer. + public static final String APPROXIMATION_SCALE = "approximation-scale"; + + // Tell Java to use the official command line, loaded from the + // official-command-line.xml files. WARNING this is not done + // immediately on startup, so early running Java code will not see + // these flags. + public static final String ADD_OFFICIAL_COMMAND_LINE = "add-official-command-line"; + + // If specified, enables notification center verbose logging. + public static final String NOTIFICATION_CENTER_LOGGING = "notification-center-logging"; + + // Enables test intent handling. + public static final String ENABLE_TEST_INTENTS = "enable-test-intents"; + + // Override the system fling friction (0.015f). This can make it easier to see scrolling bugs. + public static final String FLING_FRICTION = "fling-friction"; + + // Enables StrictMode violation detection. By default this logs violations to logcat. + public static final String STRICT_MODE = "strict-mode"; + + // Enable the First Run Experience + // TODO(dtrainor): This should be removed eventually, as it is only used for development and + // debugging. + public static final String ENABLE_FIRST_RUN_EXPERIENCE = "enable-fre"; + + // Disable the First Run Experience + // TODO(dtrainor): This should be removed eventually, as it is only used for development and + // debugging. + public static final String DISABLE_FIRST_RUN_EXPERIENCE = "disable-fre"; + + // Force the First Run Experience to show. + public static final String FORCE_FIRST_RUN_EXPERIENCE = "force-fre"; + + // Default country code to be used for search engine localization. + public static final String DEFAULT_COUNTRY_CODE_AT_INSTALL = "default-country-code"; + + // Sets the max number of sandboxed service processes to use. + // Unlike renderer-process-limit, this is a hard limit on the number of + // concurrent sandboxed processes. + public static final String SANDBOXED_SERVICE_LIMIT = "sandboxed-service-limit"; + + // Mirrors of switches defined in native code. It is up to you, + // the developer, to keep these strings in sync with + // base/base_switches.h, or content/public/common/content_switches.h, or + // wherever. + + // Don't restore persistent state from saved files on startup. + public static final String NO_RESTORE_STATE = "no-restore-state"; + + // Specifies which page will be displayed on startup. + public static final String HOME_PAGE = "homepage"; + + // Dump frames-per-second to the log + public static final String LOG_FPS = "log-fps"; + + // Override the default server used for profile sync. + public static final String SYNC_URL = "sync-url"; + + // Sets the max number of render processes to use. + public static final String RENDER_PROCESS_LIMIT = "renderer-process-limit"; + + // Enable to swap the overview mode between different implementation. + // TODO(jscholler): This should be removed eventually, as it is only used for development and + // debugging. + public static final String ENABLE_OVERVIEW_SWAP = "enable-overview-swap"; + + // DO NOT ADD YOUR NEW SWITCH HERE unless it is a mirror of a native switch. Otherwise, see + // the "Switches used from Java" section above. + + // Public abstract interface, implemented in derived classes. + // All these methods reflect their native-side counterparts. + /** + * Returns true if this command line contains the given switch. + * (Switch names ARE case-sensitive). + */ + public abstract boolean hasSwitch(String switchString); + + /** + * Return the value associated with the given switch, or null. + * @param switchString The switch key to lookup. It should NOT start with '--' ! + * @return switch value, or null if the switch is not set or set to empty. + */ + public abstract String getSwitchValue(String switchString); + + /** + * Append a switch to the command line. There is no guarantee + * this action happens before the switch is needed. + * @param switchString the switch to add. It should NOT start with '--' ! + */ + public abstract void appendSwitch(String switchString); + + /** + * Append a switch and value to the command line. There is no + * guarantee this action happens before the switch is needed. + * @param switchString the switch to add. It should NOT start with '--' ! + * @param value the value for this switch. + * For example, --foo=bar becomes 'foo', 'bar'. + */ + public abstract void appendSwitchWithValue(String switchString, String value); + + /** + * Append switch/value items in "command line" format (excluding argv[0] program name). + * E.g. { '--gofast', '--username=fred' } + * @param array an array of switch or switch/value items in command line format. + * Unlike the other append routines, these switches SHOULD start with '--' . + * Unlike init(), this does not include the program name in array[0]. + */ + public abstract void appendSwitchesAndArguments(String[] array); + + /** + * Determine if the command line is bound to the native (JNI) implementation. + * @return true if the underlying implementation is delegating to the native command line. + */ + public boolean isNativeImplementation() { + return false; + } + + private static final AtomicReference<CommandLine> sCommandLine = + new AtomicReference<CommandLine>(); + + /** + * @returns true if the command line has already been initialized. + */ + public static boolean isInitialized() { + return sCommandLine.get() != null; + } + + // Equivalent to CommandLine::ForCurrentProcess in C++. + public static CommandLine getInstance() { + assert sCommandLine.get() != null; + return sCommandLine.get(); + } + + /** + * Initialize the singleton instance, must be called exactly once (either directly or + * via one of the convenience wrappers below) before using the static singleton instance. + * @param args command line flags in 'argv' format: args[0] is the program name. + */ + public static void init(String[] args) { + assert sCommandLine.get() == null; + sCommandLine.compareAndSet(null, new JavaCommandLine(args)); + } + + /** + * Initialize the command line from the command-line file. + * + * @param file The fully qualified command line file. + */ + public static void initFromFile(String file) { + char[] buffer = new char[0]; + try { + // Arbitrary clamp of 8k on the amount of file we read in. + buffer = readUtf8FileFully(file, 8 * 1024); + } catch (FileNotFoundException e) { + // Ignore: having a command line file is optional. + } catch (IOException e) { + Log.w(TAG, "error reading command line file " + file + e); + } + init(tokenizeQuotedAruments(buffer)); + } + + /** + * Resets both the java proxy and the native command lines. This allows the entire + * command line initialization to be re-run including the call to onJniLoaded. + */ + public static void reset() { + if (sCommandLine.get() != null && sCommandLine.get().isNativeImplementation()) { + nativeReset(); + } + sCommandLine.set(null); + } + + /** + * Public for testing (TODO: why are the tests in a different package?) + * Parse command line flags from a flat buffer, supporting double-quote enclosed strings + * containing whitespace. argv elements are derived by splitting the buffer on whitepace; + * double quote characters may enclose tokens containing whitespace; a double-quote literal + * may be escaped with back-slash. (Otherwise backslash is taken as a literal). + * @param buffer A command line in command line file format as described above. + * @return the tokenized arguments, suitable for passing to init(). + */ + public static String[] tokenizeQuotedAruments(char[] buffer) { + boolean inQuotes = false; + ArrayList<String> args = new ArrayList<String>(); + StringBuilder arg = null; + for (char c : buffer) { + if (c == '\"') { + if (arg != null && arg.length() > 0 && arg.charAt(arg.length() - 1) == '\\') { + // Last char was a backslash; pop it, and treat this " as a literal. + arg.setCharAt(arg.length() - 1, c); + } else { + inQuotes = !inQuotes; + } + } else if (!inQuotes && Character.isWhitespace(c)) { + if (arg != null) { + args.add(arg.toString()); + arg = null; + } + } else { + if (arg == null) arg = new StringBuilder(); + arg.append(c); + } + } + if (arg != null) { + if (inQuotes) { + Log.w(TAG, "Unterminated quoted string: " + arg); + } + args.add(arg.toString()); + } + return args.toArray(new String[args.size()]); + } + + private static final String TAG = "CommandLine"; + private static final String SWITCH_PREFIX = "--"; + private static final String SWITCH_TERMINATOR = SWITCH_PREFIX; + private static final String SWITCH_VALUE_SEPARATOR = "="; + + static void enableNativeProxy() { + // Make a best-effort to ensure we make a clean (atomic) switch over from the old to + // the new command line implementation. If another thread is modifying the command line + // when this happens, all bets are off. (As per the native CommandLine). + sCommandLine.set(new NativeCommandLine()); + } + + static String[] getJavaSwitchesOrNull() { + CommandLine commandLine = sCommandLine.get(); + if (commandLine != null) { + assert !commandLine.isNativeImplementation(); + return ((JavaCommandLine) commandLine).getCommandLineArguments(); + } + return null; + } + + /** + * @param fileName the file to read in. + * @param sizeLimit cap on the file size: will throw an exception if exceeded + * @return Array of chars read from the file + * @throws FileNotFoundException file does not exceed + * @throws IOException error encountered accessing the file + */ + private static char[] readUtf8FileFully(String fileName, int sizeLimit) throws + FileNotFoundException, IOException { + Reader reader = null; + try { + File f = new File(fileName); + if (f.length() > sizeLimit) { + throw new IOException("File " + fileName + " length " + f.length() + + " exceeds limit " + sizeLimit); + } + char[] buffer = new char[(int) f.length()]; + reader = new InputStreamReader(new FileInputStream(f), "UTF-8"); + int charsRead = reader.read(buffer); + // Debug check that we've exhausted the input stream (will fail e.g. if the + // file grew after we inspected its length). + assert !reader.ready(); + return charsRead < buffer.length ? Arrays.copyOfRange(buffer, 0, charsRead) : buffer; + } finally { + if (reader != null) reader.close(); + } + } + + private CommandLine() {} + + private static class JavaCommandLine extends CommandLine { + private HashMap<String, String> mSwitches = new HashMap<String, String>(); + private ArrayList<String> mArgs = new ArrayList<String>(); + + // The arguments begin at index 1, since index 0 contains the executable name. + private int mArgsBegin = 1; + + JavaCommandLine(String[] args) { + if (args == null || args.length == 0 || args[0] == null) { + mArgs.add(""); + } else { + mArgs.add(args[0]); + appendSwitchesInternal(args, 1); + } + // Invariant: we always have the argv[0] program name element. + assert mArgs.size() > 0; + } + + /** + * Returns the switches and arguments passed into the program, with switches and their + * values coming before all of the arguments. + */ + private String[] getCommandLineArguments() { + return mArgs.toArray(new String[mArgs.size()]); + } + + @Override + public boolean hasSwitch(String switchString) { + return mSwitches.containsKey(switchString); + } + + @Override + public String getSwitchValue(String switchString) { + // This is slightly round about, but needed for consistency with the NativeCommandLine + // version which does not distinguish empty values from key not present. + String value = mSwitches.get(switchString); + return value == null || value.isEmpty() ? null : value; + } + + @Override + public void appendSwitch(String switchString) { + appendSwitchWithValue(switchString, null); + } + + /** + * Appends a switch to the current list. + * @param switchString the switch to add. It should NOT start with '--' ! + * @param value the value for this switch. + */ + @Override + public void appendSwitchWithValue(String switchString, String value) { + mSwitches.put(switchString, value == null ? "" : value); + + // Append the switch and update the switches/arguments divider mArgsBegin. + String combinedSwitchString = SWITCH_PREFIX + switchString; + if (value != null && !value.isEmpty()) + combinedSwitchString += SWITCH_VALUE_SEPARATOR + value; + + mArgs.add(mArgsBegin++, combinedSwitchString); + } + + @Override + public void appendSwitchesAndArguments(String[] array) { + appendSwitchesInternal(array, 0); + } + + // Add the specified arguments, but skipping the first |skipCount| elements. + private void appendSwitchesInternal(String[] array, int skipCount) { + boolean parseSwitches = true; + for (String arg : array) { + if (skipCount > 0) { + --skipCount; + continue; + } + + if (arg.equals(SWITCH_TERMINATOR)) { + parseSwitches = false; + } + + if (parseSwitches && arg.startsWith(SWITCH_PREFIX)) { + String[] parts = arg.split(SWITCH_VALUE_SEPARATOR, 2); + String value = parts.length > 1 ? parts[1] : null; + appendSwitchWithValue(parts[0].substring(SWITCH_PREFIX.length()), value); + } else { + mArgs.add(arg); + } + } + } + } + + private static class NativeCommandLine extends CommandLine { + @Override + public boolean hasSwitch(String switchString) { + return nativeHasSwitch(switchString); + } + + @Override + public String getSwitchValue(String switchString) { + return nativeGetSwitchValue(switchString); + } + + @Override + public void appendSwitch(String switchString) { + nativeAppendSwitch(switchString); + } + + @Override + public void appendSwitchWithValue(String switchString, String value) { + nativeAppendSwitchWithValue(switchString, value); + } + + @Override + public void appendSwitchesAndArguments(String[] array) { + nativeAppendSwitchesAndArguments(array); + } + + @Override + public boolean isNativeImplementation() { + return true; + } + } + + private static native void nativeReset(); + private static native boolean nativeHasSwitch(String switchString); + private static native String nativeGetSwitchValue(String switchString); + private static native void nativeAppendSwitch(String switchString); + private static native void nativeAppendSwitchWithValue(String switchString, String value); + private static native void nativeAppendSwitchesAndArguments(String[] array); +}; diff --git a/content/public/android/java/org/chromium/content/browser/JNIHelper.java b/content/public/android/java/org/chromium/content/browser/JNIHelper.java new file mode 100644 index 0000000..e1d3673 --- /dev/null +++ b/content/public/android/java/org/chromium/content/browser/JNIHelper.java @@ -0,0 +1,71 @@ +// 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.browser; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.Bitmap; +import android.graphics.RectF; + +import java.util.HashSet; +import java.util.Set; + +import org.chromium.base.CalledByNative; + +class JNIHelper { + // Holder for content backing store bitmaps of all ChromeViews. Note that + // when in software mode, this will be one of the main consumers of RAM in + // the Java code. This data structure exists solely to prevent garbage + // collection and expose the allocations to the DDMS tool. The backing + // stores are actually used in C++ with weak global references. + private static final Set<Bitmap> mBitmapHolder = new HashSet<Bitmap>(); + + @CalledByNative + private static Bitmap createJavaBitmap(int w, int h, boolean keep) { + Bitmap newBitmap = + Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); + + if (keep) { + mBitmapHolder.add(newBitmap); + } + + return newBitmap; + } + + @CalledByNative + private static void deleteJavaBitmap(Bitmap b) { + mBitmapHolder.remove(b); + b.recycle(); + } + + // Statics used transiently in paintJavaBitmapToJavaBitmap, but retained to reduce + // heap churn on each call to that method. + static private Rect sSourceRect; + static private RectF sDestRect; + static private Canvas sCanvas; + static private Bitmap sNullBitmap; + static private Paint sPaint; + + @CalledByNative + public static void paintJavaBitmapToJavaBitmap( + Bitmap sourceBitmap, + int sourceX, int sourceY, int sourceBottom, int sourceRight, + Bitmap destBitmap, + float destX, float destY, float destBottom, float destRight) { + if (sCanvas == null) { + sSourceRect = new Rect(); + sDestRect = new RectF(); + sCanvas = new Canvas(); + sNullBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8); + sPaint = new Paint(Paint.FILTER_BITMAP_FLAG); + } + sSourceRect.set(sourceX, sourceY, sourceRight, sourceBottom); + sDestRect.set(destX, destY, destRight, destBottom); + sCanvas.setBitmap(destBitmap); + sCanvas.drawBitmap(sourceBitmap, sSourceRect, sDestRect, sPaint); + sCanvas.setBitmap(sNullBitmap); // To release |destBitmap| reference. + } +} diff --git a/content/public/android/java/org/chromium/content/browser/LibraryLoader.java b/content/public/android/java/org/chromium/content/browser/LibraryLoader.java new file mode 100644 index 0000000..ee9b358 --- /dev/null +++ b/content/public/android/java/org/chromium/content/browser/LibraryLoader.java @@ -0,0 +1,239 @@ +// 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.browser; + +import android.os.AsyncTask; +import android.os.Handler; +import android.text.TextUtils; +import android.util.Log; + +// This class provides functionality to: +// - synchronously load and register the native library. This is used by callers +// that can't do anything useful without the native side. +// - asynchronously load and register the native library. This is used by callers +// that can do more work in the java-side, and let a separate thread do all the +// file IO and library loading. +public class LibraryLoader { + private static final String TAG = "LibraryLoader"; + + /* TODO(jrg): resolve up and downstream discrepancy; there is no + * upstream libchromeview.so */ + private static String sLibrary = "chromeview"; + + private static boolean sLoaded = false; + + private static boolean sInitialized = false; + + private static AsyncTask<Void, Void, Boolean> sAsyncLoader; + + /** + * Callback for handling loading of the native library. + * + * <p> The callback methods will always be triggered on the UI thread. + */ + public static interface Callback { + /** + * Called when loading the native library is successful. + */ + void onSuccess(); + + /** + * Called when loading the native library fails. + */ + void onFailure(); + } + + /** + * Sets the library name that is to be loaded. This must be called prior to the library being + * loaded the first time. + * + * @param library The name of the library to be loaded (without the lib prefix). + */ + public static void setLibraryToLoad(String library) { + if (TextUtils.equals(sLibrary, library)) return; + + assert !sLoaded : "Setting the library must happen before load is called."; + sLibrary = library; + } + + /** + * This method blocks until the library is fully loaded and initialized; + * must be called on the thread that the native will call its "main" thread. + */ + public static void loadAndInitSync() { + checkThreadUsage(); + if (sInitialized) { + // Already initialized, nothing to do. + return; + } + if (sAsyncLoader != null) { + // Async initialization in progress, wait. + waitForAsyncInitialized(); + return; + } + loadNow(); + initializeOnMainThread(); + } + + /** + * Block until the library is fully initialized. + * Must be called on the thread that the native will call its "main" thread. + */ + private static void waitForAsyncInitialized() { + checkThreadUsage(); + if (sInitialized) { + // Already initialized. + return; + } + synchronized(LibraryLoader.class) { + try { + while (!sLoaded) { + LibraryLoader.class.wait(); + } + // If the UI thread blocked waiting for the task it will already + // have handled the library load completion, so don't duplicate that work here. + } catch (InterruptedException e) { + } + } + initializeOnMainThread(); + } + + /** + * Kicks off an asynchronous library load, and will asynchronously initialize the + * library when that completes. + * Must be called on the thread that the native will call its "main" thread. + */ + public static void loadAndInitAsync(final Callback onLoadedListener) { + checkThreadUsage(); + if (sInitialized) { + // Already initialized, post our Runnable if needed. + if (onLoadedListener != null) { + new Handler().post(new Runnable() { + @Override + public void run() { + onLoadedListener.onSuccess(); + } + }); + } + return; + } + sAsyncLoader = new AsyncTask<Void, Void, Boolean>() { + @Override + public Boolean doInBackground(Void... voids) { + // We're loading the .so in a background thread. Potentially, this + // can break native code that relies on static initializers using + // thread local storage, as the library would normally load in the + // main thread. If do we hit such cases we should remove those static + // initializers, as we chrome has banned them. + // (Worst case, we can go back to just warming up the file in the system + // cache here and do the actual loading in onPostExecute().) + return loadNow(); + } + + @Override + protected void onPostExecute(Boolean result) { + if (result) { + initializeOnMainThread(); + if (onLoadedListener != null) onLoadedListener.onSuccess(); + } else { + if (onLoadedListener != null) onLoadedListener.onFailure(); + } + + } + }; + sAsyncLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + /** + * @throws UnsatisfiedLinkError if the library is not yet initialized. + */ + public static void checkIsReady() { + if (!sInitialized) { + throw new UnsatisfiedLinkError(sLibrary + " is not initialized"); + } + } + + /** + * Loads the library and blocks until the load completes. The caller is responsible + * for subsequently calling initialize(). + * May be called on any thread, but should only be called once. Note the thread + * this is called on will be the thread that runs the native code's static initializers. + * See the comment in doInBackground() for more considerations on this. + * + * @return Whether the native library was successfully loaded. + */ + static boolean loadNow() { + assert !sInitialized; + try { + Log.i(TAG, "loading: " + sLibrary); + System.loadLibrary(sLibrary); + Log.i(TAG, "loaded: " + sLibrary); + synchronized(LibraryLoader.class) { + sLoaded = true; + LibraryLoader.class.notifyAll(); + } + } catch (UnsatisfiedLinkError e) { + Log.e(TAG, "error loading: " + sLibrary, e); + return false; + } + return true; + } + + /** + * initializes the library here and now: must be called on the thread that the + * native will call its "main" thread. The library must have previously been + * loaded with loadNow. + * @param initCommandLine The command line arguments that native command line will + * be initialized with. + */ + static void initializeOnMainThread(String[] initCommandLine) { + checkThreadUsage(); + if (sInitialized) { + return; + } + if (!nativeLibraryLoadedOnMainThread(initCommandLine)) { + Log.e(TAG, "error calling nativeLibraryLoadedOnMainThread"); + throw new UnsatisfiedLinkError(); + } + // From this point on, native code is ready to use and checkIsReady() + // shouldn't complain from now on (and in fact, it's used by the + // following calls). + sInitialized = true; + CommandLine.enableNativeProxy(); + TraceEvent.setEnabledToMatchNative(); + } + + static private void initializeOnMainThread() { + checkThreadUsage(); + if (!sInitialized) { + initializeOnMainThread(CommandLine.getJavaSwitchesOrNull()); + } + } + + private LibraryLoader() { + } + + // The public API of this class is meant to be used from a single + // thread. Internally, we may bounce to a separate thread to actually + // load the library. + private static Thread sMyThread; + private static void checkThreadUsage() { + Thread currentThread = java.lang.Thread.currentThread(); + if (sMyThread == null) { + sMyThread = currentThread; + } else { + if (sMyThread != currentThread) { + Log.e(TAG, "Threading violation detected. My thread=" + sMyThread + + " but I'm being accessed from thread=" + currentThread); + assert false; + } + } + } + + // This is the only method that is registered during System.loadLibrary, as it + // happens on a different thread. We then call it on the main thread to register + // everything else. + private static native boolean nativeLibraryLoadedOnMainThread(String[] initCommandLine); +} diff --git a/content/public/android/java/org/chromium/content/browser/TraceEvent.java b/content/public/android/java/org/chromium/content/browser/TraceEvent.java new file mode 100644 index 0000000..b1ad95f --- /dev/null +++ b/content/public/android/java/org/chromium/content/browser/TraceEvent.java @@ -0,0 +1,143 @@ +// 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.browser; + +import android.os.Looper; +import android.util.Printer; + +// Java mirror of Chrome trace event API. See +// base/debug/trace_event.h. Unlike the native version, Java does not +// have stack objects, so a TRACE_EVENT() which does both +// TRACE_EVENT_BEGIN() and TRACE_EVENT_END() in ctor/dtor is not +// possible. +// It is OK to use tracing before the native library has loaded, but such traces will +// be ignored. (Perhaps we could devise to buffer them up in future?). +public class TraceEvent { + + private static boolean sEnabled = false; + + private static class LooperTracePrinter implements Printer { + private static final String NAME = "Looper.dispatchMessage"; + @Override + public void println(String line) { + if (line.startsWith(">>>>>")) { + TraceEvent.begin(NAME, line); + } else { + assert line.startsWith("<<<<<"); + TraceEvent.end(NAME); + } + } + } + + /** + * Calling this will cause enabled() to be updated to match that set on the native side. + * The native library must be loaded before calling this method. + */ + public static void setEnabledToMatchNative() { + setEnabled(nativeTraceEnabled()); + } + + /** + * Enables or disables tracing. + * The native library must be loaded before the first call with enabled == true. + */ + public static synchronized void setEnabled(boolean enabled) { + if (enabled) { + LibraryLoader.checkIsReady(); + } + if (sEnabled == enabled) { + return; + } + sEnabled = enabled; + Looper.getMainLooper().setMessageLogging(enabled ? new LooperTracePrinter() : null); + } + + /** + * @return True if tracing is enabled, false otherwise. + * It is safe to call trace methods without checking if TraceEvent + * is enabled. + */ + public static boolean enabled() { + return sEnabled; + } + + public static void instant(String name) { + if (sEnabled) { + nativeInstant(name, null); + } + } + + public static void instant(String name, String arg) { + if (sEnabled) { + nativeInstant(name, arg); + } + } + + /** + * Convenience wrapper around the versions of begin() that take string parameters. + * The name of the event will be derived from the class and function name that call this. + * IMPORTANT: if using this version, ensure end() (no parameters) is always called from the + * same calling context. + */ + public static void begin() { + if (sEnabled) { + nativeBegin(getCallerName(), null); + } + } + + public static void begin(String name) { + if (sEnabled) { + nativeBegin(name, null); + } + } + + public static void begin(String name, String arg) { + if (sEnabled) { + nativeBegin(name, arg); + } + } + + /** + * Convenience wrapper around the versions of end() that take string parameters. See begin() + * for more information. + */ + public static void end() { + if (sEnabled) { + nativeEnd(getCallerName(), null); + } + } + + public static void end(String name) { + if (sEnabled) { + nativeEnd(name, null); + } + } + + public static void end(String name, String arg) { + if (sEnabled) { + nativeEnd(name, arg); + } + } + + private static String getCallerName() { + // This was measured to take about 1ms on Trygon device. + StackTraceElement[] stack = java.lang.Thread.currentThread().getStackTrace(); + + // Commented out to avoid excess call overhead, but these lines can be useful to debug + // exactly where the TraceEvent's client is on the callstack. + // int index = 0; + // while (!stack[index].getClassName().equals(TraceEvent.class.getName())) ++index; + // while (stack[index].getClassName().equals(TraceEvent.class.getName())) ++index; + // System.logW("TraceEvent caller is at stack index " + index); + + // '4' Was derived using the above commented out code snippet. + return stack[4].getClassName() + "." + stack[4].getMethodName(); + } + + private static native boolean nativeTraceEnabled(); + private static native void nativeInstant(String name, String arg); + private static native void nativeBegin(String name, String arg); + private static native void nativeEnd(String name, String arg); +} diff --git a/content/shell/android/content_shell_apk.xml b/content/shell/android/content_shell_apk.xml index 3e89773b..003380b 100644 --- a/content/shell/android/content_shell_apk.xml +++ b/content/shell/android/content_shell_apk.xml @@ -23,6 +23,13 @@ <fileset file="${toolchain.dir}/../../gdbserver"/> </path> <property name="native.libs.absolute.dir" location="${out.dir}/libs" /> + <!-- If your app crashes when referencing java from a different .jar + (e.g. java.lang.NoClassDefFoundError: + org.chromium.content.browser.CommandLine), And if + chromium_content.jar is in fact in your java/libs directory (as + placed by a gyp 'cp'), the following line will make sure it + gets added to the classes.dex file. --> + <property name="out.classes.absolute.dir" location="${jar.libs.dir}" /> <!-- We expect PRODUCT_DIR to be set like the gyp var (e.g. $ROOT/out/Debug) --> diff --git a/content/shell/android/java/org/chromium/content_shell/ContentShellActivity.java b/content/shell/android/java/org/chromium/content_shell/ContentShellActivity.java index a3a1028..ce6ca88 100644 --- a/content/shell/android/java/org/chromium/content_shell/ContentShellActivity.java +++ b/content/shell/android/java/org/chromium/content_shell/ContentShellActivity.java @@ -7,11 +7,14 @@ package org.chromium.content_shell; import android.app.Activity; import android.content.Intent; import android.os.Bundle; +import android.os.Debug; import android.text.TextUtils; import android.util.Log; import android.view.KeyEvent; +import org.chromium.content.browser.CommandLine; import org.chromium.content.browser.ContentView; +import org.chromium.content.browser.LibraryLoader; /** * Activity for managing the Content Shell. @@ -20,6 +23,7 @@ public class ContentShellActivity extends Activity { private static final String COMMAND_LINE_FILE = "/data/local/content-shell-command-line"; private static final String TAG = "ContentShellActivity"; + private static final String NATIVE_LIBRARY = "content_shell_content_view"; private ShellManager mShellManager; @@ -28,17 +32,17 @@ public class ContentShellActivity extends Activity { super.onCreate(savedInstanceState); // Initializing the command line must occur before loading the library. - // TODO(tedchoc): Initialize command line from file. + CommandLine.initFromFile(COMMAND_LINE_FILE); String startupUrl = getUrlFromIntent(getIntent()); if (!TextUtils.isEmpty(startupUrl)) { - // TODO(tedchoc): Append URL to command line. + CommandLine.getInstance().appendSwitchesAndArguments( + new String[] {ShellView.sanitizeUrl(startupUrl)}); } - // TODO(jrg,tedchoc): upstream the async library loader, then - // make this call look like this: - // LibraryLoader.loadAndInitSync(); - loadNativeLibrary(); - + // TODO(jrg): once command line support is addef (for + // --wait-for-debugger), remove this. + // Debug.waitForDebugger(); + LibraryLoader.loadAndInitSync(); initializeContentViewResources(); setContentView(R.layout.content_shell_activity); @@ -93,18 +97,4 @@ public class ContentShellActivity extends Activity { ContentView.registerPopupOverlayCornerRadius(0); ContentView.registerPopupOverlayResourceId(R.drawable.popup_zoomer_overlay); } - - - private static final String NATIVE_LIBRARY = "content_shell_content_view"; - - private void loadNativeLibrary() throws UnsatisfiedLinkError { - Log.i(TAG, "loading: " + NATIVE_LIBRARY); - try { - System.loadLibrary(NATIVE_LIBRARY); - } catch (UnsatisfiedLinkError e) { - Log.e(TAG, "Unable to load lib" + NATIVE_LIBRARY + ".so: " + e); - throw e; - } - Log.i(TAG, "loaded: " + NATIVE_LIBRARY); - } } diff --git a/content/shell/android/java/org/chromium/content_shell/ContentShellApplication.java b/content/shell/android/java/org/chromium/content_shell/ContentShellApplication.java index 9f2a0de..56b4cf5 100644 --- a/content/shell/android/java/org/chromium/content_shell/ContentShellApplication.java +++ b/content/shell/android/java/org/chromium/content_shell/ContentShellApplication.java @@ -5,16 +5,22 @@ package org.chromium.content_shell; import android.app.Application; +import org.chromium.content.browser.LibraryLoader; /** * Entry point for the content shell application. Handles initialization of information that needs * to be shared across the main activity and the sandbox services created. */ public class ContentShellApplication extends Application { + + // TODO(jrg): do not downstream this filename! + private static final String NATIVE_LIBRARY = "content_shell_content_view"; + @Override public void onCreate() { super.onCreate(); - // TODO(tedchoc): Initialize the .pak files to load and the native library name. + // TODO(tedchoc): Initialize the .pak files to load + LibraryLoader.setLibraryToLoad(NATIVE_LIBRARY); } } diff --git a/content/shell/android/shell_library_loader.cc b/content/shell/android/shell_library_loader.cc index e447d7e7..3b6757e 100644 --- a/content/shell/android/shell_library_loader.cc +++ b/content/shell/android/shell_library_loader.cc @@ -3,8 +3,11 @@ // found in the LICENSE file. #include "base/basictypes.h" +#include "base/debug/debugger.h" +#include "base/logging.h" #include "base/android/jni_android.h" #include "base/android/jni_registrar.h" +#include "content/public/app/content_main_runner.h" #include "content/public/browser/android_library_loader_hooks.h" #include "content/shell/shell_main_delegate.h" #include "content/shell/android/shell_manager.h" @@ -15,8 +18,32 @@ static base::android::RegistrationMethod kRegistrationMethods[] = { { "ShellView", content::ShellView::Register }, }; +namespace { + content::ContentMainRunner* g_content_main_runner = NULL; +} + // This is called by the VM when the shared library is first loaded. JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { + + // Don't call anything in base without initializing it. + // ContentMainRunner will do what we need. + g_content_main_runner = content::ContentMainRunner::Create(); + + // TODO(tedchoc): Set this to the main delegate once the Android specific + // browser process initialization gets checked in. + ShellMainDelegate* delegate = new ShellMainDelegate(); + + // We use a ShellContentClient, created as a member of + // ShellMainDelegate and set with a call to + // content::SetContentClient() in PreSandboxStartup(). + // That must be done before ContentMainRunner::Initialize(). + // TODO(jrg): resolve the upstream/downstream discrepancy; we + // shouldn't need to do this. + delegate->PreSandboxStartup(); + + // TODO(jrg): find command line info from java; pass down in here. + g_content_main_runner->Initialize(0, NULL, NULL); + base::android::InitVM(vm); JNIEnv* env = base::android::AttachCurrentThread(); if (!RegisterLibraryLoaderEntryHook(env)) { @@ -29,9 +56,12 @@ JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { arraysize(kRegistrationMethods))) return -1; - // TODO(tedchoc): Set this to the main delegate once the Android specific - // browser process initialization gets checked in. - new ShellMainDelegate(); - return JNI_VERSION_1_4; } + + +JNI_EXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) { + delete g_content_main_runner; + g_content_main_runner = NULL; +} + diff --git a/media/base/android/java/media.xml b/media/base/android/java/media.xml index 28a8ef1..8516e3e 100644 --- a/media/base/android/java/media.xml +++ b/media/base/android/java/media.xml @@ -14,6 +14,8 @@ <property name="src" location="."/> <property name="build" location="build"/> <property name="dist" location="dist"/> + <property name="out.dir" location="${PRODUCT_DIR}/lib.java"/> + <property name="dest.dir" location="${PRODUCT_DIR}/java/media"/> <condition property="location.base" value="${sdk.dir}" @@ -25,15 +27,17 @@ <!-- Create the time stamp --> <tstamp/> <!-- Create the build directory structure used by compile --> - <mkdir dir="${build}"/> + <mkdir dir="${out.dir}"/> + <mkdir dir="${dest.dir}"/> </target> <target name="compile" depends="init" description="compile the source " > - <!-- Compile the java code from ${src} into ${build} --> - <javac srcdir="${src}" destdir="${build}"> + <!-- Compile the java code from ${src} into ${dest.dir} --> + <javac srcdir="${src}" destdir="${dest.dir}"> <classpath> - <path location="${location.base}/android.jar"/> + <pathelement path="${location.base}/android.jar" /> + <pathelement path="${out.dir}/chromium_base.jar" /> </classpath> </javac> </target> @@ -44,13 +48,13 @@ <mkdir dir="${dist}/lib"/> <!-- Put everything in ${build} into the chromium_media.jar file --> - <jar jarfile="${dist}/lib/chromium_media.jar" basedir="${build}"/> + <jar jarfile="${out.dir}/chromium_media.jar" basedir="${dest.dir}"/> </target> <target name="clean" description="clean up" > - <!-- Delete the ${build} and ${dist} directory trees --> - <delete dir="${build}"/> + <!-- Delete the appropriate directory trees --> + <delete dir="${dest.dir}"/> <delete dir="${dist}"/> </target> </project> diff --git a/media/media.gyp b/media/media.gyp index 619c188..65fd702 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -939,6 +939,7 @@ ], 'dependencies': [ '../base/base.gyp:base', + 'media_java', ], 'include_dirs': [ '<(SHARED_INTERMEDIATE_DIR)/media', @@ -963,6 +964,17 @@ }, ], }, + { + 'target_name': 'media_java', + 'type': 'none', + 'dependencies': [ '../base/base.gyp:base_java' ], + 'variables': { + 'package_name': 'media', + 'java_in_dir': 'base/android/java', + }, + 'includes': [ '../build/java.gypi' ], + }, + ], }, { # OS != "android"' # Android does not use ffmpeg, so disable the targets which require it. diff --git a/net/android/java/net.xml b/net/android/java/net.xml index 76dddcc..29c9b02 100644 --- a/net/android/java/net.xml +++ b/net/android/java/net.xml @@ -38,7 +38,7 @@ <javac srcdir="${src}" destdir="${dest.dir}"> <classpath> <path location="${location.base}/android.jar"/> - <path location="${PRODUCT_DIR}/chromium_base.jar"/> + <path location="${out.dir}/chromium_base.jar"/> </classpath> </javac> </target> |