Start with media/mp4, media/webm and base codes from Chromium.
This commit is contained in:
parent
1205b85f3e
commit
0f24c7f9ac
|
@ -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",
|
||||
]
|
|
@ -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
|
|
@ -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',
|
||||
]
|
|
@ -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
|
|
@ -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',
|
||||
],
|
||||
},
|
||||
],
|
||||
}],
|
||||
],
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
|
@ -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_
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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())
|
|
@ -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();
|
||||
}
|
|
@ -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)
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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();
|
||||
}
|
|
@ -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)
|
|
@ -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_
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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"
|
|
@ -0,0 +1,3 @@
|
|||
bulach@chromium.org
|
||||
joth@chromium.org
|
||||
yfriedman@chromium.org
|
|
@ -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)
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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 "";
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 "";
|
||||
}
|
|
@ -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 "";
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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
Loading…
Reference in New Issue