Start with media/mp4, media/webm and base codes from Chromium.

This commit is contained in:
Kongqun Yang 2013-09-23 18:35:40 -07:00
parent 1205b85f3e
commit 0f24c7f9ac
3376 changed files with 777539 additions and 1 deletions

15
base/DEPS Normal file
View File

@ -0,0 +1,15 @@
include_rules = [
"+jni",
"+native_client/src/untrusted/irt/irt.h",
"+third_party/ashmem",
"+third_party/apple_apsl",
"+third_party/libevent",
"+third_party/dmg_fp",
"+third_party/GTM",
"+third_party/mach_override",
"+third_party/modp_b64",
"+third_party/tcmalloc",
# ICU dependendencies must be separate from the rest of base.
"-i18n",
]

29
base/OWNERS Normal file
View File

@ -0,0 +1,29 @@
mark@chromium.org
darin@chromium.org
brettw@chromium.org
willchan@chromium.org
jar@chromium.org
per-file *.isolate=csharp@chromium.org
per-file *.isolate=maruel@chromium.org
per-file bind.h=ajwong@chromium.org
per-file bind_helpers.cc=ajwong@chromium.org
per-file bind_helpers.h=ajwong@chromium.org
per-file bind_helpers_unittest.cc=ajwong@chromium.org
per-file bind.h.pump=ajwong@chromium.org
per-file bind_internal.h=ajwong@chromium.org
per-file bind_internal.h.pump=ajwong@chromium.org
per-file bind_internal_win.h=ajwong@chromium.org
per-file bind_internal_win.h.pump=ajwong@chromium.org
per-file bind_unittest.cc=ajwong@chromium.org
per-file bind_unittest.nc=ajwong@chromium.org
per-file callback_forward.h=ajwong@chromium.org
per-file callback.h=ajwong@chromium.org
per-file callback_helpers.h=ajwong@chromium.org
per-file callback.h.pump=ajwong@chromium.org
per-file callback_internal.cc=ajwong@chromium.org
per-file callback_internal.h=ajwong@chromium.org
per-file callback_unittest.cc=ajwong@chromium.org
per-file callback_unittest.h=ajwong@chromium.org
per-file callback_unittest.nc=ajwong@chromium.org
per-file security_unittest.cc=jln@chromium.org

56
base/PRESUBMIT.py Normal file
View File

@ -0,0 +1,56 @@
# 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.
"""Chromium presubmit script for src/base.
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details on the presubmit API built into gcl.
"""
def _CheckNoInterfacesInBase(input_api, output_api):
"""Checks to make sure no files in libbase.a have |@interface|."""
pattern = input_api.re.compile(r'^\s*@interface', input_api.re.MULTILINE)
files = []
for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
if (f.LocalPath().startswith('base/') and
not "/test/" in f.LocalPath() and
not f.LocalPath().endswith('_unittest.mm') and
not f.LocalPath().endswith('mac/sdk_forward_declarations.h')):
contents = input_api.ReadFile(f)
if pattern.search(contents):
files.append(f)
if len(files):
return [ output_api.PresubmitError(
'Objective-C interfaces or categories are forbidden in libbase. ' +
'See http://groups.google.com/a/chromium.org/group/chromium-dev/' +
'browse_thread/thread/efb28c10435987fd',
files) ]
return []
def _CommonChecks(input_api, output_api):
"""Checks common to both upload and commit."""
results = []
results.extend(_CheckNoInterfacesInBase(input_api, output_api))
return results
def CheckChangeOnUpload(input_api, output_api):
results = []
results.extend(_CommonChecks(input_api, output_api))
return results
def CheckChangeOnCommit(input_api, output_api):
results = []
results.extend(_CommonChecks(input_api, output_api))
return results
def GetPreferredTrySlaves():
return [
'linux_rel:sync_integration_tests',
'mac_rel:sync_integration_tests',
'win_rel:sync_integration_tests',
]

59
base/allocator/README Normal file
View File

@ -0,0 +1,59 @@
Notes about the Chrome memory allocator.
Background
----------
We use this library as a generic way to fork into any of several allocators.
Currently we can, at runtime, switch between:
the default windows allocator
the windows low-fragmentation-heap
tcmalloc
jemalloc (the heap used most notably within Mozilla Firefox)
The mechanism for hooking LIBCMT in windows is rather tricky. The core
problem is that by default, the windows library does not declare malloc and
free as weak symbols. Because of this, they cannot be overriden. To work
around this, we start with the LIBCMT.LIB, and manually remove all allocator
related functions from it using the visual studio library tool. Once removed,
we can now link against the library and provide custom versions of the
allocator related functionality.
Source code
-----------
This directory contains just the allocator (i.e. shim) layer that switches
between the different underlying memory allocation implementations.
The tcmalloc and jemalloc libraries originate outside of Chromium
and exist in ../../third_party/tcmalloc and ../../third_party/jemalloc
(currently, the actual locations are defined in the allocator.gyp file).
The third party sources use a vendor-branch SCM pattern to track
Chromium-specific changes independently from upstream changes.
The general intent is to push local changes upstream so that over
time we no longer need any forked files.
Adding a new allocator
----------------------
Adding a new allocator requires definition of the following five functions:
extern "C" {
bool init();
void* malloc(size_t s);
void* realloc(void* p, size_t s);
void free(void* s);
size_t msize(void* p);
}
All other allocation related functions (new/delete/calloc/etc) have been
implemented generically to work across all allocators.
Usage
-----
You can use the different allocators by setting the environment variable
CHROME_ALLOCATOR to:
"tcmalloc" - TC Malloc (default)
"jemalloc" - JE Malloc
"winheap" - Windows default heap
"winlfh" - Windows Low-Fragmentation heap

View File

@ -0,0 +1,676 @@
# 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.
{
'variables': {
'jemalloc_dir': '../../third_party/jemalloc/chromium',
'tcmalloc_dir': '../../third_party/tcmalloc/chromium',
'use_vtable_verify%': 0,
},
'targets': [
# Only executables and not libraries should depend on the
# allocator target; only the application (the final executable)
# knows what allocator makes sense.
{
'target_name': 'allocator',
'type': 'static_library',
# Make sure the allocation library is optimized to
# the hilt in official builds.
'variables': {
'optimize': 'max',
},
'include_dirs': [
'.',
'<(tcmalloc_dir)/src/base',
'<(tcmalloc_dir)/src',
'../..',
],
'direct_dependent_settings': {
'configurations': {
'Common_Base': {
'msvs_settings': {
'VCLinkerTool': {
'IgnoreDefaultLibraryNames': ['libcmtd.lib', 'libcmt.lib'],
'AdditionalDependencies': [
'<(SHARED_INTERMEDIATE_DIR)/allocator/libcmt.lib'
],
},
},
},
},
'conditions': [
['OS=="win"', {
'defines': [
'PERFTOOLS_DLL_DECL=',
],
}],
],
},
'sources': [
# Generated for our configuration from tcmalloc's build
# and checked in.
'<(tcmalloc_dir)/src/config.h',
'<(tcmalloc_dir)/src/config_android.h',
'<(tcmalloc_dir)/src/config_linux.h',
'<(tcmalloc_dir)/src/config_win.h',
# all tcmalloc native and forked files
'<(tcmalloc_dir)/src/addressmap-inl.h',
'<(tcmalloc_dir)/src/base/abort.cc',
'<(tcmalloc_dir)/src/base/abort.h',
'<(tcmalloc_dir)/src/base/arm_instruction_set_select.h',
'<(tcmalloc_dir)/src/base/atomicops-internals-linuxppc.h',
'<(tcmalloc_dir)/src/base/atomicops-internals-arm-generic.h',
'<(tcmalloc_dir)/src/base/atomicops-internals-arm-v6plus.h',
'<(tcmalloc_dir)/src/base/atomicops-internals-macosx.h',
'<(tcmalloc_dir)/src/base/atomicops-internals-windows.h',
'<(tcmalloc_dir)/src/base/atomicops-internals-x86.cc',
'<(tcmalloc_dir)/src/base/atomicops-internals-x86.h',
'<(tcmalloc_dir)/src/base/atomicops.h',
'<(tcmalloc_dir)/src/base/basictypes.h',
'<(tcmalloc_dir)/src/base/commandlineflags.h',
'<(tcmalloc_dir)/src/base/cycleclock.h',
# We don't list dynamic_annotations.c since its copy is already
# present in the dynamic_annotations target.
'<(tcmalloc_dir)/src/base/dynamic_annotations.h',
'<(tcmalloc_dir)/src/base/elf_mem_image.cc',
'<(tcmalloc_dir)/src/base/elf_mem_image.h',
'<(tcmalloc_dir)/src/base/elfcore.h',
'<(tcmalloc_dir)/src/base/googleinit.h',
'<(tcmalloc_dir)/src/base/linux_syscall_support.h',
'<(tcmalloc_dir)/src/base/linuxthreads.cc',
'<(tcmalloc_dir)/src/base/linuxthreads.h',
'<(tcmalloc_dir)/src/base/logging.cc',
'<(tcmalloc_dir)/src/base/logging.h',
'<(tcmalloc_dir)/src/base/low_level_alloc.cc',
'<(tcmalloc_dir)/src/base/low_level_alloc.h',
'<(tcmalloc_dir)/src/base/simple_mutex.h',
'<(tcmalloc_dir)/src/base/spinlock.cc',
'<(tcmalloc_dir)/src/base/spinlock.h',
'<(tcmalloc_dir)/src/base/spinlock_internal.cc',
'<(tcmalloc_dir)/src/base/spinlock_internal.h',
'<(tcmalloc_dir)/src/base/spinlock_linux-inl.h',
'<(tcmalloc_dir)/src/base/spinlock_posix-inl.h',
'<(tcmalloc_dir)/src/base/spinlock_win32-inl.h',
'<(tcmalloc_dir)/src/base/stl_allocator.h',
'<(tcmalloc_dir)/src/base/synchronization_profiling.h',
'<(tcmalloc_dir)/src/base/sysinfo.cc',
'<(tcmalloc_dir)/src/base/sysinfo.h',
'<(tcmalloc_dir)/src/base/thread_annotations.h',
'<(tcmalloc_dir)/src/base/thread_lister.c',
'<(tcmalloc_dir)/src/base/thread_lister.h',
'<(tcmalloc_dir)/src/base/vdso_support.cc',
'<(tcmalloc_dir)/src/base/vdso_support.h',
'<(tcmalloc_dir)/src/central_freelist.cc',
'<(tcmalloc_dir)/src/central_freelist.h',
'<(tcmalloc_dir)/src/common.cc',
'<(tcmalloc_dir)/src/common.h',
'<(tcmalloc_dir)/src/debugallocation.cc',
'<(tcmalloc_dir)/src/deep-heap-profile.cc',
'<(tcmalloc_dir)/src/deep-heap-profile.h',
'<(tcmalloc_dir)/src/free_list.cc',
'<(tcmalloc_dir)/src/free_list.h',
'<(tcmalloc_dir)/src/getpc.h',
'<(tcmalloc_dir)/src/gperftools/heap-checker.h',
'<(tcmalloc_dir)/src/gperftools/heap-profiler.h',
'<(tcmalloc_dir)/src/gperftools/malloc_extension.h',
'<(tcmalloc_dir)/src/gperftools/malloc_extension_c.h',
'<(tcmalloc_dir)/src/gperftools/malloc_hook.h',
'<(tcmalloc_dir)/src/gperftools/malloc_hook_c.h',
'<(tcmalloc_dir)/src/gperftools/profiler.h',
'<(tcmalloc_dir)/src/gperftools/stacktrace.h',
'<(tcmalloc_dir)/src/gperftools/tcmalloc.h',
'<(tcmalloc_dir)/src/heap-checker-bcad.cc',
'<(tcmalloc_dir)/src/heap-checker.cc',
'<(tcmalloc_dir)/src/heap-profile-table.cc',
'<(tcmalloc_dir)/src/heap-profile-table.h',
'<(tcmalloc_dir)/src/heap-profiler.cc',
'<(tcmalloc_dir)/src/internal_logging.cc',
'<(tcmalloc_dir)/src/internal_logging.h',
'<(tcmalloc_dir)/src/libc_override.h',
'<(tcmalloc_dir)/src/libc_override_gcc_and_weak.h',
'<(tcmalloc_dir)/src/libc_override_glibc.h',
'<(tcmalloc_dir)/src/libc_override_osx.h',
'<(tcmalloc_dir)/src/libc_override_redefine.h',
'<(tcmalloc_dir)/src/linked_list.h',
'<(tcmalloc_dir)/src/malloc_extension.cc',
'<(tcmalloc_dir)/src/malloc_hook-inl.h',
'<(tcmalloc_dir)/src/malloc_hook.cc',
'<(tcmalloc_dir)/src/malloc_hook_mmap_freebsd.h',
'<(tcmalloc_dir)/src/malloc_hook_mmap_linux.h',
'<(tcmalloc_dir)/src/maybe_threads.cc',
'<(tcmalloc_dir)/src/maybe_threads.h',
'<(tcmalloc_dir)/src/memfs_malloc.cc',
'<(tcmalloc_dir)/src/memory_region_map.cc',
'<(tcmalloc_dir)/src/memory_region_map.h',
'<(tcmalloc_dir)/src/packed-cache-inl.h',
'<(tcmalloc_dir)/src/page_heap.cc',
'<(tcmalloc_dir)/src/page_heap.h',
'<(tcmalloc_dir)/src/page_heap_allocator.h',
'<(tcmalloc_dir)/src/pagemap.h',
'<(tcmalloc_dir)/src/profile-handler.cc',
'<(tcmalloc_dir)/src/profile-handler.h',
'<(tcmalloc_dir)/src/profiledata.cc',
'<(tcmalloc_dir)/src/profiledata.h',
'<(tcmalloc_dir)/src/profiler.cc',
'<(tcmalloc_dir)/src/raw_printer.cc',
'<(tcmalloc_dir)/src/raw_printer.h',
'<(tcmalloc_dir)/src/sampler.cc',
'<(tcmalloc_dir)/src/sampler.h',
'<(tcmalloc_dir)/src/span.cc',
'<(tcmalloc_dir)/src/span.h',
'<(tcmalloc_dir)/src/stack_trace_table.cc',
'<(tcmalloc_dir)/src/stack_trace_table.h',
'<(tcmalloc_dir)/src/stacktrace.cc',
'<(tcmalloc_dir)/src/stacktrace_arm-inl.h',
'<(tcmalloc_dir)/src/stacktrace_config.h',
'<(tcmalloc_dir)/src/stacktrace_generic-inl.h',
'<(tcmalloc_dir)/src/stacktrace_libunwind-inl.h',
'<(tcmalloc_dir)/src/stacktrace_powerpc-inl.h',
'<(tcmalloc_dir)/src/stacktrace_win32-inl.h',
'<(tcmalloc_dir)/src/stacktrace_with_context.cc',
'<(tcmalloc_dir)/src/stacktrace_x86-inl.h',
'<(tcmalloc_dir)/src/static_vars.cc',
'<(tcmalloc_dir)/src/static_vars.h',
'<(tcmalloc_dir)/src/symbolize.cc',
'<(tcmalloc_dir)/src/symbolize.h',
'<(tcmalloc_dir)/src/system-alloc.cc',
'<(tcmalloc_dir)/src/system-alloc.h',
'<(tcmalloc_dir)/src/tcmalloc.cc',
'<(tcmalloc_dir)/src/tcmalloc_guard.h',
'<(tcmalloc_dir)/src/thread_cache.cc',
'<(tcmalloc_dir)/src/thread_cache.h',
'<(tcmalloc_dir)/src/windows/config.h',
'<(tcmalloc_dir)/src/windows/get_mangled_names.cc',
'<(tcmalloc_dir)/src/windows/gperftools/tcmalloc.h',
'<(tcmalloc_dir)/src/windows/ia32_modrm_map.cc',
'<(tcmalloc_dir)/src/windows/ia32_opcode_map.cc',
'<(tcmalloc_dir)/src/windows/mingw.h',
'<(tcmalloc_dir)/src/windows/mini_disassembler.cc',
'<(tcmalloc_dir)/src/windows/mini_disassembler.h',
'<(tcmalloc_dir)/src/windows/mini_disassembler_types.h',
'<(tcmalloc_dir)/src/windows/override_functions.cc',
'<(tcmalloc_dir)/src/windows/patch_functions.cc',
'<(tcmalloc_dir)/src/windows/port.cc',
'<(tcmalloc_dir)/src/windows/port.h',
'<(tcmalloc_dir)/src/windows/preamble_patcher.cc',
'<(tcmalloc_dir)/src/windows/preamble_patcher.h',
'<(tcmalloc_dir)/src/windows/preamble_patcher_with_stub.cc',
# jemalloc files
'<(jemalloc_dir)/jemalloc.c',
'<(jemalloc_dir)/jemalloc.h',
'<(jemalloc_dir)/ql.h',
'<(jemalloc_dir)/qr.h',
'<(jemalloc_dir)/rb.h',
'allocator_shim.cc',
'allocator_shim.h',
'debugallocation_shim.cc',
'generic_allocators.cc',
'win_allocator.cc',
],
# sources! means that these are not compiled directly.
'sources!': [
# Included by allocator_shim.cc for maximal inlining.
'generic_allocators.cc',
'win_allocator.cc',
# Included by debugallocation_shim.cc.
'<(tcmalloc_dir)/src/debugallocation.cc',
'<(tcmalloc_dir)/src/tcmalloc.cc',
# We simply don't use these, but list them above so that IDE
# users can view the full available source for reference, etc.
'<(tcmalloc_dir)/src/addressmap-inl.h',
'<(tcmalloc_dir)/src/base/atomicops-internals-linuxppc.h',
'<(tcmalloc_dir)/src/base/atomicops-internals-macosx.h',
'<(tcmalloc_dir)/src/base/atomicops-internals-x86-msvc.h',
'<(tcmalloc_dir)/src/base/atomicops-internals-x86.cc',
'<(tcmalloc_dir)/src/base/atomicops-internals-x86.h',
'<(tcmalloc_dir)/src/base/atomicops.h',
'<(tcmalloc_dir)/src/base/basictypes.h',
'<(tcmalloc_dir)/src/base/commandlineflags.h',
'<(tcmalloc_dir)/src/base/cycleclock.h',
'<(tcmalloc_dir)/src/base/elf_mem_image.h',
'<(tcmalloc_dir)/src/base/elfcore.h',
'<(tcmalloc_dir)/src/base/googleinit.h',
'<(tcmalloc_dir)/src/base/linux_syscall_support.h',
'<(tcmalloc_dir)/src/base/simple_mutex.h',
'<(tcmalloc_dir)/src/base/spinlock_linux-inl.h',
'<(tcmalloc_dir)/src/base/spinlock_posix-inl.h',
'<(tcmalloc_dir)/src/base/spinlock_win32-inl.h',
'<(tcmalloc_dir)/src/base/stl_allocator.h',
'<(tcmalloc_dir)/src/base/thread_annotations.h',
'<(tcmalloc_dir)/src/getpc.h',
'<(tcmalloc_dir)/src/gperftools/heap-checker.h',
'<(tcmalloc_dir)/src/gperftools/heap-profiler.h',
'<(tcmalloc_dir)/src/gperftools/malloc_extension.h',
'<(tcmalloc_dir)/src/gperftools/malloc_extension_c.h',
'<(tcmalloc_dir)/src/gperftools/malloc_hook.h',
'<(tcmalloc_dir)/src/gperftools/malloc_hook_c.h',
'<(tcmalloc_dir)/src/gperftools/profiler.h',
'<(tcmalloc_dir)/src/gperftools/stacktrace.h',
'<(tcmalloc_dir)/src/gperftools/tcmalloc.h',
'<(tcmalloc_dir)/src/libc_override.h',
'<(tcmalloc_dir)/src/libc_override_gcc_and_weak.h',
'<(tcmalloc_dir)/src/libc_override_glibc.h',
'<(tcmalloc_dir)/src/libc_override_osx.h',
'<(tcmalloc_dir)/src/libc_override_redefine.h',
'<(tcmalloc_dir)/src/malloc_hook_mmap_freebsd.h',
'<(tcmalloc_dir)/src/malloc_hook_mmap_linux.h',
'<(tcmalloc_dir)/src/memfs_malloc.cc',
'<(tcmalloc_dir)/src/packed-cache-inl.h',
'<(tcmalloc_dir)/src/page_heap_allocator.h',
'<(tcmalloc_dir)/src/pagemap.h',
'<(tcmalloc_dir)/src/stacktrace_arm-inl.h',
'<(tcmalloc_dir)/src/stacktrace_config.h',
'<(tcmalloc_dir)/src/stacktrace_generic-inl.h',
'<(tcmalloc_dir)/src/stacktrace_libunwind-inl.h',
'<(tcmalloc_dir)/src/stacktrace_powerpc-inl.h',
'<(tcmalloc_dir)/src/stacktrace_win32-inl.h',
'<(tcmalloc_dir)/src/stacktrace_with_context.cc',
'<(tcmalloc_dir)/src/stacktrace_x86-inl.h',
'<(tcmalloc_dir)/src/tcmalloc_guard.h',
'<(tcmalloc_dir)/src/windows/config.h',
'<(tcmalloc_dir)/src/windows/gperftools/tcmalloc.h',
'<(tcmalloc_dir)/src/windows/get_mangled_names.cc',
'<(tcmalloc_dir)/src/windows/ia32_modrm_map.cc',
'<(tcmalloc_dir)/src/windows/ia32_opcode_map.cc',
'<(tcmalloc_dir)/src/windows/mingw.h',
'<(tcmalloc_dir)/src/windows/mini_disassembler.cc',
'<(tcmalloc_dir)/src/windows/mini_disassembler.h',
'<(tcmalloc_dir)/src/windows/mini_disassembler_types.h',
'<(tcmalloc_dir)/src/windows/override_functions.cc',
'<(tcmalloc_dir)/src/windows/patch_functions.cc',
'<(tcmalloc_dir)/src/windows/preamble_patcher.cc',
'<(tcmalloc_dir)/src/windows/preamble_patcher.h',
'<(tcmalloc_dir)/src/windows/preamble_patcher_with_stub.cc',
],
'dependencies': [
'../third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
],
'msvs_settings': {
# TODO(sgk): merge this with build/common.gypi settings
'VCLibrarianTool': {
'AdditionalOptions': ['/ignore:4006,4221'],
},
'VCLinkerTool': {
'AdditionalOptions': ['/ignore:4006'],
},
},
'configurations': {
'Debug_Base': {
'msvs_settings': {
'VCCLCompilerTool': {
'RuntimeLibrary': '0',
},
},
'variables': {
# Provide a way to force disable debugallocation in Debug builds,
# e.g. for profiling (it's more rare to profile Debug builds,
# but people sometimes need to do that).
'disable_debugallocation%': 0,
},
'conditions': [
# TODO(phajdan.jr): Also enable on Windows.
['disable_debugallocation==0 and OS!="win"', {
'defines': [
# Use debugallocation for Debug builds to catch problems early
# and cleanly, http://crbug.com/30715 .
'TCMALLOC_FOR_DEBUGALLOCATION',
],
}],
],
},
},
'conditions': [
['OS=="linux" and clang_type_profiler==1', {
'dependencies': [
'type_profiler_tcmalloc',
],
# It is undoing dependencies and cflags_cc for type_profiler which
# build/common.gypi injects into all targets.
'dependencies!': [
'type_profiler',
],
'cflags_cc!': [
'-fintercept-allocation-functions',
],
}],
['OS=="win"', {
'defines': [
'PERFTOOLS_DLL_DECL=',
],
'defines!': [
# tcmalloc source files unconditionally define this, remove it from
# the list of defines that common.gypi defines globally.
'NOMINMAX',
],
'dependencies': [
'libcmt',
],
'include_dirs': [
'<(jemalloc_dir)',
'<(tcmalloc_dir)/src/windows',
],
'sources!': [
'<(tcmalloc_dir)/src/base/elf_mem_image.cc',
'<(tcmalloc_dir)/src/base/elf_mem_image.h',
'<(tcmalloc_dir)/src/base/linuxthreads.cc',
'<(tcmalloc_dir)/src/base/linuxthreads.h',
'<(tcmalloc_dir)/src/base/vdso_support.cc',
'<(tcmalloc_dir)/src/base/vdso_support.h',
'<(tcmalloc_dir)/src/maybe_threads.cc',
'<(tcmalloc_dir)/src/maybe_threads.h',
'<(tcmalloc_dir)/src/symbolize.h',
'<(tcmalloc_dir)/src/system-alloc.cc',
'<(tcmalloc_dir)/src/system-alloc.h',
# included by allocator_shim.cc
'debugallocation_shim.cc',
# heap-profiler/checker/cpuprofiler
'<(tcmalloc_dir)/src/base/thread_lister.c',
'<(tcmalloc_dir)/src/base/thread_lister.h',
'<(tcmalloc_dir)/src/deep-heap-profile.cc',
'<(tcmalloc_dir)/src/deep-heap-profile.h',
'<(tcmalloc_dir)/src/heap-checker-bcad.cc',
'<(tcmalloc_dir)/src/heap-checker.cc',
'<(tcmalloc_dir)/src/heap-profiler.cc',
'<(tcmalloc_dir)/src/heap-profile-table.cc',
'<(tcmalloc_dir)/src/heap-profile-table.h',
'<(tcmalloc_dir)/src/memory_region_map.cc',
'<(tcmalloc_dir)/src/memory_region_map.h',
'<(tcmalloc_dir)/src/profiledata.cc',
'<(tcmalloc_dir)/src/profiledata.h',
'<(tcmalloc_dir)/src/profile-handler.cc',
'<(tcmalloc_dir)/src/profile-handler.h',
'<(tcmalloc_dir)/src/profiler.cc',
],
}],
['OS=="linux" or OS=="freebsd" or OS=="solaris" or OS=="android"', {
'sources!': [
'<(tcmalloc_dir)/src/system-alloc.h',
'<(tcmalloc_dir)/src/windows/port.cc',
'<(tcmalloc_dir)/src/windows/port.h',
# TODO(willchan): Support allocator shim later on.
'allocator_shim.cc',
# TODO(willchan): support jemalloc on other platforms
# jemalloc files
'<(jemalloc_dir)/jemalloc.c',
'<(jemalloc_dir)/jemalloc.h',
'<(jemalloc_dir)/ql.h',
'<(jemalloc_dir)/qr.h',
'<(jemalloc_dir)/rb.h',
],
# We enable all warnings by default, but upstream disables a few.
# Keep "-Wno-*" flags in sync with upstream by comparing against:
# http://code.google.com/p/google-perftools/source/browse/trunk/Makefile.am
'cflags': [
'-Wno-sign-compare',
'-Wno-unused-result',
],
'cflags!': [
'-fvisibility=hidden',
],
'link_settings': {
'ldflags': [
# Don't let linker rip this symbol out, otherwise the heap&cpu
# profilers will not initialize properly on startup.
'-Wl,-uIsHeapProfilerRunning,-uProfilerStart',
# Do the same for heap leak checker.
'-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi',
'-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl',
'-Wl,-u_ZN15HeapLeakChecker12IgnoreObjectEPKv,-u_ZN15HeapLeakChecker14UnIgnoreObjectEPKv',
]},
}],
# Need to distinguish a non-SDK build for Android WebView
# due to differences in C include files.
['OS=="android" and android_webview_build==1', {
'defines': ['ANDROID_NON_SDK_BUILD'],
}],
[ 'use_vtable_verify==1', {
'cflags': [
'-fvtable-verify=preinit',
],
}],
[ 'linux_keep_shadow_stacks==1', {
'sources': [
'<(tcmalloc_dir)/src/linux_shadow_stacks.cc',
'<(tcmalloc_dir)/src/linux_shadow_stacks.h',
'<(tcmalloc_dir)/src/stacktrace_shadow-inl.h',
],
'cflags': [
'-finstrument-functions',
],
'defines': [
'KEEP_SHADOW_STACKS',
],
}],
[ 'linux_use_heapchecker==0', {
# Do not compile and link the heapchecker source.
'sources!': [
'<(tcmalloc_dir)/src/heap-checker-bcad.cc',
'<(tcmalloc_dir)/src/heap-checker.cc',
],
# Disable the heap checker in tcmalloc.
'defines': [
'NO_HEAP_CHECK',
],
}],
['order_profiling != 0', {
'target_conditions' : [
['_toolset=="target"', {
'cflags!': [ '-finstrument-functions' ],
}],
],
}],
],
},
{
# This library is linked in to src/base.gypi:base and allocator_unittests
# It can't depend on either and nothing else should depend on it - all
# other code should use the interfaced provided by base.
'target_name': 'allocator_extension_thunks',
'type': 'static_library',
'sources': [
'allocator_extension_thunks.cc',
'allocator_extension_thunks.h',
],
'toolsets': ['host', 'target'],
'include_dirs': [
'../../'
],
'conditions': [
['OS=="linux" and clang_type_profiler==1', {
# It is undoing dependencies and cflags_cc for type_profiler which
# build/common.gypi injects into all targets.
'dependencies!': [
'type_profiler',
],
'cflags_cc!': [
'-fintercept-allocation-functions',
],
}],
],
},
],
'conditions': [
['OS=="win"', {
'targets': [
{
'target_name': 'libcmt',
'type': 'none',
'actions': [
{
'action_name': 'libcmt',
'inputs': [
'prep_libc.py',
],
'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/allocator/libcmt.lib',
],
'action': [
'python',
'prep_libc.py',
'$(VCInstallDir)lib',
'<(SHARED_INTERMEDIATE_DIR)/allocator',
'<(target_arch)',
],
},
],
},
{
'target_name': 'allocator_unittests',
'type': 'executable',
'dependencies': [
'allocator',
'allocator_extension_thunks',
'../../testing/gtest.gyp:gtest',
],
'include_dirs': [
'.',
'<(tcmalloc_dir)/src/base',
'<(tcmalloc_dir)/src',
'../..',
],
'sources': [
'allocator_unittests.cc',
'../profiler/alternate_timer.cc',
'../profiler/alternate_timer.h',
],
},
{
'target_name': 'tcmalloc_unittest',
'type': 'executable',
'sources': [
'tcmalloc_unittest.cc',
],
'include_dirs': [
'../..',
# For constants of TCMalloc.
'<(tcmalloc_dir)/src',
],
'dependencies': [
'../../testing/gtest.gyp:gtest',
'../base.gyp:base',
'allocator',
],
},
],
}],
['OS=="win" and target_arch=="ia32"', {
'targets': [
{
'target_name': 'allocator_extension_thunks_win64',
'type': 'static_library',
'sources': [
'allocator_extension_thunks.cc',
'allocator_extension_thunks.h',
],
'toolsets': ['host', 'target'],
'include_dirs': [
'../../'
],
'configurations': {
'Common_Base': {
'msvs_target_platform': 'x64',
},
},
},
],
}],
['OS=="linux" and clang_type_profiler==1', {
# Some targets in this section undo dependencies and cflags_cc for
# type_profiler which build/common.gypi injects into all targets.
'targets': [
{
'target_name': 'type_profiler',
'type': 'static_library',
'dependencies!': [
'type_profiler',
],
'cflags_cc!': [
'-fintercept-allocation-functions',
],
'include_dirs': [
'../..',
],
'sources': [
'type_profiler.cc',
'type_profiler.h',
'type_profiler_control.h',
],
'toolsets': ['host', 'target'],
},
{
'target_name': 'type_profiler_tcmalloc',
'type': 'static_library',
'dependencies!': [
'type_profiler',
],
'cflags_cc!': [
'-fintercept-allocation-functions',
],
'include_dirs': [
'<(tcmalloc_dir)/src',
'../..',
],
'sources': [
'type_profiler_tcmalloc.cc',
'type_profiler_tcmalloc.h',
'<(tcmalloc_dir)/src/gperftools/type_profiler_map.h',
'<(tcmalloc_dir)/src/type_profiler_map.cc',
],
},
{
'target_name': 'type_profiler_unittests',
'type': 'executable',
'dependencies': [
'../../testing/gtest.gyp:gtest',
'../base.gyp:base',
'allocator',
'type_profiler_tcmalloc',
],
'include_dirs': [
'../..',
],
'sources': [
'type_profiler_control.cc',
'type_profiler_control.h',
'type_profiler_unittests.cc',
],
},
{
'target_name': 'type_profiler_map_unittests',
'type': 'executable',
'dependencies': [
'../../testing/gtest.gyp:gtest',
'../base.gyp:base',
'allocator',
],
'dependencies!': [
'type_profiler',
],
'cflags_cc!': [
'-fintercept-allocation-functions',
],
'include_dirs': [
'<(tcmalloc_dir)/src',
'../..',
],
'sources': [
'type_profiler_map_unittests.cc',
'<(tcmalloc_dir)/src/gperftools/type_profiler_map.h',
'<(tcmalloc_dir)/src/type_profiler_map.cc',
],
},
],
}],
],
}

View File

@ -0,0 +1,56 @@
// 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/allocator/allocator_extension.h"
#include "base/logging.h"
namespace base {
namespace allocator {
bool GetAllocatorWasteSize(size_t* size) {
thunks::GetAllocatorWasteSizeFunction get_allocator_waste_size_function =
thunks::GetGetAllocatorWasteSizeFunction();
return get_allocator_waste_size_function != NULL &&
get_allocator_waste_size_function(size);
}
void GetStats(char* buffer, int buffer_length) {
DCHECK_GT(buffer_length, 0);
thunks::GetStatsFunction get_stats_function = thunks::GetGetStatsFunction();
if (get_stats_function)
get_stats_function(buffer, buffer_length);
else
buffer[0] = '\0';
}
void ReleaseFreeMemory() {
thunks::ReleaseFreeMemoryFunction release_free_memory_function =
thunks::GetReleaseFreeMemoryFunction();
if (release_free_memory_function)
release_free_memory_function();
}
void SetGetAllocatorWasteSizeFunction(
thunks::GetAllocatorWasteSizeFunction get_allocator_waste_size_function) {
DCHECK_EQ(thunks::GetGetAllocatorWasteSizeFunction(),
reinterpret_cast<thunks::GetAllocatorWasteSizeFunction>(NULL));
thunks::SetGetAllocatorWasteSizeFunction(get_allocator_waste_size_function);
}
void SetGetStatsFunction(thunks::GetStatsFunction get_stats_function) {
DCHECK_EQ(thunks::GetGetStatsFunction(),
reinterpret_cast<thunks::GetStatsFunction>(NULL));
thunks::SetGetStatsFunction(get_stats_function);
}
void SetReleaseFreeMemoryFunction(
thunks::ReleaseFreeMemoryFunction release_free_memory_function) {
DCHECK_EQ(thunks::GetReleaseFreeMemoryFunction(),
reinterpret_cast<thunks::ReleaseFreeMemoryFunction>(NULL));
thunks::SetReleaseFreeMemoryFunction(release_free_memory_function);
}
} // namespace allocator
} // namespace base

View File

@ -0,0 +1,59 @@
// 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_ALLOCATOR_ALLOCATOR_EXTENSION_H
#define BASE_ALLOCATOR_ALLOCATOR_EXTENSION_H
#include <stddef.h> // for size_t
#include "base/allocator/allocator_extension_thunks.h"
#include "base/base_export.h"
#include "build/build_config.h"
namespace base {
namespace allocator {
// Request the allocator to report value of its waste memory size.
// Waste size corresponds to memory that has been allocated from the OS but
// not passed up to the application. It e.g. includes memory retained by free
// lists, internal data, chunks padding, etc.
//
// |size| pointer to the returned value, must be not NULL.
// Returns true if the value has been returned, false otherwise.
BASE_EXPORT bool GetAllocatorWasteSize(size_t* size);
// Request that the allocator print a human-readable description of the current
// state of the allocator into a null-terminated string in the memory segment
// buffer[0,buffer_length-1].
//
// |buffer| must point to a valid piece of memory
// |buffer_length| must be > 0.
BASE_EXPORT void GetStats(char* buffer, int buffer_length);
// Request that the allocator release any free memory it knows about to the
// system.
BASE_EXPORT void ReleaseFreeMemory();
// These settings allow specifying a callback used to implement the allocator
// extension functions. These are optional, but if set they must only be set
// once. These will typically called in an allocator-specific initialization
// routine.
//
// No threading promises are made. The caller is responsible for making sure
// these pointers are set before any other threads attempt to call the above
// functions.
BASE_EXPORT void SetGetAllocatorWasteSizeFunction(
thunks::GetAllocatorWasteSizeFunction get_allocator_waste_size_function);
BASE_EXPORT void SetGetStatsFunction(
thunks::GetStatsFunction get_stats_function);
BASE_EXPORT void SetReleaseFreeMemoryFunction(
thunks::ReleaseFreeMemoryFunction release_free_memory_function);
} // namespace allocator
} // namespace base
#endif

View File

@ -0,0 +1,52 @@
// 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/allocator/allocator_extension_thunks.h"
#include <cstddef> // for NULL
namespace base {
namespace allocator {
namespace thunks {
// This slightly odd translation unit exists because of the peculularity of how
// allocator_unittests work on windows. That target has to perform
// tcmalloc-specific initialization on windows, but it cannot depend on base
// otherwise. This target sits in the middle - base and allocator_unittests
// can depend on it. This file can't depend on anything else in base, including
// logging.
static GetAllocatorWasteSizeFunction g_get_allocator_waste_size_function = NULL;
static GetStatsFunction g_get_stats_function = NULL;
static ReleaseFreeMemoryFunction g_release_free_memory_function = NULL;
void SetGetAllocatorWasteSizeFunction(
GetAllocatorWasteSizeFunction get_allocator_waste_size_function) {
g_get_allocator_waste_size_function = get_allocator_waste_size_function;
}
GetAllocatorWasteSizeFunction GetGetAllocatorWasteSizeFunction() {
return g_get_allocator_waste_size_function;
}
void SetGetStatsFunction(GetStatsFunction get_stats_function) {
g_get_stats_function = get_stats_function;
}
GetStatsFunction GetGetStatsFunction() {
return g_get_stats_function;
}
void SetReleaseFreeMemoryFunction(
ReleaseFreeMemoryFunction release_free_memory_function) {
g_release_free_memory_function = release_free_memory_function;
}
ReleaseFreeMemoryFunction GetReleaseFreeMemoryFunction() {
return g_release_free_memory_function;
}
} // namespace thunks
} // namespace allocator
} // namespace base

View File

@ -0,0 +1,36 @@
// 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_ALLOCATOR_ALLOCATOR_THUNKS_EXTENSION_H
#define BASE_ALLOCATOR_ALLOCATOR_THUNKS_EXTENSION_H
#include <stddef.h> // for size_t
namespace base {
namespace allocator {
namespace thunks {
// WARNING: You probably don't want to use this file unless you are routing a
// new allocator extension from a specific allocator implementation to base.
// See allocator_extension.h to see the interface that base exports.
typedef bool (*GetAllocatorWasteSizeFunction)(size_t* size);
void SetGetAllocatorWasteSizeFunction(
GetAllocatorWasteSizeFunction get_allocator_waste_size_function);
GetAllocatorWasteSizeFunction GetGetAllocatorWasteSizeFunction();
typedef void (*GetStatsFunction)(char* buffer, int buffer_length);
void SetGetStatsFunction(GetStatsFunction get_stats_function);
GetStatsFunction GetGetStatsFunction();
typedef void (*ReleaseFreeMemoryFunction)();
void SetReleaseFreeMemoryFunction(
ReleaseFreeMemoryFunction release_free_memory_function);
ReleaseFreeMemoryFunction GetReleaseFreeMemoryFunction();
} // namespace thunks
} // namespace allocator
} // namespace base
#endif

View File

@ -0,0 +1,446 @@
// 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/allocator/allocator_shim.h"
#include <config.h>
#include "base/allocator/allocator_extension_thunks.h"
#include "base/profiler/alternate_timer.h"
#include "base/sysinfo.h"
#include "jemalloc.h"
// When defined, different heap allocators can be used via an environment
// variable set before running the program. This may reduce the amount
// of inlining that we get with malloc/free/etc. Disabling makes it
// so that only tcmalloc can be used.
#define ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
// TODO(mbelshe): Ensure that all calls to tcmalloc have the proper call depth
// from the "user code" so that debugging tools (HeapChecker) can work.
// __THROW is defined in glibc systems. It means, counter-intuitively,
// "This function will never throw an exception." It's an optional
// optimization tool, but we may need to use it to match glibc prototypes.
#ifndef __THROW // I guess we're not on a glibc system
# define __THROW // __THROW is just an optimization, so ok to make it ""
#endif
// new_mode behaves similarly to MSVC's _set_new_mode.
// If flag is 0 (default), calls to malloc will behave normally.
// If flag is 1, calls to malloc will behave like calls to new,
// and the std_new_handler will be invoked on failure.
// Can be set by calling _set_new_mode().
static int new_mode = 0;
typedef enum {
TCMALLOC, // TCMalloc is the default allocator.
JEMALLOC, // JEMalloc.
WINHEAP, // Windows Heap (standard Windows allocator).
WINLFH, // Windows LFH Heap.
} Allocator;
// This is the default allocator. This value can be changed at startup by
// specifying environment variables shown below it.
// See SetupSubprocessAllocator() to specify a default secondary (subprocess)
// allocator.
// TODO(jar): Switch to using TCMALLOC for the renderer as well.
#if (defined(ADDRESS_SANITIZER) && defined(OS_WIN))
// The Windows implementation of Asan requires the use of "WINHEAP".
static Allocator allocator = WINHEAP;
#else
static Allocator allocator = TCMALLOC;
#endif
// The names of the environment variables that can optionally control the
// selection of the allocator. The primary may be used to control overall
// allocator selection, and the secondary can be used to specify an allocator
// to use in sub-processes.
static const char primary_name[] = "CHROME_ALLOCATOR";
static const char secondary_name[] = "CHROME_ALLOCATOR_2";
// We include tcmalloc and the win_allocator to get as much inlining as
// possible.
#include "debugallocation_shim.cc"
#include "win_allocator.cc"
// Forward declarations from jemalloc.
extern "C" {
void* je_malloc(size_t s);
void* je_realloc(void* p, size_t s);
void je_free(void* s);
size_t je_msize(void* p);
bool je_malloc_init_hard();
void* je_memalign(size_t a, size_t s);
}
// Call the new handler, if one has been set.
// Returns true on successfully calling the handler, false otherwise.
inline bool call_new_handler(bool nothrow) {
// Get the current new handler. NB: this function is not
// thread-safe. We make a feeble stab at making it so here, but
// this lock only protects against tcmalloc interfering with
// itself, not with other libraries calling set_new_handler.
std::new_handler nh;
{
SpinLockHolder h(&set_new_handler_lock);
nh = std::set_new_handler(0);
(void) std::set_new_handler(nh);
}
#if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \
(defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
if (!nh)
return false;
// Since exceptions are disabled, we don't really know if new_handler
// failed. Assume it will abort if it fails.
(*nh)();
return false; // break out of the retry loop.
#else
// If no new_handler is established, the allocation failed.
if (!nh) {
if (nothrow)
return false;
throw std::bad_alloc();
}
// Otherwise, try the new_handler. If it returns, retry the
// allocation. If it throws std::bad_alloc, fail the allocation.
// if it throws something else, don't interfere.
try {
(*nh)();
} catch (const std::bad_alloc&) {
if (!nothrow)
throw;
return true;
}
#endif // (defined(__GNUC__) && !defined(__EXCEPTIONS)) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)
return false;
}
extern "C" {
void* malloc(size_t size) __THROW {
void* ptr;
for (;;) {
#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
switch (allocator) {
case JEMALLOC:
ptr = je_malloc(size);
break;
case WINHEAP:
case WINLFH:
ptr = win_heap_malloc(size);
break;
case TCMALLOC:
default:
ptr = do_malloc(size);
break;
}
#else
// TCMalloc case.
ptr = do_malloc(size);
#endif
if (ptr)
return ptr;
if (!new_mode || !call_new_handler(true))
break;
}
return ptr;
}
void free(void* p) __THROW {
#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
switch (allocator) {
case JEMALLOC:
je_free(p);
return;
case WINHEAP:
case WINLFH:
win_heap_free(p);
return;
}
#endif
// TCMalloc case.
do_free(p);
}
void* realloc(void* ptr, size_t size) __THROW {
// Webkit is brittle for allocators that return NULL for malloc(0). The
// realloc(0, 0) code path does not guarantee a non-NULL return, so be sure
// to call malloc for this case.
if (!ptr)
return malloc(size);
void* new_ptr;
for (;;) {
#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
switch (allocator) {
case JEMALLOC:
new_ptr = je_realloc(ptr, size);
break;
case WINHEAP:
case WINLFH:
new_ptr = win_heap_realloc(ptr, size);
break;
case TCMALLOC:
default:
new_ptr = do_realloc(ptr, size);
break;
}
#else
// TCMalloc case.
new_ptr = do_realloc(ptr, size);
#endif
// Subtle warning: NULL return does not alwas indicate out-of-memory. If
// the requested new size is zero, realloc should free the ptr and return
// NULL.
if (new_ptr || !size)
return new_ptr;
if (!new_mode || !call_new_handler(true))
break;
}
return new_ptr;
}
// TODO(mbelshe): Implement this for other allocators.
void malloc_stats(void) __THROW {
#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
switch (allocator) {
case JEMALLOC:
// No stats.
return;
case WINHEAP:
case WINLFH:
// No stats.
return;
}
#endif
tc_malloc_stats();
}
#ifdef WIN32
extern "C" size_t _msize(void* p) {
#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
switch (allocator) {
case JEMALLOC:
return je_msize(p);
case WINHEAP:
case WINLFH:
return win_heap_msize(p);
}
#endif
return MallocExtension::instance()->GetAllocatedSize(p);
}
// This is included to resolve references from libcmt.
extern "C" intptr_t _get_heap_handle() {
return 0;
}
static bool get_allocator_waste_size_thunk(size_t* size) {
#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
switch (allocator) {
case JEMALLOC:
case WINHEAP:
case WINLFH:
// TODO(alexeif): Implement for allocators other than tcmalloc.
return false;
}
#endif
size_t heap_size, allocated_bytes, unmapped_bytes;
MallocExtension* ext = MallocExtension::instance();
if (ext->GetNumericProperty("generic.heap_size", &heap_size) &&
ext->GetNumericProperty("generic.current_allocated_bytes",
&allocated_bytes) &&
ext->GetNumericProperty("tcmalloc.pageheap_unmapped_bytes",
&unmapped_bytes)) {
*size = heap_size - allocated_bytes - unmapped_bytes;
return true;
}
return false;
}
static void get_stats_thunk(char* buffer, int buffer_length) {
MallocExtension::instance()->GetStats(buffer, buffer_length);
}
static void release_free_memory_thunk() {
MallocExtension::instance()->ReleaseFreeMemory();
}
// The CRT heap initialization stub.
extern "C" int _heap_init() {
#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
// Don't use the environment variable if ADDRESS_SANITIZER is defined on
// Windows, as the implementation requires Winheap to be the allocator.
#if !(defined(ADDRESS_SANITIZER) && defined(OS_WIN))
const char* environment_value = GetenvBeforeMain(primary_name);
if (environment_value) {
if (!stricmp(environment_value, "jemalloc"))
allocator = JEMALLOC;
else if (!stricmp(environment_value, "winheap"))
allocator = WINHEAP;
else if (!stricmp(environment_value, "winlfh"))
allocator = WINLFH;
else if (!stricmp(environment_value, "tcmalloc"))
allocator = TCMALLOC;
}
#endif
switch (allocator) {
case JEMALLOC:
return je_malloc_init_hard() ? 0 : 1;
case WINHEAP:
return win_heap_init(false) ? 1 : 0;
case WINLFH:
return win_heap_init(true) ? 1 : 0;
case TCMALLOC:
default:
// fall through
break;
}
#endif
// Initializing tcmalloc.
// We intentionally leak this object. It lasts for the process
// lifetime. Trying to teardown at _heap_term() is so late that
// you can't do anything useful anyway.
new TCMallocGuard();
// Provide optional hook for monitoring allocation quantities on a per-thread
// basis. Only set the hook if the environment indicates this needs to be
// enabled.
const char* profiling =
GetenvBeforeMain(tracked_objects::kAlternateProfilerTime);
if (profiling && *profiling == '1') {
tracked_objects::SetAlternateTimeSource(
tcmalloc::ThreadCache::GetBytesAllocatedOnCurrentThread,
tracked_objects::TIME_SOURCE_TYPE_TCMALLOC);
}
base::allocator::thunks::SetGetAllocatorWasteSizeFunction(
get_allocator_waste_size_thunk);
base::allocator::thunks::SetGetStatsFunction(get_stats_thunk);
base::allocator::thunks::SetReleaseFreeMemoryFunction(
release_free_memory_thunk);
return 1;
}
// The CRT heap cleanup stub.
extern "C" void _heap_term() {}
// We set this to 1 because part of the CRT uses a check of _crtheap != 0
// to test whether the CRT has been initialized. Once we've ripped out
// the allocators from libcmt, we need to provide this definition so that
// the rest of the CRT is still usable.
extern "C" void* _crtheap = reinterpret_cast<void*>(1);
// Provide support for aligned memory through Windows only _aligned_malloc().
void* _aligned_malloc(size_t size, size_t alignment) {
// _aligned_malloc guarantees parameter validation, so do so here. These
// checks are somewhat stricter than _aligned_malloc() since we're effectively
// using memalign() under the hood.
DCHECK_GT(size, 0U);
DCHECK_EQ(alignment & (alignment - 1), 0U);
DCHECK_EQ(alignment % sizeof(void*), 0U);
void* ptr;
for (;;) {
#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
switch (allocator) {
case JEMALLOC:
ptr = je_memalign(alignment, size);
break;
case WINHEAP:
case WINLFH:
ptr = win_heap_memalign(alignment, size);
break;
case TCMALLOC:
default:
ptr = tc_memalign(alignment, size);
break;
}
#else
// TCMalloc case.
ptr = tc_memalign(alignment, size);
#endif
if (ptr) {
// Sanity check alignment.
DCHECK_EQ(reinterpret_cast<uintptr_t>(ptr) & (alignment - 1), 0U);
return ptr;
}
if (!new_mode || !call_new_handler(true))
break;
}
return ptr;
}
void _aligned_free(void* p) {
// Both JEMalloc and TCMalloc return pointers from memalign() that are safe to
// use with free(). Pointers allocated with win_heap_memalign() MUST be freed
// via win_heap_memalign_free() since the aligned pointer is not the real one.
#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
switch (allocator) {
case JEMALLOC:
je_free(p);
return;
case WINHEAP:
case WINLFH:
win_heap_memalign_free(p);
return;
}
#endif
// TCMalloc case.
do_free(p);
}
#endif // WIN32
#include "generic_allocators.cc"
} // extern C
namespace base {
namespace allocator {
void SetupSubprocessAllocator() {
#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
size_t primary_length = 0;
getenv_s(&primary_length, NULL, 0, primary_name);
size_t secondary_length = 0;
char buffer[20];
getenv_s(&secondary_length, buffer, sizeof(buffer), secondary_name);
DCHECK_GT(sizeof(buffer), secondary_length);
buffer[sizeof(buffer) - 1] = '\0';
if (secondary_length || !primary_length) {
// Don't use the environment variable if ADDRESS_SANITIZER is defined on
// Windows, as the implementation require Winheap to be the allocator.
#if !(defined(ADDRESS_SANITIZER) && defined(OS_WIN))
const char* secondary_value = secondary_length ? buffer : "TCMALLOC";
// Force renderer (or other subprocesses) to use secondary_value.
#else
const char* secondary_value = "WINHEAP";
#endif
int ret_val = _putenv_s(primary_name, secondary_value);
DCHECK_EQ(0, ret_val);
}
#endif // ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
}
void* TCMallocDoMallocForTest(size_t size) {
return do_malloc(size);
}
void TCMallocDoFreeForTest(void* ptr) {
do_free(ptr);
}
size_t ExcludeSpaceForMarkForTest(size_t size) {
return ExcludeSpaceForMark(size);
}
} // namespace allocator.
} // namespace base.

View File

@ -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_ALLOCATOR_ALLOCATOR_SHIM_H_
#define BASE_ALLOCATOR_ALLOCATOR_SHIM_H_
#include <stddef.h>
namespace base {
namespace allocator {
// Resets the environment variable CHROME_ALLOCATOR to specify the choice to
// be used by subprocesses. Priority is given to the current value of
// CHROME_ALLOCATOR_2 (if specified), then CHROME_ALLOCATOR (if specified), and
// then a default value (typically set to TCMALLOC).
void SetupSubprocessAllocator();
// Expose some of tcmalloc functions for test.
void* TCMallocDoMallocForTest(size_t size);
void TCMallocDoFreeForTest(void* ptr);
size_t ExcludeSpaceForMarkForTest(size_t size);
} // namespace allocator.
} // namespace base.
#endif // BASE_ALLOCATOR_ALLOCATOR_SHIM_H_

View File

@ -0,0 +1,521 @@
// 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 <stdio.h>
#include <stdlib.h>
#include <algorithm> // for min()
#include "base/atomicops.h"
#include "testing/gtest/include/gtest/gtest.h"
// Number of bits in a size_t.
static const int kSizeBits = 8 * sizeof(size_t);
// The maximum size of a size_t.
static const size_t kMaxSize = ~static_cast<size_t>(0);
// Maximum positive size of a size_t if it were signed.
static const size_t kMaxSignedSize = ((size_t(1) << (kSizeBits-1)) - 1);
// An allocation size which is not too big to be reasonable.
static const size_t kNotTooBig = 100000;
// An allocation size which is just too big.
static const size_t kTooBig = ~static_cast<size_t>(0);
namespace {
using std::min;
// Fill a buffer of the specified size with a predetermined pattern
static void Fill(unsigned char* buffer, int n) {
for (int i = 0; i < n; i++) {
buffer[i] = (i & 0xff);
}
}
// Check that the specified buffer has the predetermined pattern
// generated by Fill()
static bool Valid(unsigned char* buffer, int n) {
for (int i = 0; i < n; i++) {
if (buffer[i] != (i & 0xff)) {
return false;
}
}
return true;
}
// Check that a buffer is completely zeroed.
static bool IsZeroed(unsigned char* buffer, int n) {
for (int i = 0; i < n; i++) {
if (buffer[i] != 0) {
return false;
}
}
return true;
}
// Check alignment
static void CheckAlignment(void* p, int align) {
EXPECT_EQ(0, reinterpret_cast<uintptr_t>(p) & (align-1));
}
// Return the next interesting size/delta to check. Returns -1 if no more.
static int NextSize(int size) {
if (size < 100)
return size+1;
if (size < 100000) {
// Find next power of two
int power = 1;
while (power < size)
power <<= 1;
// Yield (power-1, power, power+1)
if (size < power-1)
return power-1;
if (size == power-1)
return power;
assert(size == power);
return power+1;
} else {
return -1;
}
}
#define GG_ULONGLONG(x) static_cast<uint64>(x)
template <class AtomicType>
static void TestAtomicIncrement() {
// For now, we just test single threaded execution
// use a guard value to make sure the NoBarrier_AtomicIncrement doesn't go
// outside the expected address bounds. This is in particular to
// test that some future change to the asm code doesn't cause the
// 32-bit NoBarrier_AtomicIncrement to do the wrong thing on 64-bit machines.
struct {
AtomicType prev_word;
AtomicType count;
AtomicType next_word;
} s;
AtomicType prev_word_value, next_word_value;
memset(&prev_word_value, 0xFF, sizeof(AtomicType));
memset(&next_word_value, 0xEE, sizeof(AtomicType));
s.prev_word = prev_word_value;
s.count = 0;
s.next_word = next_word_value;
EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 1), 1);
EXPECT_EQ(s.count, 1);
EXPECT_EQ(s.prev_word, prev_word_value);
EXPECT_EQ(s.next_word, next_word_value);
EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 2), 3);
EXPECT_EQ(s.count, 3);
EXPECT_EQ(s.prev_word, prev_word_value);
EXPECT_EQ(s.next_word, next_word_value);
EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 3), 6);
EXPECT_EQ(s.count, 6);
EXPECT_EQ(s.prev_word, prev_word_value);
EXPECT_EQ(s.next_word, next_word_value);
EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -3), 3);
EXPECT_EQ(s.count, 3);
EXPECT_EQ(s.prev_word, prev_word_value);
EXPECT_EQ(s.next_word, next_word_value);
EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -2), 1);
EXPECT_EQ(s.count, 1);
EXPECT_EQ(s.prev_word, prev_word_value);
EXPECT_EQ(s.next_word, next_word_value);
EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -1), 0);
EXPECT_EQ(s.count, 0);
EXPECT_EQ(s.prev_word, prev_word_value);
EXPECT_EQ(s.next_word, next_word_value);
EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -1), -1);
EXPECT_EQ(s.count, -1);
EXPECT_EQ(s.prev_word, prev_word_value);
EXPECT_EQ(s.next_word, next_word_value);
EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -4), -5);
EXPECT_EQ(s.count, -5);
EXPECT_EQ(s.prev_word, prev_word_value);
EXPECT_EQ(s.next_word, next_word_value);
EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 5), 0);
EXPECT_EQ(s.count, 0);
EXPECT_EQ(s.prev_word, prev_word_value);
EXPECT_EQ(s.next_word, next_word_value);
}
#define NUM_BITS(T) (sizeof(T) * 8)
template <class AtomicType>
static void TestCompareAndSwap() {
AtomicType value = 0;
AtomicType prev = base::subtle::NoBarrier_CompareAndSwap(&value, 0, 1);
EXPECT_EQ(1, value);
EXPECT_EQ(0, prev);
// Use test value that has non-zero bits in both halves, more for testing
// 64-bit implementation on 32-bit platforms.
const AtomicType k_test_val = (GG_ULONGLONG(1) <<
(NUM_BITS(AtomicType) - 2)) + 11;
value = k_test_val;
prev = base::subtle::NoBarrier_CompareAndSwap(&value, 0, 5);
EXPECT_EQ(k_test_val, value);
EXPECT_EQ(k_test_val, prev);
value = k_test_val;
prev = base::subtle::NoBarrier_CompareAndSwap(&value, k_test_val, 5);
EXPECT_EQ(5, value);
EXPECT_EQ(k_test_val, prev);
}
template <class AtomicType>
static void TestAtomicExchange() {
AtomicType value = 0;
AtomicType new_value = base::subtle::NoBarrier_AtomicExchange(&value, 1);
EXPECT_EQ(1, value);
EXPECT_EQ(0, new_value);
// Use test value that has non-zero bits in both halves, more for testing
// 64-bit implementation on 32-bit platforms.
const AtomicType k_test_val = (GG_ULONGLONG(1) <<
(NUM_BITS(AtomicType) - 2)) + 11;
value = k_test_val;
new_value = base::subtle::NoBarrier_AtomicExchange(&value, k_test_val);
EXPECT_EQ(k_test_val, value);
EXPECT_EQ(k_test_val, new_value);
value = k_test_val;
new_value = base::subtle::NoBarrier_AtomicExchange(&value, 5);
EXPECT_EQ(5, value);
EXPECT_EQ(k_test_val, new_value);
}
template <class AtomicType>
static void TestAtomicIncrementBounds() {
// Test increment at the half-width boundary of the atomic type.
// It is primarily for testing at the 32-bit boundary for 64-bit atomic type.
AtomicType test_val = GG_ULONGLONG(1) << (NUM_BITS(AtomicType) / 2);
AtomicType value = test_val - 1;
AtomicType new_value = base::subtle::NoBarrier_AtomicIncrement(&value, 1);
EXPECT_EQ(test_val, value);
EXPECT_EQ(value, new_value);
base::subtle::NoBarrier_AtomicIncrement(&value, -1);
EXPECT_EQ(test_val - 1, value);
}
// This is a simple sanity check that values are correct. Not testing
// atomicity
template <class AtomicType>
static void TestStore() {
const AtomicType kVal1 = static_cast<AtomicType>(0xa5a5a5a5a5a5a5a5LL);
const AtomicType kVal2 = static_cast<AtomicType>(-1);
AtomicType value;
base::subtle::NoBarrier_Store(&value, kVal1);
EXPECT_EQ(kVal1, value);
base::subtle::NoBarrier_Store(&value, kVal2);
EXPECT_EQ(kVal2, value);
base::subtle::Acquire_Store(&value, kVal1);
EXPECT_EQ(kVal1, value);
base::subtle::Acquire_Store(&value, kVal2);
EXPECT_EQ(kVal2, value);
base::subtle::Release_Store(&value, kVal1);
EXPECT_EQ(kVal1, value);
base::subtle::Release_Store(&value, kVal2);
EXPECT_EQ(kVal2, value);
}
// This is a simple sanity check that values are correct. Not testing
// atomicity
template <class AtomicType>
static void TestLoad() {
const AtomicType kVal1 = static_cast<AtomicType>(0xa5a5a5a5a5a5a5a5LL);
const AtomicType kVal2 = static_cast<AtomicType>(-1);
AtomicType value;
value = kVal1;
EXPECT_EQ(kVal1, base::subtle::NoBarrier_Load(&value));
value = kVal2;
EXPECT_EQ(kVal2, base::subtle::NoBarrier_Load(&value));
value = kVal1;
EXPECT_EQ(kVal1, base::subtle::Acquire_Load(&value));
value = kVal2;
EXPECT_EQ(kVal2, base::subtle::Acquire_Load(&value));
value = kVal1;
EXPECT_EQ(kVal1, base::subtle::Release_Load(&value));
value = kVal2;
EXPECT_EQ(kVal2, base::subtle::Release_Load(&value));
}
template <class AtomicType>
static void TestAtomicOps() {
TestCompareAndSwap<AtomicType>();
TestAtomicExchange<AtomicType>();
TestAtomicIncrementBounds<AtomicType>();
TestStore<AtomicType>();
TestLoad<AtomicType>();
}
static void TestCalloc(size_t n, size_t s, bool ok) {
char* p = reinterpret_cast<char*>(calloc(n, s));
if (!ok) {
EXPECT_EQ(NULL, p) << "calloc(n, s) should not succeed";
} else {
EXPECT_NE(reinterpret_cast<void*>(NULL), p) <<
"calloc(n, s) should succeed";
for (int i = 0; i < n*s; i++) {
EXPECT_EQ('\0', p[i]);
}
free(p);
}
}
// A global test counter for number of times the NewHandler is called.
static int news_handled = 0;
static void TestNewHandler() {
++news_handled;
throw std::bad_alloc();
}
// Because we compile without exceptions, we expect these will not throw.
static void TestOneNewWithoutExceptions(void* (*func)(size_t),
bool should_throw) {
// success test
try {
void* ptr = (*func)(kNotTooBig);
EXPECT_NE(reinterpret_cast<void*>(NULL), ptr) <<
"allocation should not have failed.";
} catch(...) {
EXPECT_EQ(0, 1) << "allocation threw unexpected exception.";
}
// failure test
try {
void* rv = (*func)(kTooBig);
EXPECT_EQ(NULL, rv);
EXPECT_FALSE(should_throw) << "allocation should have thrown.";
} catch(...) {
EXPECT_TRUE(should_throw) << "allocation threw unexpected exception.";
}
}
static void TestNothrowNew(void* (*func)(size_t)) {
news_handled = 0;
// test without new_handler:
std::new_handler saved_handler = std::set_new_handler(0);
TestOneNewWithoutExceptions(func, false);
// test with new_handler:
std::set_new_handler(TestNewHandler);
TestOneNewWithoutExceptions(func, true);
EXPECT_EQ(news_handled, 1) << "nothrow new_handler was not called.";
std::set_new_handler(saved_handler);
}
} // namespace
//-----------------------------------------------------------------------------
TEST(Atomics, AtomicIncrementWord) {
TestAtomicIncrement<AtomicWord>();
}
TEST(Atomics, AtomicIncrement32) {
TestAtomicIncrement<Atomic32>();
}
TEST(Atomics, AtomicOpsWord) {
TestAtomicIncrement<AtomicWord>();
}
TEST(Atomics, AtomicOps32) {
TestAtomicIncrement<Atomic32>();
}
TEST(Allocators, Malloc) {
// Try allocating data with a bunch of alignments and sizes
for (int size = 1; size < 1048576; size *= 2) {
unsigned char* ptr = reinterpret_cast<unsigned char*>(malloc(size));
CheckAlignment(ptr, 2); // Should be 2 byte aligned
Fill(ptr, size);
EXPECT_TRUE(Valid(ptr, size));
free(ptr);
}
}
TEST(Allocators, Calloc) {
TestCalloc(0, 0, true);
TestCalloc(0, 1, true);
TestCalloc(1, 1, true);
TestCalloc(1<<10, 0, true);
TestCalloc(1<<20, 0, true);
TestCalloc(0, 1<<10, true);
TestCalloc(0, 1<<20, true);
TestCalloc(1<<20, 2, true);
TestCalloc(2, 1<<20, true);
TestCalloc(1000, 1000, true);
TestCalloc(kMaxSize, 2, false);
TestCalloc(2, kMaxSize, false);
TestCalloc(kMaxSize, kMaxSize, false);
TestCalloc(kMaxSignedSize, 3, false);
TestCalloc(3, kMaxSignedSize, false);
TestCalloc(kMaxSignedSize, kMaxSignedSize, false);
}
TEST(Allocators, New) {
TestNothrowNew(&::operator new);
TestNothrowNew(&::operator new[]);
}
// This makes sure that reallocing a small number of bytes in either
// direction doesn't cause us to allocate new memory.
TEST(Allocators, Realloc1) {
int start_sizes[] = { 100, 1000, 10000, 100000 };
int deltas[] = { 1, -2, 4, -8, 16, -32, 64, -128 };
for (int s = 0; s < sizeof(start_sizes)/sizeof(*start_sizes); ++s) {
void* p = malloc(start_sizes[s]);
ASSERT_TRUE(p);
// The larger the start-size, the larger the non-reallocing delta.
for (int d = 0; d < s*2; ++d) {
void* new_p = realloc(p, start_sizes[s] + deltas[d]);
ASSERT_EQ(p, new_p); // realloc should not allocate new memory
}
// Test again, but this time reallocing smaller first.
for (int d = 0; d < s*2; ++d) {
void* new_p = realloc(p, start_sizes[s] - deltas[d]);
ASSERT_EQ(p, new_p); // realloc should not allocate new memory
}
free(p);
}
}
TEST(Allocators, Realloc2) {
for (int src_size = 0; src_size >= 0; src_size = NextSize(src_size)) {
for (int dst_size = 0; dst_size >= 0; dst_size = NextSize(dst_size)) {
unsigned char* src = reinterpret_cast<unsigned char*>(malloc(src_size));
Fill(src, src_size);
unsigned char* dst =
reinterpret_cast<unsigned char*>(realloc(src, dst_size));
EXPECT_TRUE(Valid(dst, min(src_size, dst_size)));
Fill(dst, dst_size);
EXPECT_TRUE(Valid(dst, dst_size));
if (dst != NULL) free(dst);
}
}
// Now make sure realloc works correctly even when we overflow the
// packed cache, so some entries are evicted from the cache.
// The cache has 2^12 entries, keyed by page number.
const int kNumEntries = 1 << 14;
int** p = reinterpret_cast<int**>(malloc(sizeof(*p) * kNumEntries));
int sum = 0;
for (int i = 0; i < kNumEntries; i++) {
// no page size is likely to be bigger than 8192?
p[i] = reinterpret_cast<int*>(malloc(8192));
p[i][1000] = i; // use memory deep in the heart of p
}
for (int i = 0; i < kNumEntries; i++) {
p[i] = reinterpret_cast<int*>(realloc(p[i], 9000));
}
for (int i = 0; i < kNumEntries; i++) {
sum += p[i][1000];
free(p[i]);
}
EXPECT_EQ(kNumEntries/2 * (kNumEntries - 1), sum); // assume kNE is even
free(p);
}
TEST(Allocators, ReallocZero) {
// Test that realloc to zero does not return NULL.
for (int size = 0; size >= 0; size = NextSize(size)) {
char* ptr = reinterpret_cast<char*>(malloc(size));
EXPECT_NE(static_cast<char*>(NULL), ptr);
ptr = reinterpret_cast<char*>(realloc(ptr, 0));
EXPECT_NE(static_cast<char*>(NULL), ptr);
if (ptr)
free(ptr);
}
}
#ifdef WIN32
// Test recalloc
TEST(Allocators, Recalloc) {
for (int src_size = 0; src_size >= 0; src_size = NextSize(src_size)) {
for (int dst_size = 0; dst_size >= 0; dst_size = NextSize(dst_size)) {
unsigned char* src =
reinterpret_cast<unsigned char*>(_recalloc(NULL, 1, src_size));
EXPECT_TRUE(IsZeroed(src, src_size));
Fill(src, src_size);
unsigned char* dst =
reinterpret_cast<unsigned char*>(_recalloc(src, 1, dst_size));
EXPECT_TRUE(Valid(dst, min(src_size, dst_size)));
Fill(dst, dst_size);
EXPECT_TRUE(Valid(dst, dst_size));
if (dst != NULL)
free(dst);
}
}
}
// Test windows specific _aligned_malloc() and _aligned_free() methods.
TEST(Allocators, AlignedMalloc) {
// Try allocating data with a bunch of alignments and sizes
static const int kTestAlignments[] = {8, 16, 256, 4096, 8192, 16384};
for (int size = 1; size > 0; size = NextSize(size)) {
for (int i = 0; i < ARRAYSIZE(kTestAlignments); ++i) {
unsigned char* ptr = static_cast<unsigned char*>(
_aligned_malloc(size, kTestAlignments[i]));
CheckAlignment(ptr, kTestAlignments[i]);
Fill(ptr, size);
EXPECT_TRUE(Valid(ptr, size));
// Make a second allocation of the same size and alignment to prevent
// allocators from passing this test by accident. Per jar, tcmalloc
// provides allocations for new (never before seen) sizes out of a thread
// local heap of a given "size class." Each time the test requests a new
// size, it will usually get the first element of a span, which is a
// 4K aligned allocation.
unsigned char* ptr2 = static_cast<unsigned char*>(
_aligned_malloc(size, kTestAlignments[i]));
CheckAlignment(ptr2, kTestAlignments[i]);
Fill(ptr2, size);
EXPECT_TRUE(Valid(ptr2, size));
// Should never happen, but sanity check just in case.
ASSERT_NE(ptr, ptr2);
_aligned_free(ptr);
_aligned_free(ptr2);
}
}
}
#endif
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,9 @@
// 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.
#if defined(TCMALLOC_FOR_DEBUGALLOCATION)
#include "third_party/tcmalloc/chromium/src/debugallocation.cc"
#else
#include "third_party/tcmalloc/chromium/src/tcmalloc.cc"
#endif

View File

@ -0,0 +1,168 @@
// Copyright (c) 2009 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.
// When possible, we implement allocator functions on top of the basic
// low-level functions malloc() and free(). This way, including a new
// allocator is as simple as providing just a small interface.
//
// As such, this file should not contain any allocator-specific code.
// Implement a C++ style allocation, which always calls the new_handler
// on failure.
inline void* generic_cpp_alloc(size_t size, bool nothrow) {
void* ptr;
for (;;) {
ptr = malloc(size);
if (ptr)
return ptr;
if (!call_new_handler(nothrow))
break;
}
return ptr;
}
extern "C++" {
void* __cdecl operator new(size_t size) {
return generic_cpp_alloc(size, false);
}
void operator delete(void* p) __THROW {
free(p);
}
void* operator new[](size_t size) {
return generic_cpp_alloc(size, false);
}
void operator delete[](void* p) __THROW {
free(p);
}
void* operator new(size_t size, const std::nothrow_t& nt) __THROW {
return generic_cpp_alloc(size, true);
}
void* operator new[](size_t size, const std::nothrow_t& nt) __THROW {
return generic_cpp_alloc(size, true);
}
// This function behaves similarly to MSVC's _set_new_mode.
// If flag is 0 (default), calls to malloc will behave normally.
// If flag is 1, calls to malloc will behave like calls to new,
// and the std_new_handler will be invoked on failure.
// Returns the previous mode.
int _set_new_mode(int flag) __THROW {
int old_mode = new_mode;
new_mode = flag;
return old_mode;
}
} // extern "C++"
extern "C" {
void* calloc(size_t n, size_t elem_size) __THROW {
// Overflow check
const size_t size = n * elem_size;
if (elem_size != 0 && size / elem_size != n) return NULL;
void* result = malloc(size);
if (result != NULL) {
memset(result, 0, size);
}
return result;
}
void cfree(void* p) __THROW {
free(p);
}
#ifdef WIN32
void* _recalloc(void* p, size_t n, size_t elem_size) {
if (!p)
return calloc(n, elem_size);
// This API is a bit odd.
// Note: recalloc only guarantees zeroed memory when p is NULL.
// Generally, calls to malloc() have padding. So a request
// to malloc N bytes actually malloc's N+x bytes. Later, if
// that buffer is passed to recalloc, we don't know what N
// was anymore. We only know what N+x is. As such, there is
// no way to know what to zero out.
const size_t size = n * elem_size;
if (elem_size != 0 && size / elem_size != n) return NULL;
return realloc(p, size);
}
void* _calloc_impl(size_t n, size_t size) {
return calloc(n, size);
}
#ifndef NDEBUG
#undef malloc
#undef free
#undef calloc
static int error_handler(int reportType) {
switch (reportType) {
case 0: // _CRT_WARN
__debugbreak();
return 0;
case 1: // _CRT_ERROR
__debugbreak();
return 0;
case 2: // _CRT_ASSERT
__debugbreak();
return 0;
}
char* p = NULL;
*p = '\0';
return 0;
}
int _CrtDbgReport(int reportType,
const char*,
int, const char*,
const char*,
...) {
return error_handler(reportType);
}
int _CrtDbgReportW(int reportType,
const wchar_t*,
int, const wchar_t*,
const wchar_t*,
...) {
return error_handler(reportType);
}
int _CrtSetReportMode(int, int) {
return 0;
}
void* _malloc_dbg(size_t size, int , const char*, int) {
return malloc(size);
}
void* _realloc_dbg(void* ptr, size_t size, int, const char*, int) {
return realloc(ptr, size);
}
void _free_dbg(void* ptr, int) {
free(ptr);
}
void* _calloc_dbg(size_t n, size_t size, int, const char*, int) {
return calloc(n, size);
}
#endif // NDEBUG
#endif // WIN32
} // extern C

67
base/allocator/prep_libc.py Executable file
View File

@ -0,0 +1,67 @@
#!/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.
#
# This script takes libcmt.lib for VS2005/08/10 and removes the allocation
# related functions from it.
#
# Usage: prep_libc.py <VCLibDir> <OutputDir> <arch>
#
# VCLibDir is the path where VC is installed, something like:
# C:\Program Files\Microsoft Visual Studio 8\VC\lib
# OutputDir is the directory where the modified libcmt file should be stored.
# arch is either 'ia32' or 'x64'
import os
import shutil
import subprocess
import sys
def run(command, filter=None):
"""Run |command|, removing any lines that match |filter|. The filter is
to remove the echoing of input filename that 'lib' does."""
popen = subprocess.Popen(
command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
out, _ = popen.communicate()
for line in out.splitlines():
if filter and line.strip() != filter:
print line
return popen.returncode
def main():
bindir = 'SELF_X86'
objdir = 'INTEL'
vs_install_dir = sys.argv[1]
outdir = sys.argv[2]
if "x64" in sys.argv[3]:
bindir = 'SELF_64_amd64'
objdir = 'amd64'
vs_install_dir = os.path.join(vs_install_dir, 'amd64')
output_lib = os.path.join(outdir, 'libcmt.lib')
shutil.copyfile(os.path.join(vs_install_dir, 'libcmt.lib'), output_lib)
shutil.copyfile(os.path.join(vs_install_dir, 'libcmt.pdb'),
os.path.join(outdir, 'libcmt.pdb'))
vspaths = [
'build\\intel\\mt_obj\\',
'f:\\dd\\vctools\\crt_bld\\' + bindir + \
'\\crt\\src\\build\\' + objdir + '\\mt_obj\\',
'F:\\dd\\vctools\\crt_bld\\' + bindir + \
'\\crt\\src\\build\\' + objdir + '\\mt_obj\\nativec\\\\',
'F:\\dd\\vctools\\crt_bld\\' + bindir + \
'\\crt\\src\\build\\' + objdir + '\\mt_obj\\nativecpp\\\\' ]
objfiles = ['malloc', 'free', 'realloc', 'new', 'delete', 'new2', 'delete2',
'align', 'msize', 'heapinit', 'expand', 'heapchk', 'heapwalk',
'heapmin', 'sbheap', 'calloc', 'recalloc', 'calloc_impl',
'new_mode', 'newopnt', 'newaopnt']
for obj in objfiles:
for vspath in vspaths:
cmd = ('lib /nologo /ignore:4006,4014,4221 /remove:%s%s.obj %s' %
(vspath, obj, output_lib))
run(cmd, obj + '.obj')
if __name__ == "__main__":
sys.exit(main())

View File

@ -0,0 +1,81 @@
// 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 <stdio.h>
#include "base/allocator/allocator_shim.h"
#include "testing/gtest/include/gtest/gtest.h"
// TCMalloc header files
#include "common.h" // For TCMalloc constants like page size, etc.
using base::allocator::TCMallocDoMallocForTest;
using base::allocator::TCMallocDoFreeForTest;
using base::allocator::ExcludeSpaceForMarkForTest;
TEST(TCMallocFreeCheck, BadPointerInFirstPageOfTheLargeObject) {
char* p = reinterpret_cast<char*>(
TCMallocDoMallocForTest(ExcludeSpaceForMarkForTest(kMaxSize + 1)));
for (int offset = 1; offset < kPageSize ; offset <<= 1) {
ASSERT_DEATH(TCMallocDoFreeForTest(p + offset),
"Pointer is not pointing to the start of a span");
}
}
TEST(TCMallocFreeCheck, BadPageAlignedPointerInsideLargeObject) {
char* p = reinterpret_cast<char*>(
TCMallocDoMallocForTest(ExcludeSpaceForMarkForTest(kMaxSize + 1)));
for (int offset = kPageSize; offset < kMaxSize; offset += kPageSize) {
// Only the first and last page of a span are in heap map. So for others
// tcmalloc will give a general error of invalid pointer.
ASSERT_DEATH(TCMallocDoFreeForTest(p + offset),
"Attempt to free invalid pointer");
}
ASSERT_DEATH(TCMallocDoFreeForTest(p + kMaxSize),
"Pointer is not pointing to the start of a span");
}
TEST(TCMallocFreeCheck, DoubleFreeLargeObject) {
char* p = reinterpret_cast<char*>(
TCMallocDoMallocForTest(ExcludeSpaceForMarkForTest(kMaxSize + 1)));
ASSERT_DEATH(TCMallocDoFreeForTest(p); TCMallocDoFreeForTest(p),
"Object was not in-use");
}
#ifdef NDEBUG
TEST(TCMallocFreeCheck, DoubleFreeSmallObject) {
for (size_t size = 1;
size <= ExcludeSpaceForMarkForTest(kMaxSize);
size <<= 1) {
char* p = reinterpret_cast<char*>(TCMallocDoMallocForTest(size));
ASSERT_DEATH(TCMallocDoFreeForTest(p); TCMallocDoFreeForTest(p),
"Circular loop in list detected");
}
}
#else
TEST(TCMallocFreeCheck, DoubleFreeSmallObject) {
size_t size = 1;
// When the object is small, tcmalloc validation can not distinguish normal
// memory corruption or double free, because there's not enough space in
// freed objects to keep the mark.
for (; size <= ExcludeSpaceForMarkForTest(kMinClassSize); size <<= 1) {
char* p = reinterpret_cast<char*>(TCMallocDoMallocForTest(size));
ASSERT_DEATH(TCMallocDoFreeForTest(p); TCMallocDoFreeForTest(p),
"Memory corrupted");
}
for (; size <= ExcludeSpaceForMarkForTest(kMaxSize); size <<= 1) {
char* p = reinterpret_cast<char*>(TCMallocDoMallocForTest(size));
ASSERT_DEATH(TCMallocDoFreeForTest(p); TCMallocDoFreeForTest(p),
"Attempt to double free");
}
}
#endif
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,63 @@
// Copyright 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.
#if defined(TYPE_PROFILING)
#include "base/allocator/type_profiler.h"
#include <assert.h>
namespace {
void* NopIntercept(void* ptr, size_t size, const std::type_info& type) {
return ptr;
}
base::type_profiler::InterceptFunction* g_new_intercept = NopIntercept;
base::type_profiler::InterceptFunction* g_delete_intercept = NopIntercept;
}
void* __op_new_intercept__(void* ptr,
size_t size,
const std::type_info& type) {
return g_new_intercept(ptr, size, type);
}
void* __op_delete_intercept__(void* ptr,
size_t size,
const std::type_info& type) {
return g_delete_intercept(ptr, size, type);
}
namespace base {
namespace type_profiler {
// static
void InterceptFunctions::SetFunctions(InterceptFunction* new_intercept,
InterceptFunction* delete_intercept) {
// Don't use DCHECK, as this file is injected into targets
// that do not and should not depend on base/base.gyp:base
assert(g_new_intercept == NopIntercept);
assert(g_delete_intercept == NopIntercept);
g_new_intercept = new_intercept;
g_delete_intercept = delete_intercept;
}
// static
void InterceptFunctions::ResetFunctions() {
g_new_intercept = NopIntercept;
g_delete_intercept = NopIntercept;
}
// static
bool InterceptFunctions::IsAvailable() {
return g_new_intercept != NopIntercept || g_delete_intercept != NopIntercept;
}
} // namespace type_profiler
} // namespace base
#endif // defined(TYPE_PROFILING)

View File

@ -0,0 +1,40 @@
// Copyright 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_ALLOCATOR_TYPE_PROFILER_H_
#define BASE_ALLOCATOR_TYPE_PROFILER_H_
#if defined(TYPE_PROFILING)
#include <stddef.h> // for size_t
#include <typeinfo> // for std::typeinfo
namespace base {
namespace type_profiler {
typedef void* InterceptFunction(void*, size_t, const std::type_info&);
class InterceptFunctions {
public:
// It must be called only once in a process while it is in single-thread.
// For now, ContentMainRunnerImpl::Initialize is the only supposed caller
// of this function except for single-threaded unit tests.
static void SetFunctions(InterceptFunction* new_intercept,
InterceptFunction* delete_intercept);
private:
friend class TypeProfilerTest;
// These functions are not thread safe.
// They must be used only from single-threaded unit tests.
static void ResetFunctions();
static bool IsAvailable();
};
} // namespace type_profiler
} // namespace base
#endif // defined(TYPE_PROFILING)
#endif // BASE_ALLOCATOR_TYPE_PROFILER_H_

View File

@ -0,0 +1,38 @@
// Copyright 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/allocator/type_profiler_control.h"
namespace base {
namespace type_profiler {
namespace {
#if defined(TYPE_PROFILING)
const bool kTypeProfilingEnabled = true;
#else
const bool kTypeProfilingEnabled = false;
#endif
bool g_enable_intercept = kTypeProfilingEnabled;
} // namespace
// static
void Controller::Stop() {
g_enable_intercept = false;
}
// static
bool Controller::IsProfiling() {
return kTypeProfilingEnabled && g_enable_intercept;
}
// static
void Controller::Restart() {
g_enable_intercept = kTypeProfilingEnabled;
}
} // namespace type_profiler
} // namespace base

View File

@ -0,0 +1,31 @@
// Copyright 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_ALLOCATOR_TYPE_PROFILER_CONTROL_H_
#define BASE_ALLOCATOR_TYPE_PROFILER_CONTROL_H_
#include "base/gtest_prod_util.h"
namespace base {
namespace type_profiler {
class Controller {
public:
static void Stop();
static bool IsProfiling();
private:
FRIEND_TEST_ALL_PREFIXES(TypeProfilerTest,
TestProfileNewWithoutProfiledDelete);
// It must be used only from allowed unit tests. The following is only
// allowed for use in unit tests. Profiling should never be restarted in
// regular use.
static void Restart();
};
} // namespace type_profiler
} // namespace base
#endif // BASE_ALLOCATOR_TYPE_PROFILER_CONTROL_H_

View File

@ -0,0 +1,99 @@
// Copyright 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 is a unittest set for type_profiler_map in third_party/tcmalloc. It is
// independent from other tests and executed manually like allocator_unittests
// since type_profiler_map is a singleton (like TCMalloc's heap-profiler), and
// it requires RTTI and different compiling/linking options from others.
#if defined(TYPE_PROFILING)
#include "base/memory/scoped_ptr.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/tcmalloc/chromium/src/gperftools/type_profiler_map.h"
namespace base {
namespace type_profiler {
static const void* const g_const_null = static_cast<const void*>(NULL);
TEST(TypeProfilerMapTest, NormalOperation) {
// Allocate an object just to get a valid address.
// This 'new' is not profiled by type_profiler.
scoped_ptr<int> dummy(new int(48));
const std::type_info* type;
type = LookupType(dummy.get());
EXPECT_EQ(g_const_null, type);
InsertType(dummy.get(), 12, typeid(int));
type = LookupType(dummy.get());
ASSERT_NE(g_const_null, type);
EXPECT_STREQ(typeid(int).name(), type->name());
EraseType(dummy.get());
type = LookupType(dummy.get());
EXPECT_EQ(g_const_null, type);
}
TEST(TypeProfilerMapTest, EraseWithoutInsert) {
scoped_ptr<int> dummy(new int(48));
const std::type_info* type;
for (int i = 0; i < 10; ++i) {
EraseType(dummy.get());
type = LookupType(dummy.get());
EXPECT_EQ(g_const_null, type);
}
}
TEST(TypeProfilerMapTest, InsertThenMultipleErase) {
scoped_ptr<int> dummy(new int(48));
const std::type_info* type;
InsertType(dummy.get(), 12, typeid(int));
type = LookupType(dummy.get());
ASSERT_NE(g_const_null, type);
EXPECT_STREQ(typeid(int).name(), type->name());
for (int i = 0; i < 10; ++i) {
EraseType(dummy.get());
type = LookupType(dummy.get());
EXPECT_EQ(g_const_null, type);
}
}
TEST(TypeProfilerMapTest, MultipleInsertWithoutErase) {
scoped_ptr<int> dummy(new int(48));
const std::type_info* type;
InsertType(dummy.get(), 12, typeid(int));
type = LookupType(dummy.get());
ASSERT_NE(g_const_null, type);
EXPECT_STREQ(typeid(int).name(), type->name());
InsertType(dummy.get(), 5, typeid(char));
type = LookupType(dummy.get());
ASSERT_NE(g_const_null, type);
EXPECT_STREQ(typeid(char).name(), type->name());
InsertType(dummy.get(), 129, typeid(long));
type = LookupType(dummy.get());
ASSERT_NE(g_const_null, type);
EXPECT_STREQ(typeid(long).name(), type->name());
EraseType(dummy.get());
type = LookupType(dummy.get());
EXPECT_EQ(g_const_null, type);
}
} // namespace type_profiler
} // namespace base
#endif // defined(TYPE_PROFILING)
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,37 @@
// Copyright 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.
#if defined(TYPE_PROFILING)
#include "base/allocator/type_profiler_tcmalloc.h"
#include "base/allocator/type_profiler_control.h"
#include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h"
#include "third_party/tcmalloc/chromium/src/gperftools/type_profiler_map.h"
namespace base {
namespace type_profiler {
void* NewInterceptForTCMalloc(void* ptr,
size_t size,
const std::type_info& type) {
if (Controller::IsProfiling())
InsertType(ptr, size, type);
return ptr;
}
void* DeleteInterceptForTCMalloc(void* ptr,
size_t size,
const std::type_info& type) {
if (Controller::IsProfiling())
EraseType(ptr);
return ptr;
}
} // namespace type_profiler
} // namespace base
#endif // defined(TYPE_PROFILING)

View File

@ -0,0 +1,29 @@
// Copyright 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_ALLOCATOR_TYPE_PROFILER_TCMALLOC_H_
#define BASE_ALLOCATOR_TYPE_PROFILER_TCMALLOC_H_
#if defined(TYPE_PROFILING)
#include <cstddef> // for size_t
#include <typeinfo> // for std::type_info
namespace base {
namespace type_profiler {
void* NewInterceptForTCMalloc(void* ptr,
size_t size,
const std::type_info& type);
void* DeleteInterceptForTCMalloc(void* ptr,
size_t size,
const std::type_info& type);
} // namespace type_profiler
} // namespace base
#endif // defined(TYPE_PROFILING)
#endif // BASE_ALLOCATOR_TYPE_PROFILER_TCMALLOC_H_

View File

@ -0,0 +1,189 @@
// Copyright 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 is a unittest set for type_profiler. It is independent from other
// tests and executed manually like allocator_unittests since type_profiler_map
// used in type_profiler is a singleton (like TCMalloc's heap-profiler), and
// it requires RTTI and different compiling/linking options from others
//
// It tests that the profiler doesn't fail in suspicous cases. For example,
// 'new' is not profiled, but 'delete' for the created object is profiled.
#if defined(TYPE_PROFILING)
#include "base/allocator/type_profiler.h"
#include "base/allocator/type_profiler_control.h"
#include "base/allocator/type_profiler_tcmalloc.h"
#include "base/basictypes.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/tcmalloc/chromium/src/gperftools/type_profiler_map.h"
namespace base {
namespace type_profiler {
class TypeProfilerTest : public testing::Test {
public:
TypeProfilerTest() {}
void SetInterceptFunctions() {
InterceptFunctions::SetFunctions(NewInterceptForTCMalloc,
DeleteInterceptForTCMalloc);
}
void ResetInterceptFunctions() {
InterceptFunctions::ResetFunctions();
}
void SetUp() {
SetInterceptFunctions();
}
void TearDown() {
ResetInterceptFunctions();
}
protected:
static const size_t kDummyArraySize;
static const void* const kConstNull;
private:
DISALLOW_COPY_AND_ASSIGN(TypeProfilerTest);
};
const size_t TypeProfilerTest::kDummyArraySize = 10;
const void* const TypeProfilerTest::kConstNull = static_cast<const void*>(NULL);
TEST_F(TypeProfilerTest, TestNormalProfiling) {
int* dummy = new int(48);
const std::type_info* type;
type = LookupType(dummy);
ASSERT_NE(kConstNull, type);
EXPECT_STREQ(typeid(int).name(), type->name());
delete dummy;
type = LookupType(dummy);
EXPECT_EQ(kConstNull, type);
}
TEST_F(TypeProfilerTest, TestNormalArrayProfiling) {
int* dummy = new int[kDummyArraySize];
const std::type_info* type;
type = LookupType(dummy);
ASSERT_NE(kConstNull, type);
// For an array, the profiler remembers its base type.
EXPECT_STREQ(typeid(int).name(), type->name());
delete[] dummy;
type = LookupType(dummy);
EXPECT_EQ(kConstNull, type);
}
TEST_F(TypeProfilerTest, TestRepeatedNewAndDelete) {
int *dummy[kDummyArraySize];
const std::type_info* type;
for (int i = 0; i < kDummyArraySize; ++i)
dummy[i] = new int(i);
for (int i = 0; i < kDummyArraySize; ++i) {
type = LookupType(dummy[i]);
ASSERT_NE(kConstNull, type);
EXPECT_STREQ(typeid(int).name(), type->name());
}
for (int i = 0; i < kDummyArraySize; ++i) {
delete dummy[i];
type = LookupType(dummy[i]);
ASSERT_EQ(kConstNull, type);
}
}
TEST_F(TypeProfilerTest, TestMultipleNewWithDroppingDelete) {
static const size_t large_size = 256 * 1024;
char* dummy_char = new char[large_size / sizeof(*dummy_char)];
const std::type_info* type;
type = LookupType(dummy_char);
ASSERT_NE(kConstNull, type);
EXPECT_STREQ(typeid(char).name(), type->name());
// Call "::operator delete" directly to drop __op_delete_intercept__.
::operator delete[](dummy_char);
type = LookupType(dummy_char);
ASSERT_NE(kConstNull, type);
EXPECT_STREQ(typeid(char).name(), type->name());
// Allocates a little different size.
int* dummy_int = new int[large_size / sizeof(*dummy_int) - 1];
// We expect that tcmalloc returns the same address for these large (over 32k)
// allocation calls. It usually happens, but maybe probablistic.
ASSERT_EQ(static_cast<void*>(dummy_char), static_cast<void*>(dummy_int)) <<
"two new (malloc) calls didn't return the same address; retry it.";
type = LookupType(dummy_int);
ASSERT_NE(kConstNull, type);
EXPECT_STREQ(typeid(int).name(), type->name());
delete[] dummy_int;
type = LookupType(dummy_int);
EXPECT_EQ(kConstNull, type);
}
TEST_F(TypeProfilerTest, TestProfileDeleteWithoutProfiledNew) {
// 'dummy' should be new'ed in this test before intercept functions are set.
ResetInterceptFunctions();
int* dummy = new int(48);
const std::type_info* type;
// Set intercept functions again after 'dummy' is new'ed.
SetInterceptFunctions();
delete dummy;
type = LookupType(dummy);
EXPECT_EQ(kConstNull, type);
ResetInterceptFunctions();
}
TEST_F(TypeProfilerTest, TestProfileNewWithoutProfiledDelete) {
int* dummy = new int(48);
const std::type_info* type;
EXPECT_TRUE(Controller::IsProfiling());
// Stop profiling before deleting 'dummy'.
Controller::Stop();
EXPECT_FALSE(Controller::IsProfiling());
delete dummy;
// NOTE: We accept that a profile entry remains when a profiled object is
// deleted after Controller::Stop().
type = LookupType(dummy);
ASSERT_NE(kConstNull, type);
EXPECT_STREQ(typeid(int).name(), type->name());
Controller::Restart();
EXPECT_TRUE(Controller::IsProfiling());
// Remove manually since 'dummy' is not removed from type_profiler_map.
EraseType(dummy);
}
} // namespace type_profiler
} // namespace base
#endif // defined(TYPE_PROFILING)
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,18 @@
// Copyright (c) 2009 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.
// The unittests need a this in order to link up without pulling in tons
// of other libraries
#include <config.h>
inline int snprintf(char* buffer, size_t count, const char* format, ...) {
int result;
va_list args;
va_start(args, format);
result = _vsnprintf(buffer, count, format, args);
va_end(args);
return result;
}

View File

@ -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.
// This is a simple allocator based on the windows heap.
extern "C" {
HANDLE win_heap;
bool win_heap_init(bool use_lfh) {
win_heap = HeapCreate(0, 0, 0);
if (win_heap == NULL)
return false;
if (use_lfh) {
ULONG enable_lfh = 2;
HeapSetInformation(win_heap, HeapCompatibilityInformation,
&enable_lfh, sizeof(enable_lfh));
// NOTE: Setting LFH may fail. Vista already has it enabled.
// And under the debugger, it won't use LFH. So we
// ignore any errors.
}
return true;
}
void* win_heap_malloc(size_t size) {
return HeapAlloc(win_heap, 0, size);
}
void win_heap_free(void* size) {
HeapFree(win_heap, 0, size);
}
void* win_heap_realloc(void* ptr, size_t size) {
if (!ptr)
return win_heap_malloc(size);
if (!size) {
win_heap_free(ptr);
return NULL;
}
return HeapReAlloc(win_heap, 0, ptr, size);
}
size_t win_heap_msize(void* ptr) {
return HeapSize(win_heap, 0, ptr);
}
void* win_heap_memalign(size_t alignment, size_t size) {
// Reserve enough space to ensure we can align and set aligned_ptr[-1] to the
// original allocation for use with win_heap_memalign_free() later.
size_t allocation_size = size + (alignment - 1) + sizeof(void*);
// Check for overflow. Alignment and size are checked in allocator_shim.
DCHECK_LT(size, allocation_size);
DCHECK_LT(alignment, allocation_size);
void* ptr = win_heap_malloc(allocation_size);
char* aligned_ptr = static_cast<char*>(ptr) + sizeof(void*);
aligned_ptr +=
alignment - reinterpret_cast<uintptr_t>(aligned_ptr) & (alignment - 1);
reinterpret_cast<void**>(aligned_ptr)[-1] = ptr;
return aligned_ptr;
}
void win_heap_memalign_free(void* ptr) {
if (ptr)
win_heap_free(static_cast<void**>(ptr)[-1]);
}
} // extern "C"

3
base/android/OWNERS Normal file
View File

@ -0,0 +1,3 @@
bulach@chromium.org
joth@chromium.org
yfriedman@chromium.org

View File

@ -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)

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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

View File

@ -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_

View File

@ -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

115
base/android/build_info.h Normal file
View File

@ -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_

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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_

View File

@ -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

32
base/android/fifo_utils.h Normal file
View File

@ -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_

View File

@ -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

View File

@ -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_

View File

@ -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 "";
}

View File

@ -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
}

View File

@ -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);
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}

View File

@ -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 "";
}

View File

@ -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 "";
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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();
}
}
}
}

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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());
}
}

View File

@ -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);
}
});
}
}

View File

@ -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

View File

@ -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_

View File

@ -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);
}
}

View File

@ -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);
}
}

323
base/android/jni_android.cc Normal file
View File

@ -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

132
base/android/jni_android.h Normal file
View File

@ -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_

View File

@ -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

186
base/android/jni_array.cc Normal file
View File

@ -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

86
base/android/jni_array.h Normal file
View File

@ -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_

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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',
],
},
],
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

41
base/android/jni_helper.h Normal file
View File

@ -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_

View File

@ -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

View File

@ -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_

View File

@ -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

45
base/android/jni_string.h Normal file
View File

@ -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_

View File

@ -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

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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_

View File

@ -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

48
base/android/path_utils.h Normal file
View File

@ -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_

View File

@ -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

View File

@ -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

Some files were not shown because too many files have changed in this diff Show More