Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View file

@ -0,0 +1,10 @@
agrieve@chromium.org
lizeb@chromium.org
nyquist@chromium.org
pasko@chromium.org
rmcilroy@chromium.org
torne@chromium.org
yfriedman@chromium.org
per-file *.aidl=set noparent
per-file *.aidl=file://ipc/SECURITY_OWNERS

View file

@ -0,0 +1,122 @@
// Copyright 2017 The Chromium 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/android_hardware_buffer_compat.h"
#include <dlfcn.h>
#include "base/android/build_info.h"
#include "base/logging.h"
namespace base {
AndroidHardwareBufferCompat::AndroidHardwareBufferCompat() {
DCHECK(IsSupportAvailable());
// TODO(klausw): If the Chromium build requires __ANDROID_API__ >= 26 at some
// point in the future, we could directly use the global functions instead of
// dynamic loading. However, since this would be incompatible with pre-Oreo
// devices, this is unlikely to happen in the foreseeable future, so just
// unconditionally use dynamic loading.
// cf. base/android/linker/modern_linker_jni.cc
void* main_dl_handle = dlopen(nullptr, RTLD_NOW);
*reinterpret_cast<void**>(&allocate_) =
dlsym(main_dl_handle, "AHardwareBuffer_allocate");
DCHECK(allocate_);
*reinterpret_cast<void**>(&acquire_) =
dlsym(main_dl_handle, "AHardwareBuffer_acquire");
DCHECK(acquire_);
*reinterpret_cast<void**>(&describe_) =
dlsym(main_dl_handle, "AHardwareBuffer_describe");
DCHECK(describe_);
*reinterpret_cast<void**>(&lock_) =
dlsym(main_dl_handle, "AHardwareBuffer_lock");
DCHECK(lock_);
*reinterpret_cast<void**>(&recv_handle_) =
dlsym(main_dl_handle, "AHardwareBuffer_recvHandleFromUnixSocket");
DCHECK(recv_handle_);
*reinterpret_cast<void**>(&release_) =
dlsym(main_dl_handle, "AHardwareBuffer_release");
DCHECK(release_);
*reinterpret_cast<void**>(&send_handle_) =
dlsym(main_dl_handle, "AHardwareBuffer_sendHandleToUnixSocket");
DCHECK(send_handle_);
*reinterpret_cast<void**>(&unlock_) =
dlsym(main_dl_handle, "AHardwareBuffer_unlock");
DCHECK(unlock_);
}
// static
bool AndroidHardwareBufferCompat::IsSupportAvailable() {
return base::android::BuildInfo::GetInstance()->sdk_int() >=
base::android::SDK_VERSION_OREO;
}
// static
AndroidHardwareBufferCompat& AndroidHardwareBufferCompat::GetInstance() {
static base::NoDestructor<AndroidHardwareBufferCompat> compat;
return *compat;
}
void AndroidHardwareBufferCompat::Allocate(const AHardwareBuffer_Desc* desc,
AHardwareBuffer** out_buffer) {
DCHECK(IsSupportAvailable());
allocate_(desc, out_buffer);
}
void AndroidHardwareBufferCompat::Acquire(AHardwareBuffer* buffer) {
DCHECK(IsSupportAvailable());
acquire_(buffer);
}
void AndroidHardwareBufferCompat::Describe(const AHardwareBuffer* buffer,
AHardwareBuffer_Desc* out_desc) {
DCHECK(IsSupportAvailable());
describe_(buffer, out_desc);
}
int AndroidHardwareBufferCompat::Lock(AHardwareBuffer* buffer,
uint64_t usage,
int32_t fence,
const ARect* rect,
void** out_virtual_address) {
DCHECK(IsSupportAvailable());
return lock_(buffer, usage, fence, rect, out_virtual_address);
}
int AndroidHardwareBufferCompat::RecvHandleFromUnixSocket(
int socket_fd,
AHardwareBuffer** out_buffer) {
DCHECK(IsSupportAvailable());
return recv_handle_(socket_fd, out_buffer);
}
void AndroidHardwareBufferCompat::Release(AHardwareBuffer* buffer) {
DCHECK(IsSupportAvailable());
release_(buffer);
}
int AndroidHardwareBufferCompat::SendHandleToUnixSocket(
const AHardwareBuffer* buffer,
int socket_fd) {
DCHECK(IsSupportAvailable());
return send_handle_(buffer, socket_fd);
}
int AndroidHardwareBufferCompat::Unlock(AHardwareBuffer* buffer,
int32_t* fence) {
DCHECK(IsSupportAvailable());
return unlock_(buffer, fence);
}
} // namespace base

View file

@ -0,0 +1,76 @@
// Copyright 2017 The Chromium 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_ANDROID_HARDWARE_BUFFER_COMPAT_H_
#define BASE_ANDROID_ANDROID_HARDWARE_BUFFER_COMPAT_H_
#include <android/hardware_buffer.h>
#include <android/sensor.h>
#include "base/base_export.h"
#include "base/macros.h"
#include "base/no_destructor.h"
extern "C" {
using PFAHardwareBuffer_allocate = void (*)(const AHardwareBuffer_Desc* desc,
AHardwareBuffer** outBuffer);
using PFAHardwareBuffer_acquire = void (*)(AHardwareBuffer* buffer);
using PFAHardwareBuffer_describe = void (*)(const AHardwareBuffer* buffer,
AHardwareBuffer_Desc* outDesc);
using PFAHardwareBuffer_lock = int (*)(AHardwareBuffer* buffer,
uint64_t usage,
int32_t fence,
const ARect* rect,
void** outVirtualAddress);
using PFAHardwareBuffer_recvHandleFromUnixSocket =
int (*)(int socketFd, AHardwareBuffer** outBuffer);
using PFAHardwareBuffer_release = void (*)(AHardwareBuffer* buffer);
using PFAHardwareBuffer_sendHandleToUnixSocket =
int (*)(const AHardwareBuffer* buffer, int socketFd);
using PFAHardwareBuffer_unlock = int (*)(AHardwareBuffer* buffer,
int32_t* fence);
}
namespace base {
// This class provides runtime support for working with AHardwareBuffer objects
// on Android O systems without requiring building for the Android O NDK level.
// Don't call GetInstance() unless IsSupportAvailable() returns true.
class BASE_EXPORT AndroidHardwareBufferCompat {
public:
static bool IsSupportAvailable();
static AndroidHardwareBufferCompat& GetInstance();
void Allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer);
void Acquire(AHardwareBuffer* buffer);
void Describe(const AHardwareBuffer* buffer, AHardwareBuffer_Desc* outDesc);
int Lock(AHardwareBuffer* buffer,
uint64_t usage,
int32_t fence,
const ARect* rect,
void** out_virtual_address);
int RecvHandleFromUnixSocket(int socketFd, AHardwareBuffer** outBuffer);
void Release(AHardwareBuffer* buffer);
int SendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd);
int Unlock(AHardwareBuffer* buffer, int32_t* fence);
private:
friend class NoDestructor<AndroidHardwareBufferCompat>;
AndroidHardwareBufferCompat();
PFAHardwareBuffer_allocate allocate_;
PFAHardwareBuffer_acquire acquire_;
PFAHardwareBuffer_describe describe_;
PFAHardwareBuffer_lock lock_;
PFAHardwareBuffer_recvHandleFromUnixSocket recv_handle_;
PFAHardwareBuffer_release release_;
PFAHardwareBuffer_sendHandleToUnixSocket send_handle_;
PFAHardwareBuffer_unlock unlock_;
DISALLOW_COPY_AND_ASSIGN(AndroidHardwareBufferCompat);
};
} // namespace base
#endif // BASE_ANDROID_ANDROID_HARDWARE_BUFFER_COMPAT_H_

View file

@ -0,0 +1,116 @@
// Copyright 2018 The Chromium 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_ANDROID_IMAGE_READER_ABI_H_
#define BASE_ANDROID_ANDROID_IMAGE_READER_ABI_H_
// Minimal binary interface definitions for AImage,AImageReader
// and ANativeWindow based on include/media/NdkImage.h,
// include/media/NdkImageReader.h and include/android/native_window_jni.h
// from the Android NDK for platform level 26+. This is only
// intended for use from the AndroidImageReader wrapper for building
// without NDK platform level support, it is not a general-use header
// and is not complete. Only the functions/data types which
// are currently needed by media/gpu/android/image_reader_gl_owner.h are
// included in this ABI
//
// Please refer to the API documentation for details:
// https://developer.android.com/ndk/reference/group/media (AIMage and
// AImageReader)
// https://developer.android.com/ndk/reference/group/native-activity
// (ANativeWindow)
#include <android/native_window.h>
#include <media/NdkMediaError.h>
#include <jni.h>
#include <stdint.h>
// Use "C" linkage to match the original header file. This isn't strictly
// required since the file is not declaring global functions, but the types
// should remain in the global namespace for compatibility, and it's a reminder
// that forward declarations elsewhere should use "extern "C" to avoid
// namespace issues.
extern "C" {
// For AImage
typedef struct AHardwareBuffer AHardwareBuffer;
typedef struct AImage AImage;
typedef struct AImageCropRect {
int32_t left;
int32_t top;
int32_t right;
int32_t bottom;
} AImageCropRect;
enum AIMAGE_FORMATS {
AIMAGE_FORMAT_YUV_420_888 = 0x23,
AIMAGE_FORMAT_PRIVATE = 0x22
};
using pAImage_delete = void (*)(AImage* image);
using pAImage_deleteAsync = void (*)(AImage* image, int releaseFenceFd);
using pAImage_getHardwareBuffer = media_status_t (*)(const AImage* image,
AHardwareBuffer** buffer);
using pAImage_getWidth = media_status_t (*)(const AImage* image,
int32_t* width);
using pAImage_getHeight = media_status_t (*)(const AImage* image,
int32_t* height);
using pAImage_getCropRect = media_status_t (*)(const AImage* image,
AImageCropRect* rect);
// For AImageReader
typedef struct AImageReader AImageReader;
typedef void (*AImageReader_ImageCallback)(void* context, AImageReader* reader);
typedef struct AImageReader_ImageListener {
void* context;
AImageReader_ImageCallback onImageAvailable;
} AImageReader_ImageListener;
using pAImageReader_newWithUsage = media_status_t (*)(int32_t width,
int32_t height,
int32_t format,
uint64_t usage,
int32_t maxImages,
AImageReader** reader);
using pAImageReader_setImageListener =
media_status_t (*)(AImageReader* reader,
AImageReader_ImageListener* listener);
using pAImageReader_delete = void (*)(AImageReader* reader);
using pAImageReader_getWindow = media_status_t (*)(AImageReader* reader,
ANativeWindow** window);
using pAImageReader_getFormat = media_status_t (*)(const AImageReader* reader,
int32_t* format);
using pAImageReader_acquireLatestImageAsync =
media_status_t (*)(AImageReader* reader,
AImage** image,
int* acquireFenceFd);
using pAImageReader_acquireNextImageAsync =
media_status_t (*)(AImageReader* reader,
AImage** image,
int* acquireFenceFd);
// For ANativeWindow
using pANativeWindow_toSurface = jobject (*)(JNIEnv* env,
ANativeWindow* window);
} // extern "C"
#endif // BASE_ANDROID_ANDROID_IMAGE_READER_ABI_H_

View file

@ -0,0 +1,167 @@
// Copyright 2018 The Chromium 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/android_image_reader_compat.h"
#include <dlfcn.h>
#include "base/android/build_info.h"
#include "base/feature_list.h"
#include "base/logging.h"
#define LOAD_FUNCTION(lib, func) \
do { \
func##_ = reinterpret_cast<p##func>(dlsym(lib, #func)); \
if (!func##_) { \
DLOG(ERROR) << "Unable to load function " << #func; \
return false; \
} \
} while (0)
namespace base {
namespace android {
AndroidImageReader& AndroidImageReader::GetInstance() {
// C++11 static local variable initialization is
// thread-safe.
static base::NoDestructor<AndroidImageReader> instance;
return *instance;
}
bool AndroidImageReader::IsSupported() {
return is_supported_;
}
AndroidImageReader::AndroidImageReader() {
is_supported_ = LoadFunctions();
}
bool AndroidImageReader::LoadFunctions() {
// If the Chromium build requires __ANDROID_API__ >= 26 at some
// point in the future, we could directly use the global functions instead of
// dynamic loading. However, since this would be incompatible with pre-Oreo
// devices, this is unlikely to happen in the foreseeable future, so we use
// dynamic loading.
// Functions are not present for android version older than OREO.
// Currently we want to enable AImageReader only for android P+ devices.
if (base::android::BuildInfo::GetInstance()->sdk_int() <
base::android::SDK_VERSION_P) {
return false;
}
void* libmediandk = dlopen("libmediandk.so", RTLD_NOW);
if (libmediandk == nullptr) {
LOG(ERROR) << "Couldnt open libmediandk.so";
return false;
}
LOAD_FUNCTION(libmediandk, AImage_delete);
LOAD_FUNCTION(libmediandk, AImage_deleteAsync);
LOAD_FUNCTION(libmediandk, AImage_getHardwareBuffer);
LOAD_FUNCTION(libmediandk, AImage_getWidth);
LOAD_FUNCTION(libmediandk, AImage_getHeight);
LOAD_FUNCTION(libmediandk, AImage_getCropRect);
LOAD_FUNCTION(libmediandk, AImageReader_newWithUsage);
LOAD_FUNCTION(libmediandk, AImageReader_setImageListener);
LOAD_FUNCTION(libmediandk, AImageReader_delete);
LOAD_FUNCTION(libmediandk, AImageReader_getFormat);
LOAD_FUNCTION(libmediandk, AImageReader_getWindow);
LOAD_FUNCTION(libmediandk, AImageReader_acquireLatestImageAsync);
LOAD_FUNCTION(libmediandk, AImageReader_acquireNextImageAsync);
void* libandroid = dlopen("libandroid.so", RTLD_NOW);
if (libandroid == nullptr) {
LOG(ERROR) << "Couldnt open libandroid.so";
return false;
}
LOAD_FUNCTION(libandroid, ANativeWindow_toSurface);
return true;
}
void AndroidImageReader::AImage_delete(AImage* image) {
AImage_delete_(image);
}
void AndroidImageReader::AImage_deleteAsync(AImage* image, int releaseFenceFd) {
AImage_deleteAsync_(image, releaseFenceFd);
}
media_status_t AndroidImageReader::AImage_getHardwareBuffer(
const AImage* image,
AHardwareBuffer** buffer) {
return AImage_getHardwareBuffer_(image, buffer);
}
media_status_t AndroidImageReader::AImage_getWidth(const AImage* image,
int32_t* width) {
return AImage_getWidth_(image, width);
}
media_status_t AndroidImageReader::AImage_getHeight(const AImage* image,
int32_t* height) {
return AImage_getHeight_(image, height);
}
media_status_t AndroidImageReader::AImage_getCropRect(const AImage* image,
AImageCropRect* rect) {
return AImage_getCropRect_(image, rect);
}
media_status_t AndroidImageReader::AImageReader_newWithUsage(
int32_t width,
int32_t height,
int32_t format,
uint64_t usage,
int32_t maxImages,
AImageReader** reader) {
return AImageReader_newWithUsage_(width, height, format, usage, maxImages,
reader);
}
media_status_t AndroidImageReader::AImageReader_setImageListener(
AImageReader* reader,
AImageReader_ImageListener* listener) {
return AImageReader_setImageListener_(reader, listener);
}
void AndroidImageReader::AImageReader_delete(AImageReader* reader) {
AImageReader_delete_(reader);
}
media_status_t AndroidImageReader::AImageReader_getFormat(
const AImageReader* reader,
int32_t* format) {
return AImageReader_getFormat_(reader, format);
}
media_status_t AndroidImageReader::AImageReader_getWindow(
AImageReader* reader,
ANativeWindow** window) {
return AImageReader_getWindow_(reader, window);
}
media_status_t AndroidImageReader::AImageReader_acquireLatestImageAsync(
AImageReader* reader,
AImage** image,
int* acquireFenceFd) {
return AImageReader_acquireLatestImageAsync_(reader, image, acquireFenceFd);
}
media_status_t AndroidImageReader::AImageReader_acquireNextImageAsync(
AImageReader* reader,
AImage** image,
int* acquireFenceFd) {
return AImageReader_acquireNextImageAsync_(reader, image, acquireFenceFd);
}
jobject AndroidImageReader::ANativeWindow_toSurface(JNIEnv* env,
ANativeWindow* window) {
return ANativeWindow_toSurface_(env, window);
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,89 @@
// Copyright 2018 The Chromium 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_ANDROID_IMAGE_READER_COMPAT_H_
#define BASE_ANDROID_ANDROID_IMAGE_READER_COMPAT_H_
#include "base/android/android_image_reader_abi.h"
#include "base/base_export.h"
#include "base/macros.h"
#include "base/no_destructor.h"
namespace base {
namespace android {
// This class provides runtime support for working with AImage, AImageReader and
// ANativeWindow objects on Android O systems without requiring building for the
// Android O NDK level. Don't call GetInstance() unless IsSupported() returns
// true.
class BASE_EXPORT AndroidImageReader {
public:
// Thread safe GetInstance.
static AndroidImageReader& GetInstance();
// Check if the image reader usage is supported. This function returns TRUE
// if android version is >=OREO, image reader support is not disabled and all
// the required functions are loaded.
bool IsSupported();
// Naming convention of all the below functions are chosen to exactly match
// the function names in the NDK.
void AImage_delete(AImage* image);
void AImage_deleteAsync(AImage* image, int releaseFenceFd);
media_status_t AImage_getHardwareBuffer(const AImage* image,
AHardwareBuffer** buffer);
media_status_t AImage_getWidth(const AImage* image, int32_t* width);
media_status_t AImage_getHeight(const AImage* image, int32_t* height);
media_status_t AImage_getCropRect(const AImage* image, AImageCropRect* rect);
media_status_t AImageReader_newWithUsage(int32_t width,
int32_t height,
int32_t format,
uint64_t usage,
int32_t maxImages,
AImageReader** reader);
media_status_t AImageReader_setImageListener(
AImageReader* reader,
AImageReader_ImageListener* listener);
void AImageReader_delete(AImageReader* reader);
media_status_t AImageReader_getFormat(const AImageReader* reader,
int32_t* format);
media_status_t AImageReader_getWindow(AImageReader* reader,
ANativeWindow** window);
media_status_t AImageReader_acquireLatestImageAsync(AImageReader* reader,
AImage** image,
int* acquireFenceFd);
media_status_t AImageReader_acquireNextImageAsync(AImageReader* reader,
AImage** image,
int* acquireFenceFd);
jobject ANativeWindow_toSurface(JNIEnv* env, ANativeWindow* window);
private:
friend class base::NoDestructor<AndroidImageReader>;
AndroidImageReader();
bool LoadFunctions();
bool is_supported_;
pAImage_delete AImage_delete_;
pAImage_deleteAsync AImage_deleteAsync_;
pAImage_getHardwareBuffer AImage_getHardwareBuffer_;
pAImage_getWidth AImage_getWidth_;
pAImage_getHeight AImage_getHeight_;
pAImage_getCropRect AImage_getCropRect_;
pAImageReader_newWithUsage AImageReader_newWithUsage_;
pAImageReader_setImageListener AImageReader_setImageListener_;
pAImageReader_delete AImageReader_delete_;
pAImageReader_getFormat AImageReader_getFormat_;
pAImageReader_getWindow AImageReader_getWindow_;
pAImageReader_acquireLatestImageAsync AImageReader_acquireLatestImageAsync_;
pAImageReader_acquireNextImageAsync AImageReader_acquireNextImageAsync_;
pANativeWindow_toSurface ANativeWindow_toSurface_;
DISALLOW_COPY_AND_ASSIGN(AndroidImageReader);
};
} // namespace android
} // namespace base
#endif // BASE_ANDROID_ANDROID_IMAGE_READER_COMPAT_H_

View file

@ -0,0 +1,28 @@
// Copyright 2015 The Chromium 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/base_jni_headers/AnimationFrameTimeHistogram_jni.h"
#include "base/metrics/histogram_macros.h"
using base::android::JavaParamRef;
// static
void JNI_AnimationFrameTimeHistogram_SaveHistogram(
JNIEnv* env,
const JavaParamRef<jstring>& j_histogram_name,
const JavaParamRef<jlongArray>& j_frame_times_ms,
jint j_count) {
jlong* frame_times_ms =
env->GetLongArrayElements(j_frame_times_ms.obj(), nullptr);
std::string histogram_name = base::android::ConvertJavaStringToUTF8(
env, j_histogram_name);
for (int i = 0; i < j_count; ++i) {
UMA_HISTOGRAM_TIMES(histogram_name.c_str(),
base::TimeDelta::FromMilliseconds(frame_times_ms[i]));
}
env->ReleaseLongArrayElements(j_frame_times_ms.obj(), frame_times_ms,
JNI_ABORT);
}

View file

@ -0,0 +1,47 @@
// Copyright 2015 The Chromium 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 "base/android/apk_assets.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/base_jni_headers/ApkAssets_jni.h"
#include "base/file_descriptor_store.h"
namespace base {
namespace android {
int OpenApkAsset(const std::string& file_path,
base::MemoryMappedFile::Region* region) {
// The AssetManager API of the NDK does not expose a method for accessing raw
// resources :(
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jlongArray> jarr = Java_ApkAssets_open(
env, base::android::ConvertUTF8ToJavaString(env, file_path));
std::vector<jlong> results;
base::android::JavaLongArrayToLongVector(env, jarr, &results);
CHECK_EQ(3U, results.size());
int fd = static_cast<int>(results[0]);
region->offset = results[1];
region->size = results[2];
return fd;
}
bool RegisterApkAssetWithFileDescriptorStore(const std::string& key,
const base::FilePath& file_path) {
base::MemoryMappedFile::Region region =
base::MemoryMappedFile::Region::kWholeFile;
int asset_fd = OpenApkAsset(file_path.value(), &region);
if (asset_fd == -1)
return false;
base::FileDescriptorStore::GetInstance().Set(key, base::ScopedFD(asset_fd),
region);
return true;
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,39 @@
// Copyright 2015 The Chromium 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_APK_ASSETS_H_
#define BASE_ANDROID_APK_ASSETS_H_
#include <string>
#include "base/android/jni_android.h"
#include "base/files/file_path.h"
#include "base/files/memory_mapped_file.h"
namespace base {
namespace android {
// Opens an asset (e.g. a .pak file) from the apk.
// Can be used from renderer process.
// Fails if the asset is not stored uncompressed within the .apk.
// Returns: The File Descriptor of the asset, or -1 upon failure.
// Input arguments:
// - |file_path|: Path to file within .apk. e.g.: assets/foo.pak
// Output arguments:
// - |region|: size & offset (in bytes) within the .apk of the asset.
BASE_EXPORT int OpenApkAsset(
const std::string& file_path,
base::MemoryMappedFile::Region* region);
// Registers an uncompressed asset from within the apk in the
// FileDescriptorStore.
// Returns: true in case of success, false otherwise.
BASE_EXPORT bool RegisterApkAssetWithFileDescriptorStore(
const std::string& key,
const base::FilePath& file_path);
} // namespace android
} // namespace base
#endif // BASE_ANDROID_APK_ASSETS_H_

View file

@ -0,0 +1,121 @@
// Copyright 2014 The Chromium 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/application_status_listener.h"
#include <jni.h>
#include "base/base_jni_headers/ApplicationStatus_jni.h"
#include "base/lazy_instance.h"
#include "base/metrics/user_metrics.h"
#include "base/observer_list_threadsafe.h"
namespace base {
namespace android {
namespace {
class ApplicationStatusListenerImpl;
struct LeakyLazyObserverListTraits
: base::internal::LeakyLazyInstanceTraits<
ObserverListThreadSafe<ApplicationStatusListenerImpl>> {
static ObserverListThreadSafe<ApplicationStatusListenerImpl>* New(
void* instance) {
ObserverListThreadSafe<ApplicationStatusListenerImpl>* ret =
base::internal::LeakyLazyInstanceTraits<ObserverListThreadSafe<
ApplicationStatusListenerImpl>>::New(instance);
// Leaky.
ret->AddRef();
return ret;
}
};
LazyInstance<ObserverListThreadSafe<ApplicationStatusListenerImpl>,
LeakyLazyObserverListTraits>
g_observers = LAZY_INSTANCE_INITIALIZER;
class ApplicationStatusListenerImpl : public ApplicationStatusListener {
public:
ApplicationStatusListenerImpl(
const ApplicationStateChangeCallback& callback) {
SetCallback(callback);
g_observers.Get().AddObserver(this);
Java_ApplicationStatus_registerThreadSafeNativeApplicationStateListener(
AttachCurrentThread());
}
~ApplicationStatusListenerImpl() override {
g_observers.Get().RemoveObserver(this);
}
void SetCallback(const ApplicationStateChangeCallback& callback) override {
DCHECK(!callback_);
DCHECK(callback);
callback_ = callback;
}
void Notify(ApplicationState state) override {
if (callback_)
callback_.Run(state);
}
private:
ApplicationStateChangeCallback callback_;
};
} // namespace
ApplicationStatusListener::ApplicationStatusListener() = default;
ApplicationStatusListener::~ApplicationStatusListener() = default;
// static
std::unique_ptr<ApplicationStatusListener> ApplicationStatusListener::New(
const ApplicationStateChangeCallback& callback) {
return std::make_unique<ApplicationStatusListenerImpl>(callback);
}
// static
void ApplicationStatusListener::NotifyApplicationStateChange(
ApplicationState state) {
TRACE_COUNTER1("browser", "ApplicationState", static_cast<int>(state));
switch (state) {
case APPLICATION_STATE_UNKNOWN:
case APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES:
break;
case APPLICATION_STATE_HAS_RUNNING_ACTIVITIES:
RecordAction(UserMetricsAction("Android.LifeCycle.HasRunningActivities"));
break;
case APPLICATION_STATE_HAS_PAUSED_ACTIVITIES:
RecordAction(UserMetricsAction("Android.LifeCycle.HasPausedActivities"));
break;
case APPLICATION_STATE_HAS_STOPPED_ACTIVITIES:
RecordAction(UserMetricsAction("Android.LifeCycle.HasStoppedActivities"));
break;
}
g_observers.Get().Notify(FROM_HERE, &ApplicationStatusListenerImpl::Notify,
state);
}
// static
ApplicationState ApplicationStatusListener::GetState() {
return static_cast<ApplicationState>(
Java_ApplicationStatus_getStateForApplication(AttachCurrentThread()));
}
static void JNI_ApplicationStatus_OnApplicationStateChange(
JNIEnv* env,
jint new_state) {
ApplicationState application_state = static_cast<ApplicationState>(new_state);
ApplicationStatusListener::NotifyApplicationStateChange(application_state);
}
// static
bool ApplicationStatusListener::HasVisibleActivities() {
return Java_ApplicationStatus_hasVisibleActivities(AttachCurrentThread());
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,97 @@
// Copyright 2014 The Chromium 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_APPLICATION_STATUS_LISTENER_H_
#define BASE_ANDROID_APPLICATION_STATUS_LISTENER_H_
#include <jni.h>
#include "base/android/jni_android.h"
#include "base/base_export.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/singleton.h"
namespace base {
namespace android {
// Define application state values like APPLICATION_STATE_VISIBLE in a
// way that ensures they're always the same than their Java counterpart.
//
// Note that these states represent the most visible Activity state.
// If there are activities with states paused and stopped, only
// HAS_PAUSED_ACTIVITIES should be returned.
//
// A Java counterpart will be generated for this enum.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base
enum ApplicationState {
APPLICATION_STATE_UNKNOWN = 0,
APPLICATION_STATE_HAS_RUNNING_ACTIVITIES = 1,
APPLICATION_STATE_HAS_PAUSED_ACTIVITIES = 2,
APPLICATION_STATE_HAS_STOPPED_ACTIVITIES = 3,
APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES = 4
};
// A native helper class to listen to state changes of the Android
// Application. This mirrors org.chromium.base.ApplicationStatus.
// any thread.
//
// To start listening, create a new instance, passing a callback to a
// function that takes an ApplicationState 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 OnApplicationStateChange(ApplicationState state) {
// ...
// }
//
// // Start listening.
// auto my_listener = ApplicationStatusListener::New(
// base::BindRepeating(&OnApplicationStateChange));
//
// ...
//
// // Stop listening.
// my_listener.reset();
//
class BASE_EXPORT ApplicationStatusListener {
public:
using ApplicationStateChangeCallback =
base::RepeatingCallback<void(ApplicationState)>;
virtual ~ApplicationStatusListener();
// Sets the callback to call when application state changes.
virtual void SetCallback(const ApplicationStateChangeCallback& callback) = 0;
// Notify observers that application state has changed.
virtual void Notify(ApplicationState state) = 0;
// Create a new listener. This object should only be used on a single thread.
static std::unique_ptr<ApplicationStatusListener> New(
const ApplicationStateChangeCallback& callback);
// Internal use only: must be public to be called from JNI and unit tests.
static void NotifyApplicationStateChange(ApplicationState state);
// Expose jni call for ApplicationStatus.getStateForApplication.
static ApplicationState GetState();
// Returns true if the app is currently foregrounded.
static bool HasVisibleActivities();
protected:
ApplicationStatusListener();
private:
DISALLOW_COPY_AND_ASSIGN(ApplicationStatusListener);
};
} // namespace android
} // namespace base
#endif // BASE_ANDROID_APPLICATION_STATUS_LISTENER_H_

View file

@ -0,0 +1,24 @@
// Copyright 2015 The Chromium 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_onload.h"
#include "base/android/jni_android.h"
#include "base/android/jni_utils.h"
#include "base/android/library_loader/library_loader_hooks.h"
#include "base/bind.h"
namespace base {
namespace android {
bool OnJNIOnLoadInit() {
InitAtExitManager();
JNIEnv* env = base::android::AttachCurrentThread();
base::android::InitReplacementClassLoader(env,
base::android::GetClassLoader(env));
return true;
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,23 @@
// Copyright 2015 The Chromium 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_ONLOAD_H_
#define BASE_ANDROID_BASE_JNI_ONLOAD_H_
#include <jni.h>
#include <vector>
#include "base/base_export.h"
#include "base/callback.h"
namespace base {
namespace android {
// Returns whether initialization succeeded.
BASE_EXPORT bool OnJNIOnLoadInit();
} // namespace android
} // namespace base
#endif // BASE_ANDROID_BASE_JNI_ONLOAD_H_

View file

@ -0,0 +1,89 @@
// 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_array.h"
#include "base/android/scoped_java_ref.h"
#include "base/base_jni_headers/BuildInfo_jni.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/strings/string_number_conversions.h"
namespace base {
namespace android {
namespace {
// We are leaking these strings.
const char* StrDupParam(const std::vector<std::string>& params, int index) {
return strdup(params[index].c_str());
}
int GetIntParam(const std::vector<std::string>& params, int index) {
int ret = 0;
bool success = StringToInt(params[index], &ret);
DCHECK(success);
return ret;
}
} // namespace
struct BuildInfoSingletonTraits {
static BuildInfo* New() {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobjectArray> params_objs = Java_BuildInfo_getAll(env);
std::vector<std::string> params;
AppendJavaStringArrayToStringVector(env, params_objs, &params);
return new BuildInfo(params);
}
static void Delete(BuildInfo* x) {
// We're leaking this type, see kRegisterAtExit.
NOTREACHED();
}
static const bool kRegisterAtExit = false;
#if DCHECK_IS_ON()
static const bool kAllowedToAccessOnNonjoinableThread = true;
#endif
};
BuildInfo::BuildInfo(const std::vector<std::string>& params)
: brand_(StrDupParam(params, 0)),
device_(StrDupParam(params, 1)),
android_build_id_(StrDupParam(params, 2)),
manufacturer_(StrDupParam(params, 3)),
model_(StrDupParam(params, 4)),
sdk_int_(GetIntParam(params, 5)),
build_type_(StrDupParam(params, 6)),
board_(StrDupParam(params, 7)),
host_package_name_(StrDupParam(params, 8)),
host_version_code_(StrDupParam(params, 9)),
host_package_label_(StrDupParam(params, 10)),
package_name_(StrDupParam(params, 11)),
package_version_code_(StrDupParam(params, 12)),
package_version_name_(StrDupParam(params, 13)),
android_build_fp_(StrDupParam(params, 14)),
gms_version_code_(StrDupParam(params, 15)),
installer_package_name_(StrDupParam(params, 16)),
abi_name_(StrDupParam(params, 17)),
firebase_app_id_(StrDupParam(params, 18)),
custom_themes_(StrDupParam(params, 19)),
resources_version_(StrDupParam(params, 20)),
extracted_file_suffix_(params[21]),
is_at_least_q_(GetIntParam(params, 22)),
targets_at_least_r_(GetIntParam(params, 23)),
is_debug_android_(GetIntParam(params, 24)) {}
// static
BuildInfo* BuildInfo::GetInstance() {
return Singleton<BuildInfo, BuildInfoSingletonTraits >::get();
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,173 @@
// 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 <vector>
#include "base/base_export.h"
#include "base/macros.h"
#include "base/memory/singleton.h"
namespace base {
namespace android {
// This enumeration maps to the values returned by BuildInfo::sdk_int(),
// indicating the Android release associated with a given SDK version.
enum SdkVersion {
SDK_VERSION_JELLY_BEAN = 16,
SDK_VERSION_JELLY_BEAN_MR1 = 17,
SDK_VERSION_JELLY_BEAN_MR2 = 18,
SDK_VERSION_KITKAT = 19,
SDK_VERSION_KITKAT_WEAR = 20,
SDK_VERSION_LOLLIPOP = 21,
SDK_VERSION_LOLLIPOP_MR1 = 22,
SDK_VERSION_MARSHMALLOW = 23,
SDK_VERSION_NOUGAT = 24,
SDK_VERSION_NOUGAT_MR1 = 25,
SDK_VERSION_OREO = 26,
SDK_VERSION_O_MR1 = 27,
SDK_VERSION_P = 28,
};
// 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.
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* manufacturer() const {
return manufacturer_;
}
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* gms_version_code() const {
return gms_version_code_;
}
const char* host_package_name() const { return host_package_name_; }
const char* host_version_code() const { return host_version_code_; }
const char* host_package_label() const { return host_package_label_; }
const char* package_version_code() const {
return package_version_code_;
}
const char* package_version_name() const {
return package_version_name_;
}
const char* package_name() const {
return package_name_;
}
// Will be empty string if no app id is assigned.
const char* firebase_app_id() const { return firebase_app_id_; }
const char* custom_themes() const { return custom_themes_; }
const char* resources_version() const { return resources_version_; }
const char* build_type() const {
return build_type_;
}
const char* board() const { return board_; }
const char* installer_package_name() const { return installer_package_name_; }
const char* abi_name() const { return abi_name_; }
std::string extracted_file_suffix() const { return extracted_file_suffix_; }
int sdk_int() const {
return sdk_int_;
}
bool is_at_least_q() const { return is_at_least_q_; }
bool targets_at_least_r() const { return targets_at_least_r_; }
bool is_debug_android() const { return is_debug_android_; }
private:
friend struct BuildInfoSingletonTraits;
explicit BuildInfo(const std::vector<std::string>& params);
// 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 brand_;
const char* const device_;
const char* const android_build_id_;
const char* const manufacturer_;
const char* const model_;
const int sdk_int_;
const char* const build_type_;
const char* const board_;
const char* const host_package_name_;
const char* const host_version_code_;
const char* const host_package_label_;
const char* const package_name_;
const char* const package_version_code_;
const char* const package_version_name_;
const char* const android_build_fp_;
const char* const gms_version_code_;
const char* const installer_package_name_;
const char* const abi_name_;
const char* const firebase_app_id_;
const char* const custom_themes_;
const char* const resources_version_;
// Not needed by breakpad.
const std::string extracted_file_suffix_;
const bool is_at_least_q_;
const bool targets_at_least_r_;
const bool is_debug_android_;
DISALLOW_COPY_AND_ASSIGN(BuildInfo);
};
} // namespace android
} // namespace base
#endif // BASE_ANDROID_BUILD_INFO_H_

View file

@ -0,0 +1,104 @@
// Copyright 2019 The Chromium 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/bundle_utils.h"
#include <android/dlext.h>
#include <dlfcn.h>
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/base_jni_headers/BundleUtils_jni.h"
#include "base/files/file_path.h"
#include "base/logging.h"
// These symbols are added by the lld linker when creating a partitioned shared
// library. The symbols live in the base library, and are used to properly load
// the other partitions (feature libraries) when needed.
struct PartitionIndexEntry {
int32_t name_relptr;
int32_t addr_relptr;
uint32_t size;
};
static_assert(sizeof(PartitionIndexEntry) == 12U,
"Unexpected PartitionIndexEntry size");
// Marked as weak_import because these symbols are lld-specific. The method that
// uses them will only be invoked in builds that have lld-generated partitions.
extern PartitionIndexEntry __part_index_begin[] __attribute__((weak_import));
extern PartitionIndexEntry __part_index_end[] __attribute__((weak_import));
extern "C" {
// Marked as weak_import because this symbol is either supplied by the system
// (on Android N+), or by Crazy Linker (Android M and prior).
extern void* android_dlopen_ext(const char* __filename,
int __flags,
const android_dlextinfo* __info)
__attribute__((weak_import));
} // extern "C"
namespace base {
namespace android {
namespace {
const void* ReadRelPtr(const int32_t* relptr) {
return reinterpret_cast<const char*>(relptr) + *relptr;
}
} // namespace
// static
std::string BundleUtils::ResolveLibraryPath(const std::string& library_name) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> java_path = Java_BundleUtils_getNativeLibraryPath(
env, base::android::ConvertUTF8ToJavaString(env, library_name));
// TODO(https://crbug.com/1019853): Remove this tolerance.
if (!java_path) {
return std::string();
}
return base::android::ConvertJavaStringToUTF8(env, java_path);
}
// static
bool BundleUtils::IsBundle() {
return Java_BundleUtils_isBundleForNative(
base::android::AttachCurrentThread());
}
// static
void* BundleUtils::DlOpenModuleLibraryPartition(const std::string& library_name,
const std::string& partition) {
// TODO(https://crbug.com/1019853): Remove this tolerance.
std::string library_path = ResolveLibraryPath(library_name);
if (library_path.empty()) {
return nullptr;
}
// Linear search is required here because the partition descriptors are not
// ordered. If a large number of partitions come into existence, lld could be
// modified to sort the partitions.
DCHECK(__part_index_begin != nullptr);
DCHECK(__part_index_end != nullptr);
for (const PartitionIndexEntry* part = __part_index_begin;
part != __part_index_end; ++part) {
std::string name(
reinterpret_cast<const char*>(ReadRelPtr(&part->name_relptr)));
if (name == partition) {
android_dlextinfo info = {};
info.flags = ANDROID_DLEXT_RESERVED_ADDRESS;
info.reserved_addr = const_cast<void*>(ReadRelPtr(&part->addr_relptr));
info.reserved_size = part->size;
DCHECK(android_dlopen_ext != nullptr);
return android_dlopen_ext(library_path.c_str(), RTLD_LOCAL, &info);
}
}
NOTREACHED();
return nullptr;
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,42 @@
// Copyright 2019 The Chromium 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_BUNDLE_UTILS_H_
#define BASE_ANDROID_BUNDLE_UTILS_H_
#include <string>
#include "base/base_export.h"
namespace base {
namespace android {
// Utils to help working with android app bundles.
class BASE_EXPORT BundleUtils {
public:
// Returns true if the current build is a bundle.
static bool IsBundle();
// Helper function asking Java to resolve a library path. This is required for
// resolving a module library made available via SplitCompat, rather than in
// its eventual fully-installed state.
static std::string ResolveLibraryPath(const std::string& library_name);
// dlopen wrapper that works for partitioned native libraries in dynamic
// feature modules. This routine looks up the partition's address space in a
// table of main library symbols, and uses it when loading the feature
// library. It requires |library_name| (eg. chrome_foo) to resolve the file
// path (which may be in an interesting location due to SplitCompat) and
// |partition_name| to look up the load parameters in the main library. These
// two values may be identical, but since the partition name is set at compile
// time, and the code is linked into multiple libraries (eg. Chrome vs
// Monochrome), they may not be.
static void* DlOpenModuleLibraryPartition(const std::string& library_name,
const std::string& partition);
};
} // namespace android
} // namespace base
#endif // BASE_ANDROID_BUNDLE_UTILS_H_

View file

@ -0,0 +1,48 @@
// Copyright 2016 The Chromium 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/callback_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/base_jni_headers/Callback_jni.h"
namespace base {
namespace android {
void RunObjectCallbackAndroid(const JavaRef<jobject>& callback,
const JavaRef<jobject>& arg) {
Java_Helper_onObjectResultFromNative(AttachCurrentThread(), callback, arg);
}
void RunBooleanCallbackAndroid(const JavaRef<jobject>& callback, bool arg) {
Java_Helper_onBooleanResultFromNative(AttachCurrentThread(), callback,
static_cast<jboolean>(arg));
}
void RunIntCallbackAndroid(const JavaRef<jobject>& callback, int arg) {
Java_Helper_onIntResultFromNative(AttachCurrentThread(), callback, arg);
}
void RunStringCallbackAndroid(const JavaRef<jobject>& callback,
const std::string& arg) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> java_string = ConvertUTF8ToJavaString(env, arg);
Java_Helper_onObjectResultFromNative(env, callback, java_string);
}
void RunByteArrayCallbackAndroid(const JavaRef<jobject>& callback,
const std::vector<uint8_t>& arg) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jbyteArray> j_bytes = ToJavaByteArray(env, arg);
Java_Helper_onObjectResultFromNative(env, callback, j_bytes);
}
void RunRunnableAndroid(const JavaRef<jobject>& runnable) {
Java_Helper_runRunnable(AttachCurrentThread(), runnable);
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,40 @@
// Copyright 2016 The Chromium 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_CALLBACK_ANDROID_H_
#define BASE_ANDROID_CALLBACK_ANDROID_H_
#include <jni.h>
#include <string>
#include <vector>
#include "base/android/scoped_java_ref.h"
#include "base/base_export.h"
// Provides helper utility methods that run the given callback with the
// specified argument.
namespace base {
namespace android {
void BASE_EXPORT RunObjectCallbackAndroid(const JavaRef<jobject>& callback,
const JavaRef<jobject>& arg);
void BASE_EXPORT RunBooleanCallbackAndroid(const JavaRef<jobject>& callback,
bool arg);
void BASE_EXPORT RunIntCallbackAndroid(const JavaRef<jobject>& callback,
int arg);
void BASE_EXPORT RunStringCallbackAndroid(const JavaRef<jobject>& callback,
const std::string& arg);
void BASE_EXPORT RunByteArrayCallbackAndroid(const JavaRef<jobject>& callback,
const std::vector<uint8_t>& arg);
void BASE_EXPORT RunRunnableAndroid(const JavaRef<jobject>& runnable);
} // namespace android
} // namespace base
#endif // BASE_ANDROID_CALLBACK_ANDROID_H_

View file

@ -0,0 +1,25 @@
// Copyright 2018 The Chromium 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_CHILD_PROCESS_BINDING_TYPES_H_
#define BASE_ANDROID_CHILD_PROCESS_BINDING_TYPES_H_
namespace base {
namespace android {
// Defines the state of bindgings with child process. See ChildProcessConnection
// to see what the bindings are. Note these values are used as array indices.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base
enum class ChildBindingState {
UNBOUND,
WAIVED,
MODERATE,
STRONG,
MAX_VALUE = STRONG
};
} // namespace android
} // namespace base
#endif // BASE_ANDROID_CHILD_PROCESS_BINDING_TYPES_H_

View file

@ -0,0 +1,83 @@
// Copyright 2017 The Chromium 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_string.h"
#include "base/android/library_loader/library_loader_hooks.h"
#include "base/base_jni_headers/ChildProcessService_jni.h"
#include "base/debug/dump_without_crashing.h"
#include "base/file_descriptor_store.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/optional.h"
#include "base/posix/global_descriptors.h"
using base::android::JavaIntArrayToIntVector;
using base::android::JavaParamRef;
namespace base {
namespace android {
void JNI_ChildProcessService_RegisterFileDescriptors(
JNIEnv* env,
const JavaParamRef<jobjectArray>& j_keys,
const JavaParamRef<jintArray>& j_ids,
const JavaParamRef<jintArray>& j_fds,
const JavaParamRef<jlongArray>& j_offsets,
const JavaParamRef<jlongArray>& j_sizes) {
std::vector<base::Optional<std::string>> keys;
JavaObjectArrayReader<jstring> keys_array(j_keys);
keys.reserve(keys_array.size());
for (auto str : keys_array) {
base::Optional<std::string> key;
if (str) {
key = base::android::ConvertJavaStringToUTF8(env, str);
}
keys.push_back(std::move(key));
}
std::vector<int> ids;
base::android::JavaIntArrayToIntVector(env, j_ids, &ids);
std::vector<int> fds;
base::android::JavaIntArrayToIntVector(env, j_fds, &fds);
std::vector<int64_t> offsets;
base::android::JavaLongArrayToInt64Vector(env, j_offsets, &offsets);
std::vector<int64_t> sizes;
base::android::JavaLongArrayToInt64Vector(env, j_sizes, &sizes);
DCHECK_EQ(keys.size(), ids.size());
DCHECK_EQ(ids.size(), fds.size());
DCHECK_EQ(fds.size(), offsets.size());
DCHECK_EQ(offsets.size(), sizes.size());
for (size_t i = 0; i < ids.size(); i++) {
base::MemoryMappedFile::Region region = {offsets.at(i), sizes.at(i)};
const base::Optional<std::string>& key = keys.at(i);
int id = ids.at(i);
int fd = fds.at(i);
if (key) {
base::FileDescriptorStore::GetInstance().Set(*key, base::ScopedFD(fd),
region);
} else {
base::GlobalDescriptors::GetInstance()->Set(id, fd, region);
}
}
}
void JNI_ChildProcessService_ExitChildProcess(JNIEnv* env) {
VLOG(0) << "ChildProcessService: Exiting child process.";
base::android::LibraryLoaderExitHook();
_exit(0);
}
// Make sure this isn't inlined so it shows up in stack traces.
// the function body unique by adding a log line, so it doesn't get merged
// with other functions by link time optimizations (ICF).
NOINLINE void JNI_ChildProcessService_DumpProcessStack(JNIEnv* env) {
LOG(ERROR) << "Dumping as requested.";
base::debug::DumpWithoutCrashing();
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,101 @@
// 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/jni_array.h"
#include "base/android/jni_string.h"
#include "base/base_jni_headers/CommandLine_jni.h"
#include "base/command_line.h"
#include "base/logging.h"
using base::android::ConvertUTF8ToJavaString;
using base::android::ConvertJavaStringToUTF8;
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
using base::CommandLine;
namespace {
void JNI_CommandLine_AppendJavaStringArrayToCommandLine(
JNIEnv* env,
const JavaParamRef<jobjectArray>& array,
bool includes_program) {
std::vector<std::string> vec;
if (array)
base::android::AppendJavaStringArrayToStringVector(env, array, &vec);
if (!includes_program)
vec.insert(vec.begin(), std::string());
CommandLine extra_command_line(vec);
CommandLine::ForCurrentProcess()->AppendArguments(extra_command_line,
includes_program);
}
} // namespace
static jboolean JNI_CommandLine_HasSwitch(
JNIEnv* env,
const JavaParamRef<jstring>& jswitch) {
std::string switch_string(ConvertJavaStringToUTF8(env, jswitch));
return CommandLine::ForCurrentProcess()->HasSwitch(switch_string);
}
static ScopedJavaLocalRef<jstring> JNI_CommandLine_GetSwitchValue(
JNIEnv* env,
const JavaParamRef<jstring>& jswitch) {
std::string switch_string(ConvertJavaStringToUTF8(env, jswitch));
std::string value(CommandLine::ForCurrentProcess()->GetSwitchValueNative(
switch_string));
if (value.empty())
return ScopedJavaLocalRef<jstring>();
return ConvertUTF8ToJavaString(env, value);
}
static ScopedJavaLocalRef<jobjectArray> JNI_CommandLine_GetSwitchesFlattened(
JNIEnv* env) {
// JNI doesn't support returning Maps. Instead, express this map as a 1
// dimensional array: [ key1, value1, key2, value2, ... ]
std::vector<std::string> keys_and_values;
for (const auto& entry : CommandLine::ForCurrentProcess()->GetSwitches()) {
keys_and_values.push_back(entry.first);
keys_and_values.push_back(entry.second);
}
return base::android::ToJavaArrayOfStrings(env, keys_and_values);
}
static void JNI_CommandLine_AppendSwitch(JNIEnv* env,
const JavaParamRef<jstring>& jswitch) {
std::string switch_string(ConvertJavaStringToUTF8(env, jswitch));
CommandLine::ForCurrentProcess()->AppendSwitch(switch_string);
}
static void JNI_CommandLine_AppendSwitchWithValue(
JNIEnv* env,
const JavaParamRef<jstring>& jswitch,
const JavaParamRef<jstring>& jvalue) {
std::string switch_string(ConvertJavaStringToUTF8(env, jswitch));
std::string value_string(ConvertJavaStringToUTF8(env, jvalue));
CommandLine::ForCurrentProcess()->AppendSwitchASCII(switch_string,
value_string);
}
static void JNI_CommandLine_AppendSwitchesAndArguments(
JNIEnv* env,
const JavaParamRef<jobjectArray>& array) {
JNI_CommandLine_AppendJavaStringArrayToCommandLine(env, array, false);
}
static void JNI_CommandLine_RemoveSwitch(JNIEnv* env,
const JavaParamRef<jstring>& jswitch) {
std::string switch_string(ConvertJavaStringToUTF8(env, jswitch));
CommandLine::ForCurrentProcess()->RemoveSwitch(switch_string);
}
static void JNI_CommandLine_Init(
JNIEnv* env,
const JavaParamRef<jobjectArray>& init_command_line) {
// TODO(port): Make an overload of Init() that takes StringVector rather than
// have to round-trip via AppendArguments.
CommandLine::Init(0, nullptr);
JNI_CommandLine_AppendJavaStringArrayToCommandLine(env, init_command_line,
true);
}

View file

@ -0,0 +1,86 @@
// 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/content_uri_utils.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/base_jni_headers/ContentUriUtils_jni.h"
using base::android::ConvertUTF8ToJavaString;
using base::android::ScopedJavaLocalRef;
namespace base {
bool ContentUriExists(const FilePath& content_uri) {
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jstring> j_uri =
ConvertUTF8ToJavaString(env, content_uri.value());
return Java_ContentUriUtils_contentUriExists(env, j_uri);
}
File OpenContentUriForRead(const FilePath& content_uri) {
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jstring> j_uri =
ConvertUTF8ToJavaString(env, content_uri.value());
jint fd = Java_ContentUriUtils_openContentUriForRead(env, j_uri);
if (fd < 0)
return File();
return File(fd);
}
std::string GetContentUriMimeType(const FilePath& content_uri) {
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jstring> j_uri =
ConvertUTF8ToJavaString(env, content_uri.value());
ScopedJavaLocalRef<jstring> j_mime =
Java_ContentUriUtils_getMimeType(env, j_uri);
if (j_mime.is_null())
return std::string();
return base::android::ConvertJavaStringToUTF8(env, j_mime.obj());
}
bool MaybeGetFileDisplayName(const FilePath& content_uri,
base::string16* file_display_name) {
if (!content_uri.IsContentUri())
return false;
DCHECK(file_display_name);
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jstring> j_uri =
ConvertUTF8ToJavaString(env, content_uri.value());
ScopedJavaLocalRef<jstring> j_display_name =
Java_ContentUriUtils_maybeGetDisplayName(env, j_uri);
if (j_display_name.is_null())
return false;
*file_display_name = base::android::ConvertJavaStringToUTF16(j_display_name);
return true;
}
bool DeleteContentUri(const FilePath& content_uri) {
DCHECK(content_uri.IsContentUri());
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jstring> j_uri =
ConvertUTF8ToJavaString(env, content_uri.value());
return Java_ContentUriUtils_delete(env, j_uri);
}
FilePath GetContentUriFromFilePath(const FilePath& file_path) {
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jstring> j_file_path =
ConvertUTF8ToJavaString(env, file_path.value());
ScopedJavaLocalRef<jstring> j_content_uri =
Java_ContentUriUtils_getContentUriFromFilePath(env, j_file_path);
if (j_content_uri.is_null())
return FilePath();
return FilePath(base::android::ConvertJavaStringToUTF8(env, j_content_uri));
}
} // namespace base

View file

@ -0,0 +1,40 @@
// 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_CONTENT_URI_UTILS_H_
#define BASE_ANDROID_CONTENT_URI_UTILS_H_
#include <jni.h>
#include "base/base_export.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
namespace base {
// Opens a content URI for read and returns the file descriptor to the caller.
// Returns -1 if the URI is invalid.
BASE_EXPORT File OpenContentUriForRead(const FilePath& content_uri);
// Check whether a content URI exists.
BASE_EXPORT bool ContentUriExists(const FilePath& content_uri);
// Gets MIME type from a content URI. Returns an empty string if the URI is
// invalid.
BASE_EXPORT std::string GetContentUriMimeType(const FilePath& content_uri);
// Gets the display name from a content URI. Returns true if the name was found.
BASE_EXPORT bool MaybeGetFileDisplayName(const FilePath& content_uri,
base::string16* file_display_name);
// Deletes a content URI.
BASE_EXPORT bool DeleteContentUri(const FilePath& content_uri);
// Gets content URI's file path (eg: "content://org.chromium...") from normal
// file path (eg: "/data/user/0/...").
BASE_EXPORT FilePath GetContentUriFromFilePath(const FilePath& file_path);
} // namespace base
#endif // BASE_ANDROID_CONTENT_URI_UTILS_H_

View file

@ -0,0 +1,22 @@
// 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 "base/base_jni_headers/CpuFeatures_jni.h"
namespace base {
namespace android {
jint JNI_CpuFeatures_GetCoreCount(JNIEnv*) {
return android_getCpuCount();
}
jlong JNI_CpuFeatures_GetCpuFeatures(JNIEnv*) {
return android_getCpuFeatures();
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,80 @@
// Copyright 2016 The Chromium 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/early_trace_event_binding.h"
#include <stdint.h>
#include "base/android/jni_string.h"
#include "base/base_jni_headers/EarlyTraceEvent_jni.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
namespace base {
namespace android {
constexpr const char kEarlyJavaCategory[] = "EarlyJava";
static void JNI_EarlyTraceEvent_RecordEarlyEvent(
JNIEnv* env,
const JavaParamRef<jstring>& jname,
jlong begin_time_ns,
jlong end_time_ns,
jint thread_id,
jlong thread_duration_ms) {
std::string name = ConvertJavaStringToUTF8(env, jname);
int64_t begin_us = begin_time_ns / 1000;
int64_t end_us = end_time_ns / 1000;
int64_t thread_duration_us = thread_duration_ms * 1000;
INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMPS(
kEarlyJavaCategory, name.c_str(), trace_event_internal::kNoId, thread_id,
TimeTicks::FromInternalValue(begin_us),
TimeTicks::FromInternalValue(end_us),
ThreadTicks::Now() + TimeDelta::FromMicroseconds(thread_duration_us),
TRACE_EVENT_FLAG_JAVA_STRING_LITERALS | TRACE_EVENT_FLAG_COPY);
}
static void JNI_EarlyTraceEvent_RecordEarlyStartAsyncEvent(
JNIEnv* env,
const JavaParamRef<jstring>& jname,
jlong id,
jlong timestamp_ns) {
std::string name = ConvertJavaStringToUTF8(env, jname);
int64_t timestamp_us = timestamp_ns / 1000;
TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP_AND_FLAGS0(
kEarlyJavaCategory, name.c_str(), TRACE_ID_LOCAL(id),
base::TimeTicks() + base::TimeDelta::FromMicroseconds(timestamp_us),
TRACE_EVENT_FLAG_JAVA_STRING_LITERALS | TRACE_EVENT_FLAG_COPY);
}
static void JNI_EarlyTraceEvent_RecordEarlyFinishAsyncEvent(
JNIEnv* env,
const JavaParamRef<jstring>& jname,
jlong id,
jlong timestamp_ns) {
std::string name = ConvertJavaStringToUTF8(env, jname);
int64_t timestamp_us = timestamp_ns / 1000;
TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP_AND_FLAGS0(
kEarlyJavaCategory, name.c_str(), TRACE_ID_LOCAL(id),
base::TimeTicks() + base::TimeDelta::FromMicroseconds(timestamp_us),
TRACE_EVENT_FLAG_JAVA_STRING_LITERALS | TRACE_EVENT_FLAG_COPY);
}
bool GetBackgroundStartupTracingFlag() {
JNIEnv* env = base::android::AttachCurrentThread();
return base::android::Java_EarlyTraceEvent_getBackgroundStartupTracingFlag(
env);
}
void SetBackgroundStartupTracingFlag(bool enabled) {
JNIEnv* env = base::android::AttachCurrentThread();
base::android::Java_EarlyTraceEvent_setBackgroundStartupTracingFlag(env,
enabled);
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,24 @@
// Copyright 2018 The Chromium 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_EARLY_TRACE_EVENT_BINDING_H_
#define BASE_ANDROID_EARLY_TRACE_EVENT_BINDING_H_
#include "base/base_export.h"
namespace base {
namespace android {
// Returns true if background startup tracing flag was set on the previous
// startup.
BASE_EXPORT bool GetBackgroundStartupTracingFlag();
// Sets a flag to chrome application preferences to enable startup tracing next
// time the app is started.
BASE_EXPORT void SetBackgroundStartupTracingFlag(bool enabled);
} // namespace android
} // namespace base
#endif // BASE_ANDROID_EARLY_TRACE_EVENT_BINDING_H_

View file

@ -0,0 +1,16 @@
// 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/event_log.h"
#include "base/base_jni_headers/EventLog_jni.h"
namespace base {
namespace android {
void EventLogWriteInt(int tag, int value) {
Java_EventLog_writeEvent(AttachCurrentThread(), tag, value);
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,20 @@
// Copyright 2014 The Chromium 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_EVENT_LOG_H_
#define BASE_ANDROID_EVENT_LOG_H_
#include <jni.h>
#include "base/base_export.h"
namespace base {
namespace android {
void BASE_EXPORT EventLogWriteInt(int tag, int value);
} // namespace android
} // namespace base
#endif // BASE_ANDROID_EVENT_LOG_H_

View file

@ -0,0 +1,90 @@
// Copyright 2014 The Chromium 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/android/jni_string.h"
#include "base/base_jni_headers/FieldTrialList_jni.h"
#include "base/lazy_instance.h"
#include "base/macros.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
using base::android::ConvertJavaStringToUTF8;
using base::android::ConvertUTF8ToJavaString;
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
namespace {
// Log trials and their groups on activation, for debugging purposes.
class TrialLogger : public base::FieldTrialList::Observer {
public:
TrialLogger() {}
void OnFieldTrialGroupFinalized(const std::string& trial_name,
const std::string& group_name) override {
Log(trial_name, group_name);
}
static void Log(const std::string& trial_name,
const std::string& group_name) {
LOG(INFO) << "Active field trial \"" << trial_name
<< "\" in group \"" << group_name<< '"';
}
protected:
~TrialLogger() override {}
private:
DISALLOW_COPY_AND_ASSIGN(TrialLogger);
};
base::LazyInstance<TrialLogger>::Leaky g_trial_logger =
LAZY_INSTANCE_INITIALIZER;
} // namespace
static ScopedJavaLocalRef<jstring> JNI_FieldTrialList_FindFullName(
JNIEnv* env,
const JavaParamRef<jstring>& jtrial_name) {
std::string trial_name(ConvertJavaStringToUTF8(env, jtrial_name));
return ConvertUTF8ToJavaString(
env, base::FieldTrialList::FindFullName(trial_name));
}
static jboolean JNI_FieldTrialList_TrialExists(
JNIEnv* env,
const JavaParamRef<jstring>& jtrial_name) {
std::string trial_name(ConvertJavaStringToUTF8(env, jtrial_name));
return base::FieldTrialList::TrialExists(trial_name);
}
static ScopedJavaLocalRef<jstring> JNI_FieldTrialList_GetVariationParameter(
JNIEnv* env,
const JavaParamRef<jstring>& jtrial_name,
const JavaParamRef<jstring>& jparameter_key) {
std::map<std::string, std::string> parameters;
base::GetFieldTrialParams(ConvertJavaStringToUTF8(env, jtrial_name),
&parameters);
return ConvertUTF8ToJavaString(
env, parameters[ConvertJavaStringToUTF8(env, jparameter_key)]);
}
static void JNI_FieldTrialList_LogActiveTrials(JNIEnv* env) {
DCHECK(!g_trial_logger.IsCreated()); // This need only be called once.
LOG(INFO) << "Logging active field trials...";
base::FieldTrialList::AddObserver(&g_trial_logger.Get());
// Log any trials that were already active before adding the observer.
std::vector<base::FieldTrial::ActiveGroup> active_groups;
base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
for (const base::FieldTrial::ActiveGroup& group : active_groups) {
TrialLogger::Log(group.trial_name, group.group_name);
}
}

View file

@ -0,0 +1,34 @@
// 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 <string>
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/base_jni_headers/ImportantFileWriterAndroid_jni.h"
#include "base/files/important_file_writer.h"
#include "base/threading/thread_restrictions.h"
namespace base {
namespace android {
static jboolean JNI_ImportantFileWriterAndroid_WriteFileAtomically(
JNIEnv* env,
const JavaParamRef<jstring>& file_name,
const JavaParamRef<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);
std::string native_data_string;
JavaByteArrayToString(env, data, &native_data_string);
bool result = base::ImportantFileWriter::WriteFileAtomically(
path, native_data_string);
return result;
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,28 @@
// Copyright 2018 The Chromium 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 encapsulates the JNI headers generated for IntStringCallback, so
// that the methods defined in the generated headers only end up in one object
// file. This is similar to //base/android/callback_android.*.
#include "base/android/int_string_callback.h"
#include <jni.h>
#include "base/android/jni_string.h"
#include "base/base_jni_headers/IntStringCallback_jni.h"
namespace base {
namespace android {
void RunIntStringCallbackAndroid(const JavaRef<jobject>& callback,
int int_arg,
const std::string& str_arg) {
JNIEnv* env = AttachCurrentThread();
Java_IntStringCallback_onResult(env, callback, int_arg,
ConvertUTF8ToJavaString(env, str_arg));
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,25 @@
// Copyright 2018 The Chromium 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_INT_STRING_CALLBACK_H_
#define BASE_ANDROID_INT_STRING_CALLBACK_H_
#include <string>
#include "base/android/scoped_java_ref.h"
#include "base/base_export.h"
namespace base {
namespace android {
// Runs the Java |callback| by calling its onResult method and passing the
// integer and string as its arguments.
void BASE_EXPORT RunIntStringCallbackAndroid(const JavaRef<jobject>& callback,
int int_arg,
const std::string& str_arg);
} // namespace android
} // namespace base
#endif // BASE_ANDROID_INT_STRING_CALLBACK_H_

View file

@ -0,0 +1,96 @@
// Copyright 2015 The Chromium 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_exception_reporter.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/base_jni_headers/JavaExceptionReporter_jni.h"
#include "base/bind.h"
#include "base/callback_forward.h"
#include "base/debug/dump_without_crashing.h"
#include "base/lazy_instance.h"
using base::android::JavaParamRef;
using base::android::JavaRef;
namespace base {
namespace android {
namespace {
void (*g_java_exception_callback)(const char*);
using JavaExceptionFilter =
base::RepeatingCallback<bool(const JavaRef<jthrowable>&)>;
LazyInstance<JavaExceptionFilter>::Leaky g_java_exception_filter;
} // namespace
void InitJavaExceptionReporter() {
JNIEnv* env = base::android::AttachCurrentThread();
// Since JavaExceptionReporter#installHandler will chain through to the
// default handler, the default handler should cause a crash as if it's a
// normal java exception. Prefer to crash the browser process in java rather
// than native since for webview, the embedding app may have installed its
// own JavaExceptionReporter handler and would expect it to be called.
constexpr bool crash_after_report = false;
SetJavaExceptionFilter(
base::BindRepeating([](const JavaRef<jthrowable>&) { return true; }));
Java_JavaExceptionReporter_installHandler(env, crash_after_report);
}
void InitJavaExceptionReporterForChildProcess() {
JNIEnv* env = base::android::AttachCurrentThread();
constexpr bool crash_after_report = true;
SetJavaExceptionFilter(
base::BindRepeating([](const JavaRef<jthrowable>&) { return true; }));
Java_JavaExceptionReporter_installHandler(env, crash_after_report);
}
void SetJavaExceptionFilter(JavaExceptionFilter java_exception_filter) {
g_java_exception_filter.Get() = std::move(java_exception_filter);
}
void SetJavaExceptionCallback(void (*callback)(const char*)) {
DCHECK(!g_java_exception_callback);
g_java_exception_callback = callback;
}
void SetJavaException(const char* exception) {
DCHECK(g_java_exception_callback);
g_java_exception_callback(exception);
}
void JNI_JavaExceptionReporter_ReportJavaException(
JNIEnv* env,
jboolean crash_after_report,
const JavaParamRef<jthrowable>& e) {
std::string exception_info = base::android::GetJavaExceptionInfo(env, e);
bool should_report_exception = g_java_exception_filter.Get().Run(e);
if (should_report_exception) {
SetJavaException(exception_info.c_str());
}
if (crash_after_report) {
LOG(ERROR) << exception_info;
LOG(FATAL) << "Uncaught exception";
}
if (should_report_exception) {
base::debug::DumpWithoutCrashing();
SetJavaException(nullptr);
}
}
void JNI_JavaExceptionReporter_ReportJavaStackTrace(
JNIEnv* env,
const JavaParamRef<jstring>& stack_trace) {
SetJavaException(ConvertJavaStringToUTF8(stack_trace).c_str());
base::debug::DumpWithoutCrashing();
SetJavaException(nullptr);
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,41 @@
// Copyright 2015 The Chromium 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_JAVA_EXCEPTION_REPORTER_H_
#define BASE_ANDROID_JAVA_EXCEPTION_REPORTER_H_
#include <jni.h>
#include "base/android/scoped_java_ref.h"
#include "base/base_export.h"
namespace base {
namespace android {
// Install the exception handler. This should only be called once per process.
BASE_EXPORT void InitJavaExceptionReporter();
// Same as above except the handler ensures child process exists immediately
// after an unhandled exception. This is used for child processes because
// DumpWithoutCrashing does not work for child processes on Android.
BASE_EXPORT void InitJavaExceptionReporterForChildProcess();
// Sets a callback to be called with the contents of a Java exception, which may
// be nullptr.
BASE_EXPORT void SetJavaExceptionCallback(void (*)(const char* exception));
// Calls the Java exception callback, if any, with exception.
void SetJavaException(const char* exception);
// Sets a filter that determines whether a java exception should cause a crash
// report. |java_exception_filter| should return true if a crash report should
// be generated.
BASE_EXPORT void SetJavaExceptionFilter(
base::RepeatingCallback<bool(const JavaRef<jthrowable>&)>
java_exception_filter);
} // namespace android
} // namespace base
#endif // BASE_ANDROID_JAVA_EXCEPTION_REPORTER_H_

View file

@ -0,0 +1,170 @@
// 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/base_jni_headers/JavaHandlerThread_jni.h"
#include "base/bind.h"
#include "base/message_loop/message_pump.h"
#include "base/message_loop/message_pump_type.h"
#include "base/run_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/sequence_manager/sequence_manager_impl.h"
#include "base/threading/platform_thread_internal_posix.h"
#include "base/threading/thread_id_name_manager.h"
#include "base/threading/thread_restrictions.h"
using base::android::AttachCurrentThread;
namespace base {
namespace android {
JavaHandlerThread::JavaHandlerThread(const char* name,
base::ThreadPriority priority)
: JavaHandlerThread(
name,
Java_JavaHandlerThread_create(
AttachCurrentThread(),
ConvertUTF8ToJavaString(AttachCurrentThread(), name),
base::internal::ThreadPriorityToNiceValue(priority))) {}
JavaHandlerThread::JavaHandlerThread(
const char* name,
const base::android::ScopedJavaLocalRef<jobject>& obj)
: name_(name), java_thread_(obj) {}
JavaHandlerThread::~JavaHandlerThread() {
JNIEnv* env = base::android::AttachCurrentThread();
DCHECK(!Java_JavaHandlerThread_isAlive(env, java_thread_));
DCHECK(!state_ || state_->pump->IsAborted());
// TODO(mthiesse): We shouldn't leak the MessageLoop as this could affect
// future tests.
if (state_ && state_->pump->IsAborted()) {
// When the Pump has been aborted due to a crash, we intentionally leak the
// SequenceManager because the SequenceManager hasn't been shut down
// properly and would trigger DCHECKS. This should only happen in tests,
// where we handle the exception instead of letting it take down the
// process.
state_.release();
}
}
void JavaHandlerThread::Start() {
// Check the thread has not already been started.
DCHECK(!state_);
JNIEnv* env = base::android::AttachCurrentThread();
base::WaitableEvent initialize_event(
WaitableEvent::ResetPolicy::AUTOMATIC,
WaitableEvent::InitialState::NOT_SIGNALED);
Java_JavaHandlerThread_startAndInitialize(
env, java_thread_, reinterpret_cast<intptr_t>(this),
reinterpret_cast<intptr_t>(&initialize_event));
// Wait for thread to be initialized so it is ready to be used when Start
// returns.
base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope wait_allowed;
initialize_event.Wait();
}
void JavaHandlerThread::Stop() {
DCHECK(!task_runner()->BelongsToCurrentThread());
task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&JavaHandlerThread::StopOnThread, base::Unretained(this)));
JNIEnv* env = base::android::AttachCurrentThread();
Java_JavaHandlerThread_joinThread(env, java_thread_);
}
void JavaHandlerThread::InitializeThread(JNIEnv* env,
jlong event) {
base::ThreadIdNameManager::GetInstance()->RegisterThread(
base::PlatformThread::CurrentHandle().platform_handle(),
base::PlatformThread::CurrentId());
if (name_)
PlatformThread::SetName(name_);
state_ = std::make_unique<State>();
Init();
reinterpret_cast<base::WaitableEvent*>(event)->Signal();
}
void JavaHandlerThread::OnLooperStopped(JNIEnv* env) {
DCHECK(task_runner()->BelongsToCurrentThread());
state_.reset();
CleanUp();
base::ThreadIdNameManager::GetInstance()->RemoveName(
base::PlatformThread::CurrentHandle().platform_handle(),
base::PlatformThread::CurrentId());
}
void JavaHandlerThread::StopSequenceManagerForTesting() {
DCHECK(task_runner()->BelongsToCurrentThread());
StopOnThread();
}
void JavaHandlerThread::JoinForTesting() {
DCHECK(!task_runner()->BelongsToCurrentThread());
JNIEnv* env = base::android::AttachCurrentThread();
Java_JavaHandlerThread_joinThread(env, java_thread_);
}
void JavaHandlerThread::ListenForUncaughtExceptionsForTesting() {
DCHECK(!task_runner()->BelongsToCurrentThread());
JNIEnv* env = base::android::AttachCurrentThread();
Java_JavaHandlerThread_listenForUncaughtExceptionsForTesting(env,
java_thread_);
}
ScopedJavaLocalRef<jthrowable> JavaHandlerThread::GetUncaughtExceptionIfAny() {
DCHECK(!task_runner()->BelongsToCurrentThread());
JNIEnv* env = base::android::AttachCurrentThread();
return Java_JavaHandlerThread_getUncaughtExceptionIfAny(env, java_thread_);
}
void JavaHandlerThread::StopOnThread() {
DCHECK(task_runner()->BelongsToCurrentThread());
DCHECK(state_);
state_->pump->QuitWhenIdle(base::BindOnce(
&JavaHandlerThread::QuitThreadSafely, base::Unretained(this)));
}
void JavaHandlerThread::QuitThreadSafely() {
DCHECK(task_runner()->BelongsToCurrentThread());
JNIEnv* env = base::android::AttachCurrentThread();
Java_JavaHandlerThread_quitThreadSafely(env, java_thread_,
reinterpret_cast<intptr_t>(this));
}
JavaHandlerThread::State::State()
: sequence_manager(sequence_manager::CreateUnboundSequenceManager(
sequence_manager::SequenceManager::Settings::Builder()
.SetMessagePumpType(base::MessagePumpType::JAVA)
.Build())),
default_task_queue(sequence_manager->CreateTaskQueue(
sequence_manager::TaskQueue::Spec("default_tq"))) {
// TYPE_JAVA to get the Android java style message loop.
std::unique_ptr<MessagePump> message_pump =
MessagePump::Create(base::MessagePumpType::JAVA);
pump = static_cast<MessagePumpForUI*>(message_pump.get());
// We must set SetTaskRunner before binding because the Android UI pump
// creates a RunLoop which samples ThreadTaskRunnerHandle::Get.
static_cast<sequence_manager::internal::SequenceManagerImpl*>(
sequence_manager.get())
->SetTaskRunner(default_task_queue->task_runner());
sequence_manager->BindToMessagePump(std::move(message_pump));
}
JavaHandlerThread::State::~State() = default;
} // namespace android
} // namespace base

View file

@ -0,0 +1,108 @@
// 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_JAVA_HANDLER_THREAD_H_
#define BASE_ANDROID_JAVA_HANDLER_THREAD_H_
#include <jni.h>
#include <memory>
#include "base/android/scoped_java_ref.h"
#include "base/single_thread_task_runner.h"
#include "base/task/sequence_manager/sequence_manager.h"
#include "base/task/sequence_manager/task_queue.h"
#include "base/threading/thread_task_runner_handle.h"
namespace base {
class MessagePumpForUI;
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:
// Create new thread.
explicit JavaHandlerThread(
const char* name,
base::ThreadPriority priority = base::ThreadPriority::NORMAL);
// Wrap and connect to an existing JavaHandlerThread.
// |obj| is an instance of JavaHandlerThread.
explicit JavaHandlerThread(
const char* name,
const base::android::ScopedJavaLocalRef<jobject>& obj);
virtual ~JavaHandlerThread();
// Gets the TaskRunner associated with the message loop.
// Called from any thread.
scoped_refptr<SingleThreadTaskRunner> task_runner() const {
return state_ ? state_->default_task_queue->task_runner() : nullptr;
}
// Called from the parent thread.
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,
jlong event);
// Called from java on this thread.
void OnLooperStopped(JNIEnv* env);
// Called from this thread.
void StopSequenceManagerForTesting();
// Called from this thread.
void JoinForTesting();
// Called from this thread.
// See comment in JavaHandlerThread.java regarding use of this function.
void ListenForUncaughtExceptionsForTesting();
// Called from this thread.
ScopedJavaLocalRef<jthrowable> GetUncaughtExceptionIfAny();
protected:
// Struct exists so JavaHandlerThread destructor can intentionally leak in an
// abort scenario.
struct State {
State();
~State();
std::unique_ptr<sequence_manager::SequenceManager> sequence_manager;
scoped_refptr<sequence_manager::TaskQueue> default_task_queue;
MessagePumpForUI* pump = nullptr;
};
State* state() const { return state_.get(); }
// Semantically the same as base::Thread#Init(), but unlike base::Thread the
// Android Looper will already be running. This Init() call will still run
// before other tasks are posted to the thread.
virtual void Init() {}
// Semantically the same as base::Thread#CleanUp(), called after the message
// loop ends. The Android Looper will also have been quit by this point.
virtual void CleanUp() {}
std::unique_ptr<State> state_;
private:
void StartMessageLoop();
void StopOnThread();
void QuitThreadSafely();
const char* name_;
ScopedJavaGlobalRef<jobject> java_thread_;
};
} // namespace android
} // namespace base
#endif // BASE_ANDROID_JAVA_HANDLER_THREAD_H_

View file

@ -0,0 +1,22 @@
// Copyright 2019 The Chromium 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_heap_dump_generator.h"
#include <jni.h>
#include "base/android/jni_string.h"
#include "base/base_jni_headers/JavaHeapDumpGenerator_jni.h"
namespace base {
namespace android {
bool WriteJavaHeapDumpToPath(base::StringPiece filePath) {
JNIEnv* env = AttachCurrentThread();
return Java_JavaHeapDumpGenerator_generateHprof(
env, base::android::ConvertUTF8ToJavaString(env, filePath));
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,24 @@
// Copyright 2019 The Chromium 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_JAVA_HEAP_DUMP_GENERATOR_H_
#define BASE_ANDROID_JAVA_HEAP_DUMP_GENERATOR_H_
#include <string>
#include "base/android/scoped_java_ref.h"
#include "base/base_export.h"
namespace base {
namespace android {
// Generates heap dump and writes it to a file at |file_path|. Returns true on
// success. The heap dump is generated through the Android Java system API
// android.os.Debug#dumpHprofData(...)
BASE_EXPORT bool WriteJavaHeapDumpToPath(base::StringPiece file_path);
} // namespace android
} // namespace base
#endif // BASE_ANDROID_JAVA_HEAP_DUMP_GENERATOR_H_

View file

@ -0,0 +1,21 @@
// Copyright 2015 The Chromium 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_runtime.h"
#include "base/android_runtime_jni_headers/Runtime_jni.h"
namespace base {
namespace android {
void JavaRuntime::GetMemoryUsage(long* total_memory, long* free_memory) {
JNIEnv* env = base::android::AttachCurrentThread();
base::android::ScopedJavaLocalRef<jobject> runtime =
JNI_Runtime::Java_Runtime_getRuntime(env);
*total_memory = JNI_Runtime::Java_Runtime_totalMemory(env, runtime);
*free_memory = JNI_Runtime::Java_Runtime_freeMemory(env, runtime);
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,25 @@
// Copyright 2015 The Chromium 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_JAVA_RUNTIME_H_
#define BASE_ANDROID_JAVA_RUNTIME_H_
#include "base/android/scoped_java_ref.h"
#include "base/base_export.h"
namespace base {
namespace android {
// Wrapper class for using the java.lang.Runtime object from jni.
class BASE_EXPORT JavaRuntime {
public:
// Fills the total memory used and memory allocated for objects by the java
// heap in the current process. Returns true on success.
static void GetMemoryUsage(long* total_memory, long* free_memory);
};
} // namespace android
} // namespace base
#endif // BASE_ANDROID_JAVA_RUNTIME_H_

View file

@ -0,0 +1,297 @@
// 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 <stddef.h>
#include <sys/prctl.h>
#include <map>
#include "base/android/java_exception_reporter.h"
#include "base/android/jni_string.h"
#include "base/debug/debugging_buildflags.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/threading/thread_local.h"
namespace {
using base::android::GetClass;
using base::android::MethodID;
using base::android::ScopedJavaLocalRef;
JavaVM* g_jvm = NULL;
base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject>>::Leaky
g_class_loader = LAZY_INSTANCE_INITIALIZER;
jmethodID g_class_loader_load_class_method_id = 0;
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
base::LazyInstance<base::ThreadLocalPointer<void>>::Leaky
g_stack_frame_pointer = LAZY_INSTANCE_INITIALIZER;
#endif
bool g_fatal_exception_occurred = false;
} // namespace
namespace base {
namespace android {
JNIEnv* AttachCurrentThread() {
DCHECK(g_jvm);
JNIEnv* env = nullptr;
jint ret = g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_2);
if (ret == JNI_EDETACHED || !env) {
JavaVMAttachArgs args;
args.version = JNI_VERSION_1_2;
args.group = nullptr;
// 16 is the maximum size for thread names on Android.
char thread_name[16];
int err = prctl(PR_GET_NAME, thread_name);
if (err < 0) {
DPLOG(ERROR) << "prctl(PR_GET_NAME)";
args.name = nullptr;
} else {
args.name = thread_name;
}
ret = g_jvm->AttachCurrentThread(&env, &args);
CHECK_EQ(JNI_OK, ret);
}
return env;
}
JNIEnv* AttachCurrentThreadWithName(const std::string& thread_name) {
DCHECK(g_jvm);
JavaVMAttachArgs args;
args.version = JNI_VERSION_1_2;
args.name = thread_name.c_str();
args.group = NULL;
JNIEnv* env = NULL;
jint ret = g_jvm->AttachCurrentThread(&env, &args);
CHECK_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);
g_jvm = vm;
}
bool IsVMInitialized() {
return g_jvm != NULL;
}
void InitReplacementClassLoader(JNIEnv* env,
const JavaRef<jobject>& class_loader) {
DCHECK(g_class_loader.Get().is_null());
DCHECK(!class_loader.is_null());
ScopedJavaLocalRef<jclass> class_loader_clazz =
GetClass(env, "java/lang/ClassLoader");
CHECK(!ClearException(env));
g_class_loader_load_class_method_id =
env->GetMethodID(class_loader_clazz.obj(),
"loadClass",
"(Ljava/lang/String;)Ljava/lang/Class;");
CHECK(!ClearException(env));
DCHECK(env->IsInstanceOf(class_loader.obj(), class_loader_clazz.obj()));
g_class_loader.Get().Reset(class_loader);
}
ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) {
jclass clazz;
if (!g_class_loader.Get().is_null()) {
// ClassLoader.loadClass expects a classname with components separated by
// dots instead of the slashes that JNIEnv::FindClass expects. The JNI
// generator generates names with slashes, so we have to replace them here.
// TODO(torne): move to an approach where we always use ClassLoader except
// for the special case of base::android::GetClassLoader(), and change the
// JNI generator to generate dot-separated names. http://crbug.com/461773
size_t bufsize = strlen(class_name) + 1;
char dotted_name[bufsize];
memmove(dotted_name, class_name, bufsize);
for (size_t i = 0; i < bufsize; ++i) {
if (dotted_name[i] == '/') {
dotted_name[i] = '.';
}
}
clazz = static_cast<jclass>(
env->CallObjectMethod(g_class_loader.Get().obj(),
g_class_loader_load_class_method_id,
ConvertUTF8ToJavaString(env, dotted_name).obj()));
} else {
clazz = env->FindClass(class_name);
}
if (ClearException(env) || !clazz) {
LOG(FATAL) << "Failed to find class " << class_name;
}
return ScopedJavaLocalRef<jclass>(env, clazz);
}
jclass LazyGetClass(
JNIEnv* env,
const char* class_name,
std::atomic<jclass>* atomic_class_id) {
const jclass value = std::atomic_load(atomic_class_id);
if (value)
return value;
ScopedJavaGlobalRef<jclass> clazz;
clazz.Reset(GetClass(env, class_name));
jclass cas_result = nullptr;
if (std::atomic_compare_exchange_strong(atomic_class_id, &cas_result,
clazz.obj())) {
// We intentionally leak the global ref since we now storing it as a raw
// pointer in |atomic_class_id|.
return clazz.Release();
} else {
return cas_result;
}
}
template<MethodID::Type type>
jmethodID MethodID::Get(JNIEnv* env,
jclass clazz,
const char* method_name,
const char* jni_signature) {
auto get_method_ptr = type == MethodID::TYPE_STATIC ?
&JNIEnv::GetStaticMethodID :
&JNIEnv::GetMethodID;
jmethodID id = (env->*get_method_ptr)(clazz, method_name, jni_signature);
if (base::android::ClearException(env) || !id) {
LOG(FATAL) << "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,
std::atomic<jmethodID>* atomic_method_id) {
const jmethodID value = std::atomic_load(atomic_method_id);
if (value)
return value;
jmethodID id = MethodID::Get<type>(env, clazz, method_name, jni_signature);
std::atomic_store(atomic_method_id, 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, std::atomic<jmethodID>* atomic_method_id);
template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>(
JNIEnv* env, jclass clazz, const char* method_name,
const char* jni_signature, std::atomic<jmethodID>* atomic_method_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;
jthrowable java_throwable = env->ExceptionOccurred();
if (java_throwable) {
// Clear the pending exception, since a local reference is now held.
env->ExceptionDescribe();
env->ExceptionClear();
if (g_fatal_exception_occurred) {
// Another exception (probably OOM) occurred during GetJavaExceptionInfo.
base::android::SetJavaException(
"Java OOM'ed in exception handling, check logcat");
} else {
g_fatal_exception_occurred = true;
// RVO should avoid any extra copies of the exception string.
base::android::SetJavaException(
GetJavaExceptionInfo(env, java_throwable).c_str());
}
}
// Now, feel good about it and die.
LOG(FATAL) << "Please include Java exception stack in crash report";
}
std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) {
ScopedJavaLocalRef<jclass> log_clazz = GetClass(env, "android/util/Log");
jmethodID log_getstacktracestring = MethodID::Get<MethodID::TYPE_STATIC>(
env, log_clazz.obj(), "getStackTraceString",
"(Ljava/lang/Throwable;)Ljava/lang/String;");
// Call Log.getStackTraceString()
ScopedJavaLocalRef<jstring> exception_string(
env, static_cast<jstring>(env->CallStaticObjectMethod(
log_clazz.obj(), log_getstacktracestring, java_throwable)));
CheckException(env);
ScopedJavaLocalRef<jclass> piielider_clazz =
GetClass(env, "org/chromium/base/PiiElider");
jmethodID piielider_sanitize_stacktrace =
MethodID::Get<MethodID::TYPE_STATIC>(
env, piielider_clazz.obj(), "sanitizeStacktrace",
"(Ljava/lang/String;)Ljava/lang/String;");
ScopedJavaLocalRef<jstring> sanitized_exception_string(
env, static_cast<jstring>(env->CallStaticObjectMethod(
piielider_clazz.obj(), piielider_sanitize_stacktrace,
exception_string.obj())));
CheckException(env);
return ConvertJavaStringToUTF8(sanitized_exception_string);
}
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
JNIStackFrameSaver::JNIStackFrameSaver(void* current_fp) {
previous_fp_ = g_stack_frame_pointer.Pointer()->Get();
g_stack_frame_pointer.Pointer()->Set(current_fp);
}
JNIStackFrameSaver::~JNIStackFrameSaver() {
g_stack_frame_pointer.Pointer()->Set(previous_fp_);
}
void* JNIStackFrameSaver::SavedFrame() {
return g_stack_frame_pointer.Pointer()->Get();
}
#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
} // namespace android
} // namespace base

View file

@ -0,0 +1,174 @@
// 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 <atomic>
#include <string>
#include "base/android/scoped_java_ref.h"
#include "base/atomicops.h"
#include "base/base_export.h"
#include "base/compiler_specific.h"
#include "base/debug/debugging_buildflags.h"
#include "base/debug/stack_trace.h"
#include "base/macros.h"
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
// When profiling is enabled (enable_profiling=true) this macro is added to
// all generated JNI stubs so that it becomes the last thing that runs before
// control goes into Java.
//
// This macro saves stack frame pointer of the current function. Saved value
// used later by JNI_LINK_SAVED_FRAME_POINTER.
#define JNI_SAVE_FRAME_POINTER \
base::android::JNIStackFrameSaver jni_frame_saver(__builtin_frame_address(0))
// When profiling is enabled (enable_profiling=true) this macro is added to
// all generated JNI callbacks so that it becomes the first thing that runs
// after control returns from Java.
//
// This macro links stack frame of the current function to the stack frame
// saved by JNI_SAVE_FRAME_POINTER, allowing frame-based unwinding
// (used by the heap profiler) to produce complete traces.
#define JNI_LINK_SAVED_FRAME_POINTER \
base::debug::ScopedStackFrameLinker jni_frame_linker( \
__builtin_frame_address(0), \
base::android::JNIStackFrameSaver::SavedFrame())
#else
// Frame-based stack unwinding is not supported, do nothing.
#define JNI_SAVE_FRAME_POINTER
#define JNI_LINK_SAVED_FRAME_POINTER
#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
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);
};
// Attaches the current thread to the VM (if necessary) and return the JNIEnv*.
BASE_EXPORT JNIEnv* AttachCurrentThread();
// Same to AttachCurrentThread except that thread name will be set to
// |thread_name| if it is the first call. Otherwise, thread_name won't be
// changed. AttachCurrentThread() doesn't regard underlying platform thread
// name, but just resets it to "Thread-???". This function should be called
// right after new thread is created if it is important to keep thread name.
BASE_EXPORT JNIEnv* AttachCurrentThreadWithName(const std::string& thread_name);
// Detaches the current thread from VM if it is attached.
BASE_EXPORT void DetachFromVM();
// Initializes the global JVM.
BASE_EXPORT void InitVM(JavaVM* vm);
// Returns true if the global JVM has been initialized.
BASE_EXPORT bool IsVMInitialized();
// Initializes the global ClassLoader used by the GetClass and LazyGetClass
// methods. This is needed because JNI will use the base ClassLoader when there
// is no Java code on the stack. The base ClassLoader doesn't know about any of
// the application classes and will fail to lookup anything other than system
// classes.
BASE_EXPORT void InitReplacementClassLoader(
JNIEnv* env,
const JavaRef<jobject>& class_loader);
// 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);
// The method will initialize |atomic_class_id| to contain a global ref to the
// class. And will return that ref on subsequent calls. It's the caller's
// responsibility to release the ref when it is no longer needed.
// 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|.
BASE_EXPORT jclass LazyGetClass(
JNIEnv* env,
const char* class_name,
std::atomic<jclass>* atomic_class_id);
// 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,
std::atomic<jmethodID>* atomic_method_id);
};
// 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);
// This returns a string representation of the java stack trace.
BASE_EXPORT std::string GetJavaExceptionInfo(JNIEnv* env,
jthrowable java_throwable);
#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
// Saves caller's PC and stack frame in a thread-local variable.
// Implemented only when profiling is enabled (enable_profiling=true).
class BASE_EXPORT JNIStackFrameSaver {
public:
JNIStackFrameSaver(void* current_fp);
~JNIStackFrameSaver();
static void* SavedFrame();
private:
void* previous_fp_;
DISALLOW_COPY_AND_ASSIGN(JNIStackFrameSaver);
};
#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
} // namespace android
} // namespace base
#endif // BASE_ANDROID_JNI_ANDROID_H_

View file

@ -0,0 +1,417 @@
// 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 {
namespace {
// As |GetArrayLength| makes no guarantees about the returned value (e.g., it
// may be -1 if |array| is not a valid Java array), provide a safe wrapper
// that always returns a valid, non-negative size.
template <typename JavaArrayType>
size_t SafeGetArrayLength(JNIEnv* env, const JavaRef<JavaArrayType>& jarray) {
DCHECK(jarray);
jsize length = env->GetArrayLength(jarray.obj());
DCHECK_GE(length, 0) << "Invalid array length: " << length;
return static_cast<size_t>(std::max(0, length));
}
} // namespace
ScopedJavaLocalRef<jbyteArray> ToJavaByteArray(JNIEnv* env,
const uint8_t* bytes,
size_t len) {
jbyteArray byte_array = env->NewByteArray(len);
CheckException(env);
DCHECK(byte_array);
env->SetByteArrayRegion(byte_array, 0, len,
reinterpret_cast<const jbyte*>(bytes));
CheckException(env);
return ScopedJavaLocalRef<jbyteArray>(env, byte_array);
}
ScopedJavaLocalRef<jbyteArray> ToJavaByteArray(
JNIEnv* env,
base::span<const uint8_t> bytes) {
return ToJavaByteArray(env, bytes.data(), bytes.size());
}
ScopedJavaLocalRef<jbyteArray> ToJavaByteArray(JNIEnv* env,
const std::string& str) {
return ToJavaByteArray(env, reinterpret_cast<const uint8_t*>(str.data()),
str.size());
}
ScopedJavaLocalRef<jbooleanArray> ToJavaBooleanArray(JNIEnv* env,
const bool* bools,
size_t len) {
jbooleanArray boolean_array = env->NewBooleanArray(len);
CheckException(env);
DCHECK(boolean_array);
env->SetBooleanArrayRegion(boolean_array, 0, len,
reinterpret_cast<const jboolean*>(bools));
CheckException(env);
return ScopedJavaLocalRef<jbooleanArray>(env, boolean_array);
}
ScopedJavaLocalRef<jintArray> ToJavaIntArray(JNIEnv* env,
const int* ints,
size_t len) {
jintArray int_array = env->NewIntArray(len);
CheckException(env);
DCHECK(int_array);
env->SetIntArrayRegion(int_array, 0, len,
reinterpret_cast<const jint*>(ints));
CheckException(env);
return ScopedJavaLocalRef<jintArray>(env, int_array);
}
ScopedJavaLocalRef<jintArray> ToJavaIntArray(JNIEnv* env,
base::span<const int> ints) {
return ToJavaIntArray(env, ints.data(), ints.size());
}
ScopedJavaLocalRef<jlongArray> ToJavaLongArray(JNIEnv* env,
const int64_t* longs,
size_t len) {
jlongArray long_array = env->NewLongArray(len);
CheckException(env);
DCHECK(long_array);
env->SetLongArrayRegion(long_array, 0, len,
reinterpret_cast<const jlong*>(longs));
CheckException(env);
return ScopedJavaLocalRef<jlongArray>(env, long_array);
}
// Returns a new Java long array converted from the given int64_t array.
BASE_EXPORT ScopedJavaLocalRef<jlongArray> ToJavaLongArray(
JNIEnv* env,
base::span<const int64_t> longs) {
return ToJavaLongArray(env, longs.data(), longs.size());
}
// Returns a new Java float array converted from the given C++ float array.
BASE_EXPORT ScopedJavaLocalRef<jfloatArray>
ToJavaFloatArray(JNIEnv* env, const float* floats, size_t len) {
jfloatArray float_array = env->NewFloatArray(len);
CheckException(env);
DCHECK(float_array);
env->SetFloatArrayRegion(float_array, 0, len,
reinterpret_cast<const jfloat*>(floats));
CheckException(env);
return ScopedJavaLocalRef<jfloatArray>(env, float_array);
}
BASE_EXPORT ScopedJavaLocalRef<jfloatArray> ToJavaFloatArray(
JNIEnv* env,
base::span<const float> floats) {
return ToJavaFloatArray(env, floats.data(), floats.size());
}
BASE_EXPORT ScopedJavaLocalRef<jdoubleArray>
ToJavaDoubleArray(JNIEnv* env, const double* doubles, size_t len) {
jdoubleArray double_array = env->NewDoubleArray(len);
CheckException(env);
DCHECK(double_array);
env->SetDoubleArrayRegion(double_array, 0, len,
reinterpret_cast<const jdouble*>(doubles));
CheckException(env);
return ScopedJavaLocalRef<jdoubleArray>(env, double_array);
}
BASE_EXPORT ScopedJavaLocalRef<jdoubleArray> ToJavaDoubleArray(
JNIEnv* env,
base::span<const double> doubles) {
return ToJavaDoubleArray(env, doubles.data(), doubles.size());
}
ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfByteArray(
JNIEnv* env,
base::span<const std::string> v) {
ScopedJavaLocalRef<jclass> byte_array_clazz = GetClass(env, "[B");
jobjectArray joa =
env->NewObjectArray(v.size(), byte_array_clazz.obj(), nullptr);
CheckException(env);
for (size_t i = 0; i < v.size(); ++i) {
ScopedJavaLocalRef<jbyteArray> byte_array = ToJavaByteArray(
env, reinterpret_cast<const uint8_t*>(v[i].data()), v[i].length());
env->SetObjectArrayElement(joa, i, byte_array.obj());
}
return ScopedJavaLocalRef<jobjectArray>(env, joa);
}
ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfByteArray(
JNIEnv* env,
base::span<std::vector<uint8_t>> v) {
ScopedJavaLocalRef<jclass> byte_array_clazz = GetClass(env, "[B");
jobjectArray joa =
env->NewObjectArray(v.size(), byte_array_clazz.obj(), nullptr);
CheckException(env);
for (size_t i = 0; i < v.size(); ++i) {
ScopedJavaLocalRef<jbyteArray> byte_array =
ToJavaByteArray(env, v[i].data(), v[i].size());
env->SetObjectArrayElement(joa, i, byte_array.obj());
}
return ScopedJavaLocalRef<jobjectArray>(env, joa);
}
ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfStrings(
JNIEnv* env,
base::span<const std::string> v) {
ScopedJavaLocalRef<jclass> string_clazz = GetClass(env, "java/lang/String");
jobjectArray joa = env->NewObjectArray(v.size(), string_clazz.obj(), nullptr);
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> ToJavaArrayOfStringArray(
JNIEnv* env,
base::span<const std::vector<string16>> vec_outer) {
ScopedJavaLocalRef<jclass> string_array_clazz =
GetClass(env, "[Ljava/lang/String;");
jobjectArray joa =
env->NewObjectArray(vec_outer.size(), string_array_clazz.obj(), nullptr);
CheckException(env);
for (size_t i = 0; i < vec_outer.size(); ++i) {
ScopedJavaLocalRef<jobjectArray> inner =
ToJavaArrayOfStrings(env, vec_outer[i]);
env->SetObjectArrayElement(joa, i, inner.obj());
}
return ScopedJavaLocalRef<jobjectArray>(env, joa);
}
ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfStrings(
JNIEnv* env,
base::span<const string16> v) {
ScopedJavaLocalRef<jclass> string_clazz = GetClass(env, "java/lang/String");
jobjectArray joa = env->NewObjectArray(v.size(), string_clazz.obj(), nullptr);
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,
const JavaRef<jobjectArray>& array,
std::vector<string16>* out) {
DCHECK(out);
if (!array)
return;
size_t len = SafeGetArrayLength(env, array);
size_t back = out->size();
out->resize(back + len);
for (size_t i = 0; i < len; ++i) {
ScopedJavaLocalRef<jstring> str(
env, static_cast<jstring>(env->GetObjectArrayElement(array.obj(), i)));
ConvertJavaStringToUTF16(env, str.obj(), out->data() + back + i);
}
}
void AppendJavaStringArrayToStringVector(JNIEnv* env,
const JavaRef<jobjectArray>& array,
std::vector<std::string>* out) {
DCHECK(out);
if (!array)
return;
size_t len = SafeGetArrayLength(env, array);
size_t back = out->size();
out->resize(back + len);
for (size_t i = 0; i < len; ++i) {
ScopedJavaLocalRef<jstring> str(
env, static_cast<jstring>(env->GetObjectArrayElement(array.obj(), i)));
ConvertJavaStringToUTF8(env, str.obj(), out->data() + back + i);
}
}
void AppendJavaByteArrayToByteVector(JNIEnv* env,
const JavaRef<jbyteArray>& byte_array,
std::vector<uint8_t>* out) {
DCHECK(out);
if (!byte_array)
return;
size_t len = SafeGetArrayLength(env, byte_array);
if (!len)
return;
size_t back = out->size();
out->resize(back + len);
env->GetByteArrayRegion(byte_array.obj(), 0, len,
reinterpret_cast<int8_t*>(out->data() + back));
}
void JavaByteArrayToByteVector(JNIEnv* env,
const JavaRef<jbyteArray>& byte_array,
std::vector<uint8_t>* out) {
DCHECK(out);
DCHECK(byte_array);
out->clear();
AppendJavaByteArrayToByteVector(env, byte_array, out);
}
void JavaByteArrayToString(JNIEnv* env,
const JavaRef<jbyteArray>& byte_array,
std::string* out) {
DCHECK(out);
DCHECK(byte_array);
std::vector<uint8_t> byte_vector;
JavaByteArrayToByteVector(env, byte_array, &byte_vector);
out->assign(byte_vector.begin(), byte_vector.end());
}
void JavaBooleanArrayToBoolVector(JNIEnv* env,
const JavaRef<jbooleanArray>& boolean_array,
std::vector<bool>* out) {
DCHECK(out);
if (!boolean_array)
return;
size_t len = SafeGetArrayLength(env, boolean_array);
if (!len)
return;
out->resize(len);
// It is not possible to get bool* out of vector<bool>.
jboolean* values = env->GetBooleanArrayElements(boolean_array.obj(), nullptr);
for (size_t i = 0; i < len; ++i) {
out->at(i) = static_cast<bool>(values[i]);
}
env->ReleaseBooleanArrayElements(boolean_array.obj(), values, JNI_ABORT);
}
void JavaIntArrayToIntVector(JNIEnv* env,
const JavaRef<jintArray>& int_array,
std::vector<int>* out) {
DCHECK(out);
size_t len = SafeGetArrayLength(env, int_array);
out->resize(len);
if (!len)
return;
env->GetIntArrayRegion(int_array.obj(), 0, len, out->data());
}
void JavaLongArrayToInt64Vector(JNIEnv* env,
const JavaRef<jlongArray>& long_array,
std::vector<int64_t>* out) {
DCHECK(out);
std::vector<jlong> temp;
JavaLongArrayToLongVector(env, long_array, &temp);
out->resize(0);
out->insert(out->begin(), temp.begin(), temp.end());
}
void JavaLongArrayToLongVector(JNIEnv* env,
const JavaRef<jlongArray>& long_array,
std::vector<jlong>* out) {
DCHECK(out);
size_t len = SafeGetArrayLength(env, long_array);
out->resize(len);
if (!len)
return;
env->GetLongArrayRegion(long_array.obj(), 0, len, out->data());
}
void JavaFloatArrayToFloatVector(JNIEnv* env,
const JavaRef<jfloatArray>& float_array,
std::vector<float>* out) {
DCHECK(out);
size_t len = SafeGetArrayLength(env, float_array);
out->resize(len);
if (!len)
return;
env->GetFloatArrayRegion(float_array.obj(), 0, len, out->data());
}
void JavaArrayOfByteArrayToStringVector(JNIEnv* env,
const JavaRef<jobjectArray>& array,
std::vector<std::string>* out) {
DCHECK(out);
size_t len = SafeGetArrayLength(env, array);
out->resize(len);
for (size_t i = 0; i < len; ++i) {
ScopedJavaLocalRef<jbyteArray> bytes_array(
env,
static_cast<jbyteArray>(env->GetObjectArrayElement(array.obj(), i)));
jsize bytes_len = env->GetArrayLength(bytes_array.obj());
jbyte* bytes = env->GetByteArrayElements(bytes_array.obj(), nullptr);
(*out)[i].assign(reinterpret_cast<const char*>(bytes), bytes_len);
env->ReleaseByteArrayElements(bytes_array.obj(), bytes, JNI_ABORT);
}
}
void JavaArrayOfByteArrayToBytesVector(JNIEnv* env,
const JavaRef<jobjectArray>& array,
std::vector<std::vector<uint8_t>>* out) {
DCHECK(out);
const size_t len = SafeGetArrayLength(env, array);
out->resize(len);
for (size_t i = 0; i < len; ++i) {
ScopedJavaLocalRef<jbyteArray> bytes_array(
env,
static_cast<jbyteArray>(env->GetObjectArrayElement(array.obj(), i)));
JavaByteArrayToByteVector(env, bytes_array, &(*out)[i]);
}
}
void Java2dStringArrayTo2dStringVector(
JNIEnv* env,
const JavaRef<jobjectArray>& array,
std::vector<std::vector<string16>>* out) {
DCHECK(out);
size_t len = SafeGetArrayLength(env, array);
out->resize(len);
for (size_t i = 0; i < len; ++i) {
ScopedJavaLocalRef<jobjectArray> strings_array(
env,
static_cast<jobjectArray>(env->GetObjectArrayElement(array.obj(), i)));
out->at(i).clear();
AppendJavaStringArrayToStringVector(env, strings_array, &out->at(i));
}
}
void JavaArrayOfIntArrayToIntVector(JNIEnv* env,
const JavaRef<jobjectArray>& array,
std::vector<std::vector<int>>* out) {
DCHECK(out);
size_t len = SafeGetArrayLength(env, array);
out->resize(len);
for (size_t i = 0; i < len; ++i) {
ScopedJavaLocalRef<jintArray> int_array(
env,
static_cast<jintArray>(env->GetObjectArrayElement(array.obj(), i)));
JavaIntArrayToIntVector(env, int_array, &out->at(i));
}
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,186 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_ANDROID_JNI_ARRAY_H_
#define BASE_ANDROID_JNI_ARRAY_H_
#include <jni.h>
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <vector>
#include "base/android/scoped_java_ref.h"
#include "base/containers/span.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_t* bytes,
size_t len);
BASE_EXPORT ScopedJavaLocalRef<jbyteArray> ToJavaByteArray(
JNIEnv* env,
base::span<const uint8_t> bytes);
// Returns a new Java byte array converted from the given string. No UTF-8
// conversion is performed.
BASE_EXPORT ScopedJavaLocalRef<jbyteArray> ToJavaByteArray(
JNIEnv* env,
const std::string& str);
// Returns a new Java boolean array converted from the given bool array.
BASE_EXPORT ScopedJavaLocalRef<jbooleanArray>
ToJavaBooleanArray(JNIEnv* env, const bool* bools, size_t len);
// Returns a new Java int array converted from the given int array.
BASE_EXPORT ScopedJavaLocalRef<jintArray> ToJavaIntArray(
JNIEnv* env, const int* ints, size_t len);
BASE_EXPORT ScopedJavaLocalRef<jintArray> ToJavaIntArray(
JNIEnv* env,
base::span<const int> ints);
// Returns a new Java long array converted from the given int64_t array.
BASE_EXPORT ScopedJavaLocalRef<jlongArray> ToJavaLongArray(JNIEnv* env,
const int64_t* longs,
size_t len);
BASE_EXPORT ScopedJavaLocalRef<jlongArray> ToJavaLongArray(
JNIEnv* env,
base::span<const int64_t> longs);
// Returns a new Java float array converted from the given C++ float array.
BASE_EXPORT ScopedJavaLocalRef<jfloatArray> ToJavaFloatArray(
JNIEnv* env, const float* floats, size_t len);
BASE_EXPORT ScopedJavaLocalRef<jfloatArray> ToJavaFloatArray(
JNIEnv* env,
base::span<const float> floats);
// Returns a new Java double array converted from the given C++ double array.
BASE_EXPORT ScopedJavaLocalRef<jdoubleArray>
ToJavaDoubleArray(JNIEnv* env, const double* doubles, size_t len);
BASE_EXPORT ScopedJavaLocalRef<jdoubleArray> ToJavaDoubleArray(
JNIEnv* env,
base::span<const double> doubles);
// Returns a array of Java byte array converted from |v|.
BASE_EXPORT ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfByteArray(
JNIEnv* env,
base::span<const std::string> v);
BASE_EXPORT ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfByteArray(
JNIEnv* env,
base::span<std::vector<uint8_t>> v);
BASE_EXPORT ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfStrings(
JNIEnv* env,
base::span<const std::string> v);
BASE_EXPORT ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfStrings(
JNIEnv* env,
base::span<const string16> v);
BASE_EXPORT ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfStringArray(
JNIEnv* env,
base::span<const std::vector<string16>> v);
// Converts a Java string array to a native array.
BASE_EXPORT void AppendJavaStringArrayToStringVector(
JNIEnv* env,
const JavaRef<jobjectArray>& array,
std::vector<string16>* out);
BASE_EXPORT void AppendJavaStringArrayToStringVector(
JNIEnv* env,
const JavaRef<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,
const JavaRef<jbyteArray>& byte_array,
std::vector<uint8_t>* out);
// Replaces the content of |out| with the Java bytes in |byte_array|.
BASE_EXPORT void JavaByteArrayToByteVector(
JNIEnv* env,
const JavaRef<jbyteArray>& byte_array,
std::vector<uint8_t>* out);
// Replaces the content of |out| with the Java bytes in |byte_array|. No UTF-8
// conversion is performed.
BASE_EXPORT void JavaByteArrayToString(JNIEnv* env,
const JavaRef<jbyteArray>& byte_array,
std::string* out);
// Replaces the content of |out| with the Java booleans in |boolean_array|.
BASE_EXPORT void JavaBooleanArrayToBoolVector(
JNIEnv* env,
const JavaRef<jbooleanArray>& boolean_array,
std::vector<bool>* out);
// Replaces the content of |out| with the Java ints in |int_array|.
BASE_EXPORT void JavaIntArrayToIntVector(JNIEnv* env,
const JavaRef<jintArray>& int_array,
std::vector<int>* out);
// Replaces the content of |out| with the Java longs in |long_array|.
BASE_EXPORT void JavaLongArrayToInt64Vector(
JNIEnv* env,
const JavaRef<jlongArray>& long_array,
std::vector<int64_t>* out);
// Replaces the content of |out| with the Java longs in |long_array|.
BASE_EXPORT void JavaLongArrayToLongVector(
JNIEnv* env,
const JavaRef<jlongArray>& long_array,
std::vector<jlong>* out);
// Replaces the content of |out| with the Java floats in |float_array|.
BASE_EXPORT void JavaFloatArrayToFloatVector(
JNIEnv* env,
const JavaRef<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,
const JavaRef<jobjectArray>& array,
std::vector<std::string>* out);
// Assuming |array| is an byte[][] (array of byte arrays), replaces the
// content of |out| with the corresponding vector of vector of uint8. No UTF-8
// conversion is performed.
BASE_EXPORT void JavaArrayOfByteArrayToBytesVector(
JNIEnv* env,
const JavaRef<jobjectArray>& array,
std::vector<std::vector<uint8_t>>* out);
// Assuming |array| is an String[][] (array of String arrays), replaces the
// content of |out| with the corresponding vector of string vectors. No UTF-8
// conversion is performed.
BASE_EXPORT void Java2dStringArrayTo2dStringVector(
JNIEnv* env,
const JavaRef<jobjectArray>& array,
std::vector<std::vector<string16>>* out);
// Assuming |array| is an int[][] (array of int arrays), replaces the
// contents of |out| with the corresponding vectors of ints.
BASE_EXPORT void JavaArrayOfIntArrayToIntVector(
JNIEnv* env,
const JavaRef<jobjectArray>& array,
std::vector<std::vector<int>>* out);
} // namespace android
} // namespace base
#endif // BASE_ANDROID_JNI_ARRAY_H_

View file

@ -0,0 +1,56 @@
// Copyright 2014 The Chromium 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_INT_WRAPPER_H_
#define BASE_ANDROID_JNI_INT_WRAPPER_H_
// Wrapper used to receive int when calling Java from native.
// The wrapper disallows automatic conversion of long to int.
// This is to avoid a common anti-pattern where a Java int is used
// to receive a native pointer. Please use a Java long to receive
// native pointers, so that the code works on both 32-bit and 64-bit
// platforms. Note the wrapper allows other lossy conversions into
// jint that could be consider anti-patterns, such as from size_t.
// Checking is only done in debugging builds.
#ifdef NDEBUG
typedef jint JniIntWrapper;
// This inline is sufficiently trivial that it does not change the
// final code generated by g++.
inline jint as_jint(JniIntWrapper wrapper) {
return wrapper;
}
#else
class JniIntWrapper {
public:
JniIntWrapper() : i_(0) {}
JniIntWrapper(int i) : i_(i) {}
JniIntWrapper(const JniIntWrapper& ji) : i_(ji.i_) {}
template <class T> JniIntWrapper(const T& t) : i_(t) {}
jint as_jint() const { return i_; }
private:
// If you get an "is private" error at the line below it is because you used
// an implicit conversion to convert a long to an int when calling Java.
// We disallow this, as a common anti-pattern allows converting a native
// pointer (intptr_t) to a Java int. Please use a Java long to represent
// a native pointer. If you want a lossy conversion, please use an
// explicit conversion in your C++ code. Note an error is only seen when
// compiling on a 64-bit platform, as intptr_t is indistinguishable from
// int on 32-bit platforms.
JniIntWrapper(long);
jint i_;
};
inline jint as_jint(const JniIntWrapper& wrapper) {
return wrapper.as_jint();
}
#endif // NDEBUG
#endif // BASE_ANDROID_JNI_INT_WRAPPER_H_

View file

@ -0,0 +1,30 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/android/jni_registrar.h"
#include "base/logging.h"
#include "base/android/jni_android.h"
#include "base/trace_event/trace_event.h"
namespace base {
namespace android {
bool RegisterNativeMethods(JNIEnv* env,
const RegistrationMethod* method,
size_t count) {
TRACE_EVENT0("startup", "base_android::RegisterNativeMethods")
const RegistrationMethod* end = method + count;
while (method != end) {
if (!method->func(env)) {
DLOG(ERROR) << method->name << " failed registration!";
return false;
}
method++;
}
return true;
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,28 @@
// 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 <stddef.h>
#include "base/base_export.h"
namespace base {
namespace android {
struct RegistrationMethod;
// Registers the JNI bindings for the specified |method| definition containing
// |count| elements. Returns whether the registration of the given methods
// succeeded.
BASE_EXPORT bool RegisterNativeMethods(JNIEnv* env,
const RegistrationMethod* method,
size_t count);
} // namespace android
} // namespace base
#endif // BASE_ANDROID_JNI_REGISTRAR_H_

View file

@ -0,0 +1,120 @@
// 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(reinterpret_cast<const jchar*>(str.data()), str.length());
base::android::CheckException(env);
return result;
}
} // namespace
namespace base {
namespace android {
void ConvertJavaStringToUTF8(JNIEnv* env, jstring str, std::string* result) {
DCHECK(str);
if (!str) {
LOG(WARNING) << "ConvertJavaStringToUTF8 called with null string.";
result->clear();
return;
}
const jsize length = env->GetStringLength(str);
if (!length) {
result->clear();
CheckException(env);
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(reinterpret_cast<const char16*>(chars), length, 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());
}
std::string ConvertJavaStringToUTF8(JNIEnv* env, const JavaRef<jstring>& str) {
return ConvertJavaStringToUTF8(env, str.obj());
}
ScopedJavaLocalRef<jstring> ConvertUTF8ToJavaString(JNIEnv* env,
const 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) {
DCHECK(str);
if (!str) {
LOG(WARNING) << "ConvertJavaStringToUTF16 called with null string.";
result->clear();
return;
}
const jsize length = env->GetStringLength(str);
if (!length) {
result->clear();
CheckException(env);
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(reinterpret_cast<const char16*>(chars), length);
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());
}
string16 ConvertJavaStringToUTF16(JNIEnv* env, const JavaRef<jstring>& str) {
return ConvertJavaStringToUTF16(env, str.obj());
}
ScopedJavaLocalRef<jstring> ConvertUTF16ToJavaString(JNIEnv* env,
const StringPiece16& str) {
return ScopedJavaLocalRef<jstring>(env,
ConvertUTF16ToJavaStringImpl(env, str));
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,49 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#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);
BASE_EXPORT std::string ConvertJavaStringToUTF8(JNIEnv* env,
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);
BASE_EXPORT string16 ConvertJavaStringToUTF16(JNIEnv* env,
const JavaRef<jstring>& str);
// Convert a string16 to a Java string.
BASE_EXPORT ScopedJavaLocalRef<jstring> ConvertUTF16ToJavaString(
JNIEnv* env,
const base::StringPiece16& str);
} // namespace android
} // namespace base
#endif // BASE_ANDROID_JNI_STRING_H_

View file

@ -0,0 +1,24 @@
// Copyright 2014 The Chromium 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_utils.h"
#include "base/android/scoped_java_ref.h"
#include "base/base_jni_headers/JNIUtils_jni.h"
namespace base {
namespace android {
ScopedJavaLocalRef<jobject> GetClassLoader(JNIEnv* env) {
return Java_JNIUtils_getClassLoader(env);
}
bool IsSelectiveJniRegistrationEnabled(JNIEnv* env) {
return Java_JNIUtils_isSelectiveJniRegistrationEnabled(env);
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,28 @@
// Copyright 2014 The Chromium 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_UTILS_H_
#define BASE_ANDROID_JNI_UTILS_H_
#include <jni.h>
#include "base/android/scoped_java_ref.h"
namespace base {
namespace android {
// Gets a ClassLoader instance capable of loading Chromium java classes.
// This should be called either from JNI_OnLoad or from within a method called
// via JNI from Java.
BASE_EXPORT ScopedJavaLocalRef<jobject> GetClassLoader(JNIEnv* env);
// Returns true if the current process permits selective JNI registration.
BASE_EXPORT bool IsSelectiveJniRegistrationEnabled(JNIEnv* env);
} // namespace android
} // namespace base
#endif // BASE_ANDROID_JNI_UTILS_H_

View file

@ -0,0 +1,79 @@
// Copyright 2014 The Chromium 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_weak_ref.h"
#include <utility>
#include "base/android/jni_android.h"
#include "base/logging.h"
using base::android::AttachCurrentThread;
JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef() : obj_(nullptr) {}
JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef(
const JavaObjectWeakGlobalRef& orig)
: obj_(nullptr) {
Assign(orig);
}
JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef(
JavaObjectWeakGlobalRef&& orig) noexcept
: obj_(orig.obj_) {
orig.obj_ = nullptr;
}
JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef(JNIEnv* env, jobject obj)
: obj_(env->NewWeakGlobalRef(obj)) {
}
JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef(
JNIEnv* env,
const base::android::JavaRef<jobject>& obj)
: obj_(env->NewWeakGlobalRef(obj.obj())) {
}
JavaObjectWeakGlobalRef::~JavaObjectWeakGlobalRef() {
reset();
}
void JavaObjectWeakGlobalRef::operator=(const JavaObjectWeakGlobalRef& rhs) {
Assign(rhs);
}
void JavaObjectWeakGlobalRef::operator=(JavaObjectWeakGlobalRef&& rhs) {
std::swap(obj_, rhs.obj_);
}
void JavaObjectWeakGlobalRef::reset() {
if (obj_) {
AttachCurrentThread()->DeleteWeakGlobalRef(obj_);
obj_ = nullptr;
}
}
base::android::ScopedJavaLocalRef<jobject>
JavaObjectWeakGlobalRef::get(JNIEnv* env) const {
return GetRealObject(env, obj_);
}
base::android::ScopedJavaLocalRef<jobject> GetRealObject(
JNIEnv* env, jweak obj) {
jobject real = nullptr;
if (obj)
real = env->NewLocalRef(obj);
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_) : nullptr;
}

View file

@ -0,0 +1,51 @@
// Copyright 2014 The Chromium 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_WEAK_REF_H_
#define BASE_ANDROID_JNI_WEAK_REF_H_
#include <jni.h>
#include "base/android/scoped_java_ref.h"
#include "base/base_export.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(JavaObjectWeakGlobalRef&& orig) noexcept;
JavaObjectWeakGlobalRef(JNIEnv* env, jobject obj);
JavaObjectWeakGlobalRef(JNIEnv* env,
const base::android::JavaRef<jobject>& obj);
virtual ~JavaObjectWeakGlobalRef();
void operator=(const JavaObjectWeakGlobalRef& rhs);
void operator=(JavaObjectWeakGlobalRef&& rhs);
base::android::ScopedJavaLocalRef<jobject> get(JNIEnv* env) const;
// Returns true if the weak reference has not been initialized to point at
// an object (or ḣas had reset() called).
// Do not call this to test if the object referred to still exists! The weak
// reference remains initialized even if the target object has been collected.
bool is_uninitialized() const { return obj_ == nullptr; }
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_WEAK_REF_H_

View file

@ -0,0 +1,10 @@
# //base/android/library_loader
Native code is split between this directory and:
* [//third_party/android_crazy_linker](../../../third_party/android_crazy_linker/README.chromium)
Java code lives at:
* [//base/android/java/src/org/chromium/base/library_loader/](../java/src/org/chromium/base/library_loader/)
A high-level guide to native code on Android exists at:
* [//docs/android_native_libraries.md](../../../docs/android_native_libraries.md)

View file

@ -0,0 +1,100 @@
// Copyright 2017 The Chromium 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/library_loader/anchor_functions.h"
#include "base/logging.h"
#include "build/build_config.h"
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
// These functions are here to delimit the start and end of the ordered part of
// .text. They require a suitably constructed orderfile, with these functions at
// the beginning and end.
//
// These functions are weird: this is due to ICF (Identical Code Folding).
// The linker merges functions that have the same code, which would be the case
// if these functions were empty, or simple.
// Gold's flag --icf=safe will *not* alias functions when their address is used
// in code, but as of November 2017, we use the default setting that
// deduplicates function in this case as well.
//
// Thus these functions are made to be unique, using inline .word in assembly,
// or the equivalent directive depending on the architecture.
//
// Note that code |CheckOrderingSanity()| below will make sure that these
// functions are not aliased, in case the toolchain becomes really clever.
extern "C" {
// The assembler has a different syntax depending on the architecture.
#if defined(ARCH_CPU_ARMEL) || defined(ARCH_CPU_ARM64)
// These functions have a well-defined ordering in this file, see the comment
// in |IsOrderingSane()|.
void dummy_function_end_of_ordered_text() {
asm(".word 0x21bad44d");
asm(".word 0xb815c5b0");
}
void dummy_function_start_of_ordered_text() {
asm(".word 0xe4a07375");
asm(".word 0x66dda6dc");
}
#elif defined(ARCH_CPU_X86_FAMILY)
void dummy_function_end_of_ordered_text() {
asm(".4byte 0x21bad44d");
asm(".4byte 0xb815c5b0");
}
void dummy_function_start_of_ordered_text() {
asm(".4byte 0xe4a07375");
asm(".4byte 0x66dda6dc");
}
#endif
// These two symbols are defined by anchor_functions.lds and delimit the start
// and end of .text.
void linker_script_start_of_text();
void linker_script_end_of_text();
} // extern "C"
namespace base {
namespace android {
const size_t kStartOfText =
reinterpret_cast<size_t>(linker_script_start_of_text);
const size_t kEndOfText = reinterpret_cast<size_t>(linker_script_end_of_text);
const size_t kStartOfOrderedText =
reinterpret_cast<size_t>(dummy_function_start_of_ordered_text);
const size_t kEndOfOrderedText =
reinterpret_cast<size_t>(dummy_function_end_of_ordered_text);
bool AreAnchorsSane() {
size_t here = reinterpret_cast<size_t>(&IsOrderingSane);
return kStartOfText < here && here < kEndOfText;
}
bool IsOrderingSane() {
// The symbols linker_script_start_of_text and linker_script_end_of_text
// should cover all of .text, and dummy_function_start_of_ordered_text and
// dummy_function_end_of_ordered_text should cover the ordered part of it.
// This check is intended to catch the lack of ordering.
//
// Ordered text can start at the start of text, but should not cover the
// entire range. Most addresses are distinct nonetheless as the symbols are
// different, but linker-defined symbols have zero size and therefore the
// start address could be the same as the address of
// dummy_function_start_of_ordered_text.
return AreAnchorsSane() && kStartOfOrderedText < kEndOfOrderedText &&
kStartOfText <= kStartOfOrderedText && kEndOfOrderedText < kEndOfText;
}
} // namespace android
} // namespace base
#endif // BUILDFLAG(SUPPORTS_CODE_ORDERING)

View file

@ -0,0 +1,35 @@
// Copyright 2017 The Chromium 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_LIBRARY_LOADER_ANCHOR_FUNCTIONS_H_
#define BASE_ANDROID_LIBRARY_LOADER_ANCHOR_FUNCTIONS_H_
#include <cstdint>
#include "base/android/library_loader/anchor_functions_buildflags.h"
#include "base/base_export.h"
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
namespace base {
namespace android {
// Start and end of .text, respectively.
BASE_EXPORT extern const size_t kStartOfText;
BASE_EXPORT extern const size_t kEndOfText;
// Start and end of the ordered part of .text, respectively.
BASE_EXPORT extern const size_t kStartOfOrderedText;
BASE_EXPORT extern const size_t kEndOfOrderedText;
// Returns true if anchors are sane.
BASE_EXPORT bool AreAnchorsSane();
// Returns true if the ordering looks sane.
BASE_EXPORT bool IsOrderingSane();
} // namespace android
} // namespace base
#endif // BUILDFLAG(SUPPORTS_CODE_ORDERING)
#endif // BASE_ANDROID_LIBRARY_LOADER_ANCHOR_FUNCTIONS_H_

View file

@ -0,0 +1,7 @@
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Define symbols that point to the start and end of the .text section.
PROVIDE_HIDDEN(linker_script_start_of_text = ADDR(.text));
PROVIDE_HIDDEN(linker_script_end_of_text = ADDR(.text) + SIZEOF(.text));

View file

@ -0,0 +1,134 @@
// Copyright 2014 The Chromium 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/library_loader/library_loader_hooks.h"
#include <string>
#include "base/android/jni_string.h"
#include "base/android/library_loader/anchor_functions_buildflags.h"
#include "base/android/library_loader/library_prefetcher.h"
#include "base/android/orderfile/orderfile_buildflags.h"
#include "base/android/sys_utils.h"
#include "base/at_exit.h"
#include "base/base_jni_headers/LibraryLoader_jni.h"
#include "base/base_switches.h"
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
#include "base/android/orderfile/orderfile_instrumentation.h"
#endif
namespace base {
namespace android {
namespace {
base::AtExitManager* g_at_exit_manager = nullptr;
const char* g_library_version_number = "";
LibraryLoadedHook* g_registration_callback = nullptr;
NativeInitializationHook* g_native_initialization_hook = nullptr;
NonMainDexJniRegistrationHook* g_jni_registration_hook = nullptr;
// The amount of time, in milliseconds, that it took to load the shared
// libraries in the renderer. Set in
// JNI_LibraryLoader_RecordRendererLibraryLoadTime().
long g_renderer_library_load_time_ms = 0;
} // namespace
bool IsUsingOrderfileOptimization() {
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
return SysUtils::IsLowEndDeviceFromJni();
#else // !SUPPORTS_CODE_ORDERING
return false;
#endif
}
static void JNI_LibraryLoader_RecordRendererLibraryLoadTime(
JNIEnv* env,
jlong library_load_time_ms) {
g_renderer_library_load_time_ms = library_load_time_ms;
}
void SetNativeInitializationHook(
NativeInitializationHook native_initialization_hook) {
g_native_initialization_hook = native_initialization_hook;
}
void SetNonMainDexJniRegistrationHook(
NonMainDexJniRegistrationHook jni_registration_hook) {
DCHECK(!g_jni_registration_hook);
g_jni_registration_hook = jni_registration_hook;
}
void RecordLibraryLoaderRendererHistograms() {
// Record how long it took to load the shared libraries.
UMA_HISTOGRAM_TIMES(
"ChromiumAndroidLinker.RendererLoadTime",
base::TimeDelta::FromMilliseconds(g_renderer_library_load_time_ms));
}
void SetLibraryLoadedHook(LibraryLoadedHook* func) {
g_registration_callback = func;
}
static jboolean JNI_LibraryLoader_LibraryLoaded(
JNIEnv* env,
jint library_process_type) {
#if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
orderfile::StartDelayedDump();
#endif
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
if (CommandLine::ForCurrentProcess()->HasSwitch(
"log-native-library-residency")) {
NativeLibraryPrefetcher::MadviseForResidencyCollection();
} else if (IsUsingOrderfileOptimization()) {
NativeLibraryPrefetcher::MadviseForOrderfile();
}
#endif
if (g_native_initialization_hook &&
!g_native_initialization_hook(
static_cast<LibraryProcessType>(library_process_type)))
return false;
if (g_registration_callback &&
!g_registration_callback(
env, nullptr,
static_cast<LibraryProcessType>(library_process_type))) {
return false;
}
return true;
}
static void JNI_LibraryLoader_RegisterNonMainDexJni(JNIEnv* env) {
if (g_jni_registration_hook) {
g_jni_registration_hook();
}
}
void LibraryLoaderExitHook() {
if (g_at_exit_manager) {
delete g_at_exit_manager;
g_at_exit_manager = nullptr;
}
}
void SetVersionNumber(const char* version_number) {
g_library_version_number = strdup(version_number);
}
ScopedJavaLocalRef<jstring> JNI_LibraryLoader_GetVersionNumber(JNIEnv* env) {
return ConvertUTF8ToJavaString(env, g_library_version_number);
}
void InitAtExitManager() {
g_at_exit_manager = new base::AtExitManager();
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,92 @@
// Copyright 2014 The Chromium 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_LIBRARY_LOADER_LIBRARY_LOADER_HOOKS_H_
#define BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOADER_HOOKS_H_
#include <jni.h>
#include "base/base_export.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/metrics/field_trial.h"
namespace base {
namespace android {
// The process the shared library is loaded in.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base.library_loader
enum LibraryProcessType {
// The LibraryLoad has not been initialized.
PROCESS_UNINITIALIZED = 0,
// Shared library is running in browser process.
PROCESS_BROWSER = 1,
// Shared library is running in child process.
PROCESS_CHILD = 2,
// Shared library is running in the app that uses webview.
PROCESS_WEBVIEW = 3,
// Shared library is running in child process as part of webview.
PROCESS_WEBVIEW_CHILD = 4,
// Shared library is running in the app that uses weblayer.
PROCESS_WEBLAYER = 5,
// Shared library is running in child process as part of weblayer.
PROCESS_WEBLAYER_CHILD = 6,
};
// Whether fewer code should be prefetched, and no-readahead should be set.
// Returns true on low-end devices, where this speeds up startup, and false
// elsewhere, where it slows it down. See
// https://bugs.chromium.org/p/chromium/issues/detail?id=758566#c71 for details.
BASE_EXPORT bool IsUsingOrderfileOptimization();
typedef bool NativeInitializationHook(LibraryProcessType library_process_type);
BASE_EXPORT void SetNativeInitializationHook(
NativeInitializationHook native_initialization_hook);
typedef void NonMainDexJniRegistrationHook();
BASE_EXPORT void SetNonMainDexJniRegistrationHook(
NonMainDexJniRegistrationHook jni_registration_hook);
// Record any pending renderer histogram value as histograms. Pending values
// are set by
// JNI_LibraryLoader_RegisterChromiumAndroidLinkerRendererHistogram().
BASE_EXPORT void RecordLibraryLoaderRendererHistograms();
// Typedef for hook function to be called (indirectly from Java) once the
// libraries are loaded. The hook function should register the JNI bindings
// required to start the application. It should return true for success and
// false for failure.
// Note: this can't use base::Callback because there is no way of initializing
// the default callback without using static objects, which we forbid.
typedef bool LibraryLoadedHook(JNIEnv* env,
jclass clazz,
LibraryProcessType library_process_type);
// Set the hook function to be called (from Java) once the libraries are loaded.
// SetLibraryLoadedHook may only be called from JNI_OnLoad. The hook function
// should register the JNI bindings required to start the application.
BASE_EXPORT void SetLibraryLoadedHook(LibraryLoadedHook* func);
// Pass the version name to the loader. This used to check that the library
// version matches the version expected by Java before completing JNI
// registration.
// Note: argument must remain valid at least until library loading is complete.
BASE_EXPORT void SetVersionNumber(const char* version_number);
// Call on exit to delete the AtExitManager which OnLibraryLoadedOnUIThread
// created.
BASE_EXPORT void LibraryLoaderExitHook();
// Initialize AtExitManager, this must be done at the begining of loading
// shared library.
void InitAtExitManager();
} // namespace android
} // namespace base
#endif // BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOADER_HOOKS_H_

View file

@ -0,0 +1,347 @@
// Copyright 2015 The Chromium 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/library_loader/library_prefetcher.h"
#include <stddef.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <unistd.h>
#include <algorithm>
#include <atomic>
#include <cstdlib>
#include <memory>
#include <utility>
#include <vector>
#include "base/android/library_loader/anchor_functions.h"
#include "base/android/orderfile/orderfile_buildflags.h"
#include "base/bits.h"
#include "base/files/file.h"
#include "base/format_macros.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/posix/eintr_wrapper.h"
#include "base/process/process_metrics.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
#include "base/android/orderfile/orderfile_instrumentation.h"
#endif
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
namespace base {
namespace android {
namespace {
// Valid for all Android architectures.
constexpr size_t kPageSize = 4096;
// Populates the per-page residency between |start| and |end| in |residency|. If
// successful, |residency| has the size of |end| - |start| in pages.
// Returns true for success.
bool Mincore(size_t start, size_t end, std::vector<unsigned char>* residency) {
if (start % kPageSize || end % kPageSize)
return false;
size_t size = end - start;
size_t size_in_pages = size / kPageSize;
if (residency->size() != size_in_pages)
residency->resize(size_in_pages);
int err = HANDLE_EINTR(
mincore(reinterpret_cast<void*>(start), size, &(*residency)[0]));
PLOG_IF(ERROR, err) << "mincore() failed";
return !err;
}
// Returns the start and end of .text, aligned to the lower and upper page
// boundaries, respectively.
std::pair<size_t, size_t> GetTextRange() {
// |kStartOfText| may not be at the beginning of a page, since .plt can be
// before it, yet in the same mapping for instance.
size_t start_page = kStartOfText - kStartOfText % kPageSize;
// Set the end to the page on which the beginning of the last symbol is. The
// actual symbol may spill into the next page by a few bytes, but this is
// outside of the executable code range anyway.
size_t end_page = base::bits::Align(kEndOfText, kPageSize);
return {start_page, end_page};
}
// Returns the start and end pages of the unordered section of .text, aligned to
// lower and upper page boundaries, respectively.
std::pair<size_t, size_t> GetOrderedTextRange() {
size_t start_page = kStartOfOrderedText - kStartOfOrderedText % kPageSize;
// kEndOfUnorderedText is not considered ordered, but the byte immediately
// before is considered ordered and so can not be contained in the start page.
size_t end_page = base::bits::Align(kEndOfOrderedText, kPageSize);
return {start_page, end_page};
}
// Calls madvise(advice) on the specified range. Does nothing if the range is
// empty.
void MadviseOnRange(const std::pair<size_t, size_t>& range, int advice) {
if (range.first >= range.second) {
return;
}
size_t size = range.second - range.first;
int err = madvise(reinterpret_cast<void*>(range.first), size, advice);
if (err) {
PLOG(ERROR) << "madvise() failed";
}
}
// Timestamp in ns since Unix Epoch, and residency, as returned by mincore().
struct TimestampAndResidency {
uint64_t timestamp_nanos;
std::vector<unsigned char> residency;
TimestampAndResidency(uint64_t timestamp_nanos,
std::vector<unsigned char>&& residency)
: timestamp_nanos(timestamp_nanos), residency(residency) {}
};
// Returns true for success.
bool CollectResidency(size_t start,
size_t end,
std::vector<TimestampAndResidency>* data) {
// Not using base::TimeTicks() to not call too many base:: symbol that would
// pollute the reached symbols dumps.
struct timespec ts;
if (HANDLE_EINTR(clock_gettime(CLOCK_MONOTONIC, &ts))) {
PLOG(ERROR) << "Cannot get the time.";
return false;
}
uint64_t now =
static_cast<uint64_t>(ts.tv_sec) * 1000 * 1000 * 1000 + ts.tv_nsec;
std::vector<unsigned char> residency;
if (!Mincore(start, end, &residency))
return false;
data->emplace_back(now, std::move(residency));
return true;
}
void DumpResidency(size_t start,
size_t end,
std::unique_ptr<std::vector<TimestampAndResidency>> data) {
LOG(WARNING) << "Dumping native library residency";
auto path = base::FilePath(
base::StringPrintf("/data/local/tmp/chrome/residency-%d.txt", getpid()));
auto file =
base::File(path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
if (!file.IsValid()) {
PLOG(ERROR) << "Cannot open file to dump the residency data "
<< path.value();
return;
}
// First line: start-end of text range.
CHECK(AreAnchorsSane());
CHECK_LE(start, kStartOfText);
CHECK_LE(kEndOfText, end);
auto start_end = base::StringPrintf("%" PRIuS " %" PRIuS "\n",
kStartOfText - start, kEndOfText - start);
file.WriteAtCurrentPos(start_end.c_str(), start_end.size());
for (const auto& data_point : *data) {
auto timestamp =
base::StringPrintf("%" PRIu64 " ", data_point.timestamp_nanos);
file.WriteAtCurrentPos(timestamp.c_str(), timestamp.size());
std::vector<char> dump;
dump.reserve(data_point.residency.size() + 1);
for (auto c : data_point.residency)
dump.push_back(c ? '1' : '0');
dump[dump.size() - 1] = '\n';
file.WriteAtCurrentPos(&dump[0], dump.size());
}
}
#if !BUILDFLAG(ORDERFILE_INSTRUMENTATION)
// Reads a byte per page between |start| and |end| to force it into the page
// cache.
// Heap allocations, syscalls and library functions are not allowed in this
// function.
// Returns true for success.
#if defined(ADDRESS_SANITIZER)
// Disable AddressSanitizer instrumentation for this function. It is touching
// memory that hasn't been allocated by the app, though the addresses are
// valid. Furthermore, this takes place in a child process. See crbug.com/653372
// for the context.
__attribute__((no_sanitize_address))
#endif
void Prefetch(size_t start, size_t end) {
unsigned char* start_ptr = reinterpret_cast<unsigned char*>(start);
unsigned char* end_ptr = reinterpret_cast<unsigned char*>(end);
unsigned char dummy = 0;
for (unsigned char* ptr = start_ptr; ptr < end_ptr; ptr += kPageSize) {
// Volatile is required to prevent the compiler from eliminating this
// loop.
dummy ^= *static_cast<volatile unsigned char*>(ptr);
}
}
// These values were used in the past for recording
// "LibraryLoader.PrefetchDetailedStatus".
enum class PrefetchStatus {
kSuccess = 0,
kWrongOrdering = 1,
kForkFailed = 2,
kChildProcessCrashed = 3,
kChildProcessKilled = 4,
kMaxValue = kChildProcessKilled
};
PrefetchStatus ForkAndPrefetch(bool ordered_only) {
if (!IsOrderingSane()) {
LOG(WARNING) << "Incorrect code ordering";
return PrefetchStatus::kWrongOrdering;
}
// Looking for ranges is done before the fork, to avoid syscalls and/or memory
// allocations in the forked process. The child process inherits the lock
// state of its parent thread. It cannot rely on being able to acquire any
// lock (unless special care is taken in a pre-fork handler), including being
// able to call malloc().
//
// Always prefetch the ordered section first, as it's reached early during
// startup, and not necessarily located at the beginning of .text.
std::vector<std::pair<size_t, size_t>> ranges = {GetOrderedTextRange()};
if (!ordered_only)
ranges.push_back(GetTextRange());
pid_t pid = fork();
if (pid == 0) {
// Android defines the background priority to this value since at least 2009
// (see Process.java).
constexpr int kBackgroundPriority = 10;
setpriority(PRIO_PROCESS, 0, kBackgroundPriority);
// _exit() doesn't call the atexit() handlers.
for (const auto& range : ranges) {
Prefetch(range.first, range.second);
}
_exit(EXIT_SUCCESS);
} else {
if (pid < 0) {
return PrefetchStatus::kForkFailed;
}
int status;
const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0));
if (result == pid) {
if (WIFEXITED(status))
return PrefetchStatus::kSuccess;
if (WIFSIGNALED(status)) {
int signal = WTERMSIG(status);
switch (signal) {
case SIGSEGV:
case SIGBUS:
return PrefetchStatus::kChildProcessCrashed;
break;
case SIGKILL:
case SIGTERM:
default:
return PrefetchStatus::kChildProcessKilled;
}
}
}
// Should not happen. Per man waitpid(2), errors are:
// - EINTR: handled.
// - ECHILD if the process doesn't have an unwaited-for child with this PID.
// - EINVAL.
return PrefetchStatus::kChildProcessKilled;
}
}
#endif // !BUILDFLAG(ORDERFILE_INSTRUMENTATION)
} // namespace
// static
void NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary(bool ordered_only) {
#if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
// Avoid forking with orderfile instrumentation because the child process
// would create a dump as well.
return;
#else
PrefetchStatus status = ForkAndPrefetch(ordered_only);
if (status != PrefetchStatus::kSuccess) {
LOG(WARNING) << "Cannot prefetch the library. status = "
<< static_cast<int>(status);
}
#endif // BUILDFLAG(ORDERFILE_INSTRUMENTATION)
}
// static
int NativeLibraryPrefetcher::PercentageOfResidentCode(size_t start,
size_t end) {
size_t total_pages = 0;
size_t resident_pages = 0;
std::vector<unsigned char> residency;
bool ok = Mincore(start, end, &residency);
if (!ok)
return -1;
total_pages += residency.size();
resident_pages += std::count_if(residency.begin(), residency.end(),
[](unsigned char x) { return x & 1; });
if (total_pages == 0)
return -1;
return static_cast<int>((100 * resident_pages) / total_pages);
}
// static
int NativeLibraryPrefetcher::PercentageOfResidentNativeLibraryCode() {
if (!AreAnchorsSane()) {
LOG(WARNING) << "Incorrect code ordering";
return -1;
}
const auto& range = GetTextRange();
return PercentageOfResidentCode(range.first, range.second);
}
// static
void NativeLibraryPrefetcher::PeriodicallyCollectResidency() {
CHECK_EQ(static_cast<long>(kPageSize), sysconf(_SC_PAGESIZE));
LOG(WARNING) << "Spawning thread to periodically collect residency";
const auto& range = GetTextRange();
auto data = std::make_unique<std::vector<TimestampAndResidency>>();
// Collect residency for about minute (the actual time spent collecting
// residency can vary, so this is only approximate).
for (int i = 0; i < 120; ++i) {
if (!CollectResidency(range.first, range.second, data.get()))
return;
usleep(5e5);
}
DumpResidency(range.first, range.second, std::move(data));
}
// static
void NativeLibraryPrefetcher::MadviseForOrderfile() {
if (!IsOrderingSane()) {
LOG(WARNING) << "Code not ordered, madvise optimization skipped";
return;
}
// First MADV_RANDOM on all of text, then turn the ordered text range back to
// normal. The ordered range may be placed anywhere within .text.
MadviseOnRange(GetTextRange(), MADV_RANDOM);
MadviseOnRange(GetOrderedTextRange(), MADV_NORMAL);
}
// static
void NativeLibraryPrefetcher::MadviseForResidencyCollection() {
if (!AreAnchorsSane()) {
LOG(WARNING) << "Code not ordered, cannot madvise";
return;
}
LOG(WARNING) << "Performing madvise for residency collection";
MadviseOnRange(GetTextRange(), MADV_RANDOM);
}
} // namespace android
} // namespace base
#endif // BUILDFLAG(SUPPORTS_CODE_ORDERING)

View file

@ -0,0 +1,71 @@
// Copyright 2015 The Chromium 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_LIBRARY_LOADER_LIBRARY_PREFETCHER_H_
#define BASE_ANDROID_LIBRARY_LOADER_LIBRARY_PREFETCHER_H_
#include <jni.h>
#include <stdint.h>
#include <string>
#include "base/android/library_loader/anchor_functions_buildflags.h"
#include "base/base_export.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
namespace base {
namespace android {
// Forks and waits for a process prefetching the native library. This is done in
// a forked process for the following reasons:
// - Isolating the main process from mistakes in getting the address range, only
// crashing the forked process in case of mistake.
// - Not inflating the memory used by the main process uselessly, which could
// increase its likelihood to be killed.
// The forked process has background priority and, since it is not declared to
// the Android runtime, can be killed at any time, which is not an issue here.
class BASE_EXPORT NativeLibraryPrefetcher {
public:
// Finds the executable code range, forks a low priority process pre-fetching
// it wait()s for the process to exit or die. If ordered_only is true, only
// the ordered section is prefetched. See GetOrdrderedTextRange() in
// library_prefetcher.cc.
static void ForkAndPrefetchNativeLibrary(bool ordered_only);
// Returns the percentage of the native library code currently resident in
// memory, or -1 in case of error.
static int PercentageOfResidentNativeLibraryCode();
// Collects residency for the native library executable multiple times, then
// dumps it to disk.
static void PeriodicallyCollectResidency();
// Calls madvise() on the native library executable, using orderfile
// information to decide how to advise each part of the library.
static void MadviseForOrderfile();
// Calls madvise() on the native library executable so that residency
// collection is accurate.
static void MadviseForResidencyCollection();
private:
// Returns the percentage of [start, end] currently resident in
// memory, or -1 in case of error.
static int PercentageOfResidentCode(size_t start, size_t end);
FRIEND_TEST_ALL_PREFIXES(NativeLibraryPrefetcherTest,
TestPercentageOfResidentCode);
DISALLOW_IMPLICIT_CONSTRUCTORS(NativeLibraryPrefetcher);
};
} // namespace android
} // namespace base
#endif // BUILDFLAG(SUPPORTS_CODE_ORDERING)
#endif // BASE_ANDROID_LIBRARY_LOADER_LIBRARY_PREFETCHER_H_

View file

@ -0,0 +1,44 @@
// Copyright 2019 The Chromium 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 <string>
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/library_loader/anchor_functions_buildflags.h"
#include "base/android/library_loader/library_loader_hooks.h"
#include "base/android/library_loader/library_prefetcher.h"
#include "base/android/scoped_java_ref.h"
#include "base/base_jni_headers/LibraryPrefetcher_jni.h"
#include "base/logging.h"
namespace base {
namespace android {
static void JNI_LibraryPrefetcher_ForkAndPrefetchNativeLibrary(JNIEnv* env) {
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
return NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary(
IsUsingOrderfileOptimization());
#endif
}
static jint JNI_LibraryPrefetcher_PercentageOfResidentNativeLibraryCode(
JNIEnv* env) {
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
return NativeLibraryPrefetcher::PercentageOfResidentNativeLibraryCode();
#else
return -1;
#endif
}
static void JNI_LibraryPrefetcher_PeriodicallyCollectResidency(JNIEnv* env) {
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
NativeLibraryPrefetcher::PeriodicallyCollectResidency();
#else
LOG(WARNING) << "Collecting residency is not supported.";
#endif
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,234 @@
// Copyright 2014 The Chromium 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 the version of the Android-specific Chromium linker that uses
// the crazy linker to load libraries.
// This source code *cannot* depend on anything from base/ or the C++
// STL, to keep the final library small, and avoid ugly dependency issues.
#include "base/android/linker/legacy_linker_jni.h"
#include <crazy_linker.h>
#include <fcntl.h>
#include <jni.h>
#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include "base/android/linker/linker_jni.h"
namespace chromium_android_linker {
namespace {
// The linker uses a single crazy_context_t object created on demand.
// There is no need to protect this against concurrent access, locking
// is already handled on the Java side.
crazy_context_t* GetCrazyContext() {
static crazy_context_t* s_crazy_context = nullptr;
if (!s_crazy_context) {
// Create new context.
s_crazy_context = crazy_context_create();
// Ensure libraries located in the same directory as the linker
// can be loaded before system ones.
crazy_add_search_path_for_address(
reinterpret_cast<void*>(&GetCrazyContext));
}
return s_crazy_context;
}
// A scoped crazy_library_t that automatically closes the handle
// on scope exit, unless Release() has been called.
class ScopedLibrary {
public:
ScopedLibrary() : lib_(nullptr) {}
~ScopedLibrary() {
if (lib_)
crazy_library_close_with_context(lib_, GetCrazyContext());
}
crazy_library_t* Get() { return lib_; }
crazy_library_t** GetPtr() { return &lib_; }
crazy_library_t* Release() {
crazy_library_t* ret = lib_;
lib_ = nullptr;
return ret;
}
private:
crazy_library_t* lib_;
};
// Add a zip archive file path to the context's current search path
// list. Making it possible to load libraries directly from it.
JNI_GENERATOR_EXPORT bool
Java_org_chromium_base_library_1loader_LegacyLinker_nativeAddZipArchivePath(
JNIEnv* env,
jclass clazz,
jstring apk_path_obj) {
String apk_path(env, apk_path_obj);
char search_path[512];
snprintf(search_path, sizeof(search_path), "%s!lib/" CURRENT_ABI "/",
apk_path.c_str());
crazy_add_search_path(search_path);
return true;
}
// Load a library with the chromium linker. This will also call its
// JNI_OnLoad() method, which shall register its methods. Note that
// lazy native method resolution will _not_ work after this, because
// Dalvik uses the system's dlsym() which won't see the new library,
// so explicit registration is mandatory.
//
// |env| is the current JNI environment handle.
// |clazz| is the static class handle for org.chromium.base.Linker,
// and is ignored here.
// |library_name| is the library name (e.g. libfoo.so).
// |load_address| is an explicit load address.
// |lib_info_obj| is a LibInfo handle used to communicate information
// with the Java side.
// Return true on success.
JNI_GENERATOR_EXPORT bool
Java_org_chromium_base_library_1loader_LegacyLinker_nativeLoadLibrary(
JNIEnv* env,
jclass clazz,
jstring lib_name_obj,
jlong load_address,
jobject lib_info_obj) {
String library_name(env, lib_name_obj);
LOG_INFO("Called for %s, at address 0x%llx", library_name.c_str(),
static_cast<unsigned long long>(load_address));
crazy_context_t* context = GetCrazyContext();
if (!IsValidAddress(load_address)) {
LOG_ERROR("Invalid address 0x%llx",
static_cast<unsigned long long>(load_address));
return false;
}
// Set the desired load address (0 means randomize it).
crazy_context_set_load_address(context, static_cast<size_t>(load_address));
ScopedLibrary library;
if (!crazy_library_open(library.GetPtr(), library_name.c_str(), context)) {
return false;
}
crazy_library_info_t info;
if (!crazy_library_get_info(library.Get(), context, &info)) {
LOG_ERROR("Could not get library information for %s: %s",
library_name.c_str(), crazy_context_get_error(context));
return false;
}
// Release library object to keep it alive after the function returns.
library.Release();
s_lib_info_fields.SetLoadInfo(env, lib_info_obj, info.load_address,
info.load_size);
LOG_INFO("Success loading library %s", library_name.c_str());
return true;
}
JNI_GENERATOR_EXPORT jboolean
Java_org_chromium_base_library_1loader_LegacyLinker_nativeCreateSharedRelro(
JNIEnv* env,
jclass clazz,
jstring library_name,
jlong load_address,
jobject lib_info_obj) {
String lib_name(env, library_name);
LOG_INFO("Called for %s", lib_name.c_str());
if (!IsValidAddress(load_address)) {
LOG_ERROR("Invalid address 0x%llx",
static_cast<unsigned long long>(load_address));
return false;
}
ScopedLibrary library;
if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) {
LOG_ERROR("Could not find %s", lib_name.c_str());
return false;
}
crazy_context_t* context = GetCrazyContext();
size_t relro_start = 0;
size_t relro_size = 0;
int relro_fd = -1;
if (!crazy_library_create_shared_relro(
library.Get(), context, static_cast<size_t>(load_address),
&relro_start, &relro_size, &relro_fd)) {
LOG_ERROR("Could not create shared RELRO sharing for %s: %s\n",
lib_name.c_str(), crazy_context_get_error(context));
return false;
}
s_lib_info_fields.SetRelroInfo(env, lib_info_obj, relro_start, relro_size,
relro_fd);
return true;
}
JNI_GENERATOR_EXPORT jboolean
Java_org_chromium_base_library_1loader_LegacyLinker_nativeUseSharedRelro(
JNIEnv* env,
jclass clazz,
jstring library_name,
jobject lib_info_obj) {
String lib_name(env, library_name);
LOG_INFO("Called for %s, lib_info_ref=%p", lib_name.c_str(), lib_info_obj);
ScopedLibrary library;
if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) {
LOG_ERROR("Could not find %s", lib_name.c_str());
return false;
}
crazy_context_t* context = GetCrazyContext();
size_t relro_start = 0;
size_t relro_size = 0;
int relro_fd = -1;
s_lib_info_fields.GetRelroInfo(env, lib_info_obj, &relro_start, &relro_size,
&relro_fd);
LOG_INFO("library=%s relro start=%p size=%p fd=%d", lib_name.c_str(),
(void*)relro_start, (void*)relro_size, relro_fd);
if (!crazy_library_use_shared_relro(library.Get(), context, relro_start,
relro_size, relro_fd)) {
LOG_ERROR("Could not use shared RELRO for %s: %s", lib_name.c_str(),
crazy_context_get_error(context));
return false;
}
LOG_INFO("Library %s using shared RELRO section!", lib_name.c_str());
return true;
}
} // namespace
bool LegacyLinkerJNIInit(JavaVM* vm, JNIEnv* env) {
LOG_INFO("Entering");
// Save JavaVM* handle into linker, so that it can call JNI_OnLoad()
// automatically when loading libraries containing JNI entry points.
crazy_set_java_vm(vm, JNI_VERSION_1_4);
return true;
}
} // namespace chromium_android_linker

View file

@ -0,0 +1,21 @@
// Copyright 2015 The Chromium 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_LINKER_LEGACY_LINKER_JNI_H_
#define BASE_ANDROID_LINKER_LEGACY_LINKER_JNI_H_
#include <jni.h>
namespace chromium_android_linker {
// JNI_OnLoad() initialization hook for the legacy linker.
// Sets up JNI and other initializations for native linker code.
// |vm| is the Java VM handle passed to JNI_OnLoad().
// |env| is the current JNI environment handle.
// On success, returns true.
extern bool LegacyLinkerJNIInit(JavaVM* vm, JNIEnv* env);
} // namespace chromium_android_linker
#endif // BASE_ANDROID_LINKER_LEGACY_LINKER_JNI_H_

View file

@ -0,0 +1,150 @@
// Copyright 2015 The Chromium 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 the Android-specific Chromium linker, a tiny shared library
// implementing a custom dynamic linker that can be used to load the
// real Chromium libraries.
// The main point of this linker is to be able to share the RELRO
// section of libchrome.so (or equivalent) between renderer processes.
// This source code *cannot* depend on anything from base/ or the C++
// STL, to keep the final library small, and avoid ugly dependency issues.
#include "base/android/linker/linker_jni.h"
#include <jni.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include "base/android/linker/legacy_linker_jni.h"
#include "base/android/linker/modern_linker_jni.h"
namespace chromium_android_linker {
// Variable containing LibInfo for the loaded library.
LibInfo_class s_lib_info_fields;
// Simple scoped UTF String class constructor.
String::String(JNIEnv* env, jstring str) {
size_ = env->GetStringUTFLength(str);
ptr_ = static_cast<char*>(::malloc(size_ + 1));
// Note: This runs before browser native code is loaded, and so cannot
// rely on anything from base/. This means that we must use
// GetStringUTFChars() and not base::android::ConvertJavaStringToUTF8().
//
// GetStringUTFChars() suffices because the only strings used here are
// paths to APK files or names of shared libraries, all of which are
// plain ASCII, defined and hard-coded by the Chromium Android build.
//
// For more: see
// https://crbug.com/508876
//
// Note: GetStringUTFChars() returns Java UTF-8 bytes. This is good
// enough for the linker though.
const char* bytes = env->GetStringUTFChars(str, nullptr);
::memcpy(ptr_, bytes, size_);
ptr_[size_] = '\0';
env->ReleaseStringUTFChars(str, bytes);
}
// Find the jclass JNI reference corresponding to a given |class_name|.
// |env| is the current JNI environment handle.
// On success, return true and set |*clazz|.
bool InitClassReference(JNIEnv* env, const char* class_name, jclass* clazz) {
*clazz = env->FindClass(class_name);
if (!*clazz) {
LOG_ERROR("Could not find class for %s", class_name);
return false;
}
return true;
}
// Initialize a jfieldID corresponding to the field of a given |clazz|,
// with name |field_name| and signature |field_sig|.
// |env| is the current JNI environment handle.
// On success, return true and set |*field_id|.
bool InitFieldId(JNIEnv* env,
jclass clazz,
const char* field_name,
const char* field_sig,
jfieldID* field_id) {
*field_id = env->GetFieldID(clazz, field_name, field_sig);
if (!*field_id) {
LOG_ERROR("Could not find ID for field '%s'", field_name);
return false;
}
LOG_INFO("Found ID %p for field '%s'", *field_id, field_name);
return true;
}
// Use Android ASLR to create a random address into which we expect to be
// able to load libraries. Note that this is probabilistic; we unmap the
// address we get from mmap and assume we can re-map into it later. This
// works the majority of the time. If it doesn't, client code backs out and
// then loads the library normally at any available address.
// |env| is the current JNI environment handle, and |clazz| a class.
// Returns the address selected by ASLR, or 0 on error.
JNI_GENERATOR_EXPORT jlong
Java_org_chromium_base_library_1loader_Linker_nativeGetRandomBaseLoadAddress(
JNIEnv* env,
jclass clazz) {
size_t bytes = kAddressSpaceReservationSize;
void* address =
mmap(nullptr, bytes, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (address == MAP_FAILED) {
LOG_INFO("Random base load address not determinable");
return 0;
}
munmap(address, bytes);
LOG_INFO("Random base load address is %p", address);
return static_cast<jlong>(reinterpret_cast<uintptr_t>(address));
}
namespace {
// JNI_OnLoad() initialization hook.
bool LinkerJNIInit(JavaVM* vm, JNIEnv* env) {
// Find LibInfo field ids.
LOG_INFO("Caching field IDs");
if (!s_lib_info_fields.Init(env)) {
return false;
}
return true;
}
// JNI_OnLoad() hook called when the linker library is loaded through
// the regular System.LoadLibrary) API. This shall save the Java VM
// handle and initialize LibInfo fields.
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
LOG_INFO("Entering");
// Get new JNIEnv
JNIEnv* env;
if (JNI_OK != vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4)) {
LOG_ERROR("Could not create JNIEnv");
return -1;
}
// Initialize linker base and implementations.
if (!LinkerJNIInit(vm, env) || !LegacyLinkerJNIInit(vm, env) ||
!ModernLinkerJNIInit(vm, env)) {
return -1;
}
LOG_INFO("Done");
return JNI_VERSION_1_4;
}
} // namespace
} // namespace chromium_android_linker
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
return chromium_android_linker::JNI_OnLoad(vm, reserved);
}

View file

@ -0,0 +1,202 @@
// Copyright 2015 The Chromium 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 the Android-specific Chromium linker, a tiny shared library
// implementing a custom dynamic linker that can be used to load the
// real Chromium libraries.
// The main point of this linker is to be able to share the RELRO
// section of libcontentshell.so (or equivalent) between the browser and
// renderer process.
// This source code *cannot* depend on anything from base/ or the C++
// STL, to keep the final library small, and avoid ugly dependency issues.
#ifndef BASE_ANDROID_LINKER_LINKER_JNI_H_
#define BASE_ANDROID_LINKER_LINKER_JNI_H_
#include <android/log.h>
#include <jni.h>
#include <stddef.h>
#include <stdlib.h>
#include "build/build_config.h"
// Set this to 1 to enable debug traces to the Android log.
// Note that LOG() from "base/logging.h" cannot be used, since it is
// in base/ which hasn't been loaded yet.
#define DEBUG 0
#define TAG "cr_ChromiumAndroidLinker"
#if DEBUG
#define LOG_INFO(FORMAT, ...) \
__android_log_print(ANDROID_LOG_INFO, TAG, "%s: " FORMAT, __FUNCTION__, \
##__VA_ARGS__)
#else
#define LOG_INFO(FORMAT, ...) ((void)0)
#endif
#define LOG_ERROR(FORMAT, ...) \
__android_log_print(ANDROID_LOG_ERROR, TAG, "%s: " FORMAT, __FUNCTION__, \
##__VA_ARGS__)
#define UNUSED __attribute__((unused))
#if defined(ARCH_CPU_X86)
// Dalvik JIT generated code doesn't guarantee 16-byte stack alignment on
// x86 - use force_align_arg_pointer to realign the stack at the JNI
// boundary. https://crbug.com/655248
#define JNI_GENERATOR_EXPORT \
extern "C" __attribute__((visibility("default"), force_align_arg_pointer))
#else
#define JNI_GENERATOR_EXPORT extern "C" __attribute__((visibility("default")))
#endif
#if defined(__arm__) && defined(__ARM_ARCH_7A__)
#define CURRENT_ABI "armeabi-v7a"
#elif defined(__arm__)
#define CURRENT_ABI "armeabi"
#elif defined(__i386__)
#define CURRENT_ABI "x86"
#elif defined(__mips__)
#define CURRENT_ABI "mips"
#elif defined(__x86_64__)
#define CURRENT_ABI "x86_64"
#elif defined(__aarch64__)
#define CURRENT_ABI "arm64-v8a"
#else
#error "Unsupported target abi"
#endif
namespace chromium_android_linker {
// Larger than the largest library we might attempt to load.
static const size_t kAddressSpaceReservationSize = 192 * 1024 * 1024;
// A simple scoped UTF String class that can be initialized from
// a Java jstring handle. Modeled like std::string, which cannot
// be used here.
class String {
public:
String(JNIEnv* env, jstring str);
inline ~String() { ::free(ptr_); }
inline const char* c_str() const { return ptr_ ? ptr_ : ""; }
inline size_t size() const { return size_; }
private:
char* ptr_;
size_t size_;
};
// Return true iff |address| is a valid address for the target CPU.
inline bool IsValidAddress(jlong address) {
return static_cast<jlong>(static_cast<size_t>(address)) == address;
}
// Find the jclass JNI reference corresponding to a given |class_name|.
// |env| is the current JNI environment handle.
// On success, return true and set |*clazz|.
extern bool InitClassReference(JNIEnv* env,
const char* class_name,
jclass* clazz);
// Initialize a jfieldID corresponding to the field of a given |clazz|,
// with name |field_name| and signature |field_sig|.
// |env| is the current JNI environment handle.
// On success, return true and set |*field_id|.
extern bool InitFieldId(JNIEnv* env,
jclass clazz,
const char* field_name,
const char* field_sig,
jfieldID* field_id);
// Initialize a jfieldID corresponding to the static field of a given |clazz|,
// with name |field_name| and signature |field_sig|.
// |env| is the current JNI environment handle.
// On success, return true and set |*field_id|.
extern bool InitStaticFieldId(JNIEnv* env,
jclass clazz,
const char* field_name,
const char* field_sig,
jfieldID* field_id);
// Use Android ASLR to create a random library load address.
// |env| is the current JNI environment handle, and |clazz| a class.
// Returns the address selected by ASLR.
extern jlong GetRandomBaseLoadAddress(JNIEnv* env, jclass clazz);
// A class used to model the field IDs of the org.chromium.base.Linker
// LibInfo inner class, used to communicate data with the Java side
// of the linker.
struct LibInfo_class {
jfieldID load_address_id;
jfieldID load_size_id;
jfieldID relro_start_id;
jfieldID relro_size_id;
jfieldID relro_fd_id;
// Initialize an instance.
bool Init(JNIEnv* env) {
jclass clazz;
if (!InitClassReference(
env, "org/chromium/base/library_loader/Linker$LibInfo", &clazz)) {
return false;
}
return InitFieldId(env, clazz, "mLoadAddress", "J", &load_address_id) &&
InitFieldId(env, clazz, "mLoadSize", "J", &load_size_id) &&
InitFieldId(env, clazz, "mRelroStart", "J", &relro_start_id) &&
InitFieldId(env, clazz, "mRelroSize", "J", &relro_size_id) &&
InitFieldId(env, clazz, "mRelroFd", "I", &relro_fd_id);
}
void SetLoadInfo(JNIEnv* env,
jobject library_info_obj,
size_t load_address,
size_t load_size) {
env->SetLongField(library_info_obj, load_address_id, load_address);
env->SetLongField(library_info_obj, load_size_id, load_size);
}
void SetRelroInfo(JNIEnv* env,
jobject library_info_obj,
size_t relro_start,
size_t relro_size,
int relro_fd) {
env->SetLongField(library_info_obj, relro_start_id, relro_start);
env->SetLongField(library_info_obj, relro_size_id, relro_size);
env->SetIntField(library_info_obj, relro_fd_id, relro_fd);
}
// Use this instance to convert a RelroInfo reference into
// a crazy_library_info_t.
void GetRelroInfo(JNIEnv* env,
jobject library_info_obj,
size_t* relro_start,
size_t* relro_size,
int* relro_fd) {
if (relro_start) {
*relro_start = static_cast<size_t>(
env->GetLongField(library_info_obj, relro_start_id));
}
if (relro_size) {
*relro_size = static_cast<size_t>(
env->GetLongField(library_info_obj, relro_size_id));
}
if (relro_fd) {
*relro_fd = env->GetIntField(library_info_obj, relro_fd_id);
}
}
};
// Variable containing LibInfo for the loaded library.
extern LibInfo_class s_lib_info_fields;
} // namespace chromium_android_linker
#endif // BASE_ANDROID_LINKER_LINKER_JNI_H_

View file

@ -0,0 +1,582 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Uses android_dlopen_ext() to share relocations.
// This source code *cannot* depend on anything from base/ or the C++
// STL, to keep the final library small, and avoid ugly dependency issues.
#include "base/android/linker/modern_linker_jni.h"
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <jni.h>
#include <limits.h>
#include <link.h>
#include <stddef.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <limits>
#include <memory>
#include <android/dlext.h>
#include "base/android/linker/linker_jni.h"
// From //base/posix/eintr_wrapper.h, but we don't want to depend on //base.
#define HANDLE_EINTR(x) \
({ \
decltype(x) eintr_wrapper_result; \
do { \
eintr_wrapper_result = (x); \
} while (eintr_wrapper_result == -1 && errno == EINTR); \
eintr_wrapper_result; \
})
// Not defined on all platforms. As this linker is only supported on ARM32/64,
// x86/x86_64 and MIPS, page size is always 4k.
#if !defined(PAGE_SIZE)
#define PAGE_SIZE (1 << 12)
#define PAGE_MASK (~(PAGE_SIZE - 1))
#endif
#define PAGE_START(x) ((x)&PAGE_MASK)
#define PAGE_END(x) PAGE_START((x) + (PAGE_SIZE - 1))
extern "C" {
// <android/dlext.h> does not declare android_dlopen_ext() if __ANDROID_API__
// is smaller than 21, so declare it here as a weak function. This will allow
// detecting its availability at runtime. For API level 21 or higher, the
// attribute is ignored due to the previous declaration.
void* android_dlopen_ext(const char*, int, const android_dlextinfo*)
__attribute__((weak_import));
// This function is exported by the dynamic linker but never declared in any
// official header for some architecture/version combinations.
int dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data),
void* data) __attribute__((weak_import));
} // extern "C"
namespace chromium_android_linker {
namespace {
// Record of the Java VM passed to JNI_OnLoad().
static JavaVM* s_java_vm = nullptr;
// Helper class for anonymous memory mapping.
class ScopedAnonymousMmap {
public:
static ScopedAnonymousMmap ReserveAtAddress(void* address, size_t size);
~ScopedAnonymousMmap() {
if (addr_ && owned_)
munmap(addr_, size_);
}
ScopedAnonymousMmap(ScopedAnonymousMmap&& o) {
addr_ = o.addr_;
size_ = o.size_;
owned_ = o.owned_;
o.Release();
}
void* address() const { return addr_; }
size_t size() const { return size_; }
void Release() { owned_ = false; }
private:
ScopedAnonymousMmap() = default;
ScopedAnonymousMmap(void* addr, size_t size) : addr_(addr), size_(size) {}
private:
bool owned_ = true;
void* addr_ = nullptr;
size_t size_ = 0;
// Move only.
ScopedAnonymousMmap(const ScopedAnonymousMmap&) = delete;
ScopedAnonymousMmap& operator=(const ScopedAnonymousMmap&) = delete;
};
// Reserves an address space range, starting at |address|.
// If successful, returns a valid mapping, otherwise returns an empty one.
ScopedAnonymousMmap ScopedAnonymousMmap::ReserveAtAddress(void* address,
size_t size) {
void* actual_address =
mmap(address, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (actual_address == MAP_FAILED) {
LOG_INFO("mmap failed: %s", strerror(errno));
return {};
}
if (actual_address && actual_address != address) {
LOG_ERROR("Failed to obtain fixed address for load");
return {};
}
return {actual_address, size};
}
// Starting with API level 26, the following functions from
// libandroid.so should be used to create shared memory regions.
//
// This ensures compatibility with post-Q versions of Android that may not rely
// on ashmem for shared memory.
//
// This is heavily inspired from //third_party/ashmem/ashmem-dev.c, which we
// cannot reference directly to avoid increasing binary size. Also, we don't
// need to support API level <26.
//
// *Not* threadsafe.
struct SharedMemoryFunctions {
SharedMemoryFunctions() {
library_handle = dlopen("libandroid.so", RTLD_NOW);
create = reinterpret_cast<CreateFunction>(
dlsym(library_handle, "ASharedMemory_create"));
set_protection = reinterpret_cast<SetProtectionFunction>(
dlsym(library_handle, "ASharedMemory_setProt"));
if (!create || !set_protection)
LOG_ERROR("Cannot get the shared memory functions from libandroid");
}
~SharedMemoryFunctions() {
if (library_handle)
dlclose(library_handle);
}
typedef int (*CreateFunction)(const char*, size_t);
typedef int (*SetProtectionFunction)(int fd, int prot);
CreateFunction create;
SetProtectionFunction set_protection;
void* library_handle;
};
// Metadata about a library loaded at a given |address|.
struct LoadedLibraryMetadata {
explicit LoadedLibraryMetadata(void* address)
: load_address(address),
load_size(0),
min_vaddr(0),
relro_start(0),
relro_size(0) {}
const void* load_address;
size_t load_size;
size_t min_vaddr;
size_t relro_start;
size_t relro_size;
};
// android_dlopen_ext() wrapper.
// Returns false if no android_dlopen_ext() is available, otherwise true with
// the return value from android_dlopen_ext() in |status|.
bool AndroidDlopenExt(const char* filename,
int flag,
const android_dlextinfo& extinfo,
void** status) {
if (!android_dlopen_ext) {
LOG_ERROR("android_dlopen_ext is not found");
return false;
}
LOG_INFO(
"android_dlopen_ext:"
" flags=0x%llx, reserved_addr=%p, reserved_size=%d",
static_cast<long long>(extinfo.flags), extinfo.reserved_addr,
static_cast<int>(extinfo.reserved_size));
*status = android_dlopen_ext(filename, flag, &extinfo);
return true;
}
// Callback for dl_iterate_phdr(). Read phdrs to identify whether or not
// this library's load address matches the |load_address| passed in
// |data|. If yes, fills the metadata we care about in |data|.
//
// A non-zero return value terminates iteration.
int FindLoadedLibraryMetadata(dl_phdr_info* info,
size_t size UNUSED,
void* data) {
auto* metadata = reinterpret_cast<LoadedLibraryMetadata*>(data);
// Use max and min vaddr to compute the library's load size.
auto min_vaddr = std::numeric_limits<ElfW(Addr)>::max();
ElfW(Addr) max_vaddr = 0;
ElfW(Addr) min_relro_vaddr = ~0;
ElfW(Addr) max_relro_vaddr = 0;
bool is_matching = false;
for (int i = 0; i < info->dlpi_phnum; ++i) {
const ElfW(Phdr)* phdr = &info->dlpi_phdr[i];
switch (phdr->p_type) {
case PT_LOAD: {
// See if this segment's load address matches what we passed to
// android_dlopen_ext as extinfo.reserved_addr.
//
// Here and below, the virtual address in memory is computed by
// address == info->dlpi_addr + program_header->p_vaddr
// that is, the p_vaddr fields is relative to the object base address.
// See dl_iterate_phdr(3) for details.
void* load_addr =
reinterpret_cast<void*>(info->dlpi_addr + phdr->p_vaddr);
// Matching is based on the load address, since we have no idea
// where the relro segment is.
if (load_addr == metadata->load_address)
is_matching = true;
if (phdr->p_vaddr < min_vaddr)
min_vaddr = phdr->p_vaddr;
if (phdr->p_vaddr + phdr->p_memsz > max_vaddr)
max_vaddr = phdr->p_vaddr + phdr->p_memsz;
} break;
case PT_GNU_RELRO:
min_relro_vaddr = PAGE_START(phdr->p_vaddr);
max_relro_vaddr = phdr->p_vaddr + phdr->p_memsz;
break;
default:
break;
}
}
// If this library matches what we seek, return its load size.
if (is_matching) {
int page_size = sysconf(_SC_PAGESIZE);
if (page_size != PAGE_SIZE)
abort();
metadata->load_size = PAGE_END(max_vaddr) - PAGE_START(min_vaddr);
metadata->min_vaddr = min_vaddr;
metadata->relro_size =
PAGE_END(max_relro_vaddr) - PAGE_START(min_relro_vaddr);
metadata->relro_start = info->dlpi_addr + PAGE_START(min_relro_vaddr);
return true;
}
return false;
}
// Creates an android_dlextinfo struct so that a library is loaded inside the
// space referenced by |mapping|.
std::unique_ptr<android_dlextinfo> MakeAndroidDlextinfo(
const ScopedAnonymousMmap& mapping) {
auto info = std::make_unique<android_dlextinfo>();
memset(info.get(), 0, sizeof(*info));
info->flags = ANDROID_DLEXT_RESERVED_ADDRESS;
info->reserved_addr = mapping.address();
info->reserved_size = mapping.size();
return info;
}
// Copies the current relocations into a shared-memory file, and uses this file
// as the relocations.
//
// Returns true for success, and populate |fd| with the relocations's fd in this
// case.
bool CopyAndRemapRelocations(const LoadedLibraryMetadata& metadata, int* fd) {
LOG_INFO("Entering");
void* relro_addr = reinterpret_cast<void*>(metadata.relro_start);
SharedMemoryFunctions fns;
if (!fns.create)
return false;
int shared_mem_fd = fns.create("cr_relro", metadata.relro_size);
if (shared_mem_fd == -1) {
LOG_ERROR("Cannot create the shared memory file");
return false;
}
int rw_flags = PROT_READ | PROT_WRITE;
fns.set_protection(shared_mem_fd, rw_flags);
void* relro_copy_addr = mmap(nullptr, metadata.relro_size, rw_flags,
MAP_SHARED, shared_mem_fd, 0);
if (relro_copy_addr == MAP_FAILED) {
LOG_ERROR("Cannot mmap() space for the copy");
close(shared_mem_fd);
return false;
}
memcpy(relro_copy_addr, relro_addr, metadata.relro_size);
int retval = mprotect(relro_copy_addr, metadata.relro_size, PROT_READ);
if (retval) {
LOG_ERROR("Cannot call mprotect()");
close(shared_mem_fd);
munmap(relro_copy_addr, metadata.relro_size);
return false;
}
void* new_addr =
mremap(relro_copy_addr, metadata.relro_size, metadata.relro_size,
MREMAP_MAYMOVE | MREMAP_FIXED, relro_addr);
if (new_addr != relro_addr) {
LOG_ERROR("mremap() error");
close(shared_mem_fd);
munmap(relro_copy_addr, metadata.relro_size);
return false;
}
*fd = shared_mem_fd;
return true;
}
// Gathers metadata about the library loaded at |addr|.
//
// Returns true for success.
bool GetLoadedLibraryMetadata(LoadedLibraryMetadata* metadata) {
LOG_INFO("Called for %p", metadata->load_address);
if (!dl_iterate_phdr) {
LOG_ERROR("No dl_iterate_phdr() found");
return false;
}
int status = dl_iterate_phdr(&FindLoadedLibraryMetadata, metadata);
if (!status) {
LOG_ERROR("Failed to find library at address %p", metadata->load_address);
return false;
}
LOG_INFO("Relro start address = %p, size = %d",
reinterpret_cast<void*>(metadata->relro_start),
static_cast<int>(metadata->relro_size));
return true;
}
// Resizes the address space reservation to the actual required size.
// Failure here is only a warning, as at worst this wastes virtual address
// space, not actual memory.
void ResizeMapping(const ScopedAnonymousMmap& mapping,
const LoadedLibraryMetadata& metadata) {
// Trim the reservation mapping to match the library's actual size. Failure
// to resize is not a fatal error. At worst we lose a portion of virtual
// address space that we might otherwise have recovered. Note that trimming
// the mapping here requires that we have already released the scoped
// mapping.
const uintptr_t uintptr_addr = reinterpret_cast<uintptr_t>(mapping.address());
if (mapping.size() > metadata.load_size) {
// Unmap the part of the reserved address space that is beyond the end of
// the loaded library data.
void* unmap = reinterpret_cast<void*>(uintptr_addr + metadata.load_size);
const size_t length = mapping.size() - metadata.load_size;
if (munmap(unmap, length) == -1) {
LOG_ERROR("WARNING: unmap of %d bytes at %p failed: %s",
static_cast<int>(length), unmap, strerror(errno));
}
} else {
LOG_ERROR("WARNING: library reservation was too small");
}
}
// Calls JNI_OnLoad() in the library referenced by |handle|.
// Returns true for success.
bool CallJniOnLoad(void* handle) {
LOG_INFO("Entering");
// Locate and if found then call the loaded library's JNI_OnLoad() function.
using JNI_OnLoadFunctionPtr = int (*)(void* vm, void* reserved);
auto jni_onload =
reinterpret_cast<JNI_OnLoadFunctionPtr>(dlsym(handle, "JNI_OnLoad"));
if (jni_onload != nullptr) {
// Check that JNI_OnLoad returns a usable JNI version.
int jni_version = (*jni_onload)(s_java_vm, nullptr);
if (jni_version < JNI_VERSION_1_4) {
LOG_ERROR("JNI version is invalid: %d", jni_version);
return false;
}
}
return true;
}
// Load the library at |path| at address |wanted_address| if possible, and
// creates a file with relro at |relocations_path|.
//
// In case of success, returns a readonly file descriptor to the relocations,
// otherwise returns -1.
int LoadCreateSharedRelocations(const String& path, void* wanted_address) {
LOG_INFO("Entering");
ScopedAnonymousMmap mapping = ScopedAnonymousMmap::ReserveAtAddress(
wanted_address, kAddressSpaceReservationSize);
if (!mapping.address())
return -1;
std::unique_ptr<android_dlextinfo> dlextinfo = MakeAndroidDlextinfo(mapping);
void* handle = nullptr;
if (!AndroidDlopenExt(path.c_str(), RTLD_NOW, *dlextinfo, &handle)) {
LOG_ERROR("android_dlopen_ext() error");
return -1;
}
if (handle == nullptr) {
LOG_ERROR("android_dlopen_ext: %s", dlerror());
return -1;
}
mapping.Release();
LoadedLibraryMetadata metadata{mapping.address()};
bool ok = GetLoadedLibraryMetadata(&metadata);
int relro_fd = -1;
if (ok) {
ResizeMapping(mapping, metadata);
CopyAndRemapRelocations(metadata, &relro_fd);
}
if (!CallJniOnLoad(handle))
return false;
return relro_fd;
}
// Load the library at |path| at address |wanted_address| if possible, and
// uses the relocations in |relocations_fd| if possible.
bool LoadUseSharedRelocations(const String& path,
void* wanted_address,
int relocations_fd) {
LOG_INFO("Entering");
ScopedAnonymousMmap mapping = ScopedAnonymousMmap::ReserveAtAddress(
wanted_address, kAddressSpaceReservationSize);
if (!mapping.address())
return false;
std::unique_ptr<android_dlextinfo> dlextinfo = MakeAndroidDlextinfo(mapping);
void* handle = nullptr;
if (!AndroidDlopenExt(path.c_str(), RTLD_NOW, *dlextinfo, &handle)) {
LOG_ERROR("No android_dlopen_ext function found");
return false;
}
if (handle == nullptr) {
LOG_ERROR("android_dlopen_ext: %s", dlerror());
return false;
}
mapping.Release();
LoadedLibraryMetadata metadata{mapping.address()};
bool ok = GetLoadedLibraryMetadata(&metadata);
if (!ok) {
LOG_ERROR("Cannot get library's metadata");
return false;
}
ResizeMapping(mapping, metadata);
void* shared_relro_mapping_address = mmap(
nullptr, metadata.relro_size, PROT_READ, MAP_SHARED, relocations_fd, 0);
if (shared_relro_mapping_address == MAP_FAILED) {
LOG_ERROR("Cannot map the relocations");
return false;
}
void* current_relro_address = reinterpret_cast<void*>(metadata.relro_start);
int retval = memcmp(shared_relro_mapping_address, current_relro_address,
metadata.relro_size);
if (!retval) {
void* new_addr = mremap(shared_relro_mapping_address, metadata.relro_size,
metadata.relro_size, MREMAP_MAYMOVE | MREMAP_FIXED,
current_relro_address);
if (new_addr != current_relro_address) {
LOG_ERROR("Cannot call mremap()");
munmap(shared_relro_mapping_address, metadata.relro_size);
return false;
}
} else {
munmap(shared_relro_mapping_address, metadata.relro_size);
LOG_ERROR("Relocations are not identical, giving up.");
}
if (!CallJniOnLoad(handle))
return false;
return true;
}
bool LoadNoSharedRelocations(const String& path) {
void* handle = dlopen(path.c_str(), RTLD_NOW);
if (!handle) {
LOG_ERROR("dlopen: %s", dlerror());
return false;
}
if (!CallJniOnLoad(handle))
return false;
return true;
}
} // namespace
JNI_GENERATOR_EXPORT jboolean
Java_org_chromium_base_library_1loader_ModernLinker_nativeLoadLibraryCreateRelros(
JNIEnv* env,
jclass clazz,
jstring jdlopen_ext_path,
jlong load_address,
jobject lib_info_obj) {
LOG_INFO("Entering");
String library_path(env, jdlopen_ext_path);
if (!IsValidAddress(load_address)) {
LOG_ERROR("Invalid address 0x%llx", static_cast<long long>(load_address));
return false;
}
void* address = reinterpret_cast<void*>(load_address);
int fd = LoadCreateSharedRelocations(library_path, address);
if (fd == -1)
return false;
// Note the shared RELRO fd in the supplied libinfo object. In this
// implementation the RELRO start is set to the library's load address,
// and the RELRO size is unused.
const size_t cast_addr = reinterpret_cast<size_t>(address);
s_lib_info_fields.SetRelroInfo(env, lib_info_obj, cast_addr, 0, fd);
return true;
}
JNI_GENERATOR_EXPORT jboolean
Java_org_chromium_base_library_1loader_ModernLinker_nativeLoadLibraryUseRelros(
JNIEnv* env,
jclass clazz,
jstring jdlopen_ext_path,
jlong load_address,
jint relro_fd) {
LOG_INFO("Entering");
String library_path(env, jdlopen_ext_path);
if (!IsValidAddress(load_address)) {
LOG_ERROR("Invalid address 0x%llx", static_cast<long long>(load_address));
return false;
}
void* address = reinterpret_cast<void*>(load_address);
return LoadUseSharedRelocations(library_path, address, relro_fd);
}
JNI_GENERATOR_EXPORT jboolean
Java_org_chromium_base_library_1loader_ModernLinker_nativeLoadLibraryNoRelros(
JNIEnv* env,
jclass clazz,
jstring jdlopen_ext_path) {
String library_path(env, jdlopen_ext_path);
return LoadNoSharedRelocations(library_path);
}
bool ModernLinkerJNIInit(JavaVM* vm, JNIEnv* env) {
s_java_vm = vm;
return true;
}
} // namespace chromium_android_linker

View file

@ -0,0 +1,21 @@
// Copyright 2019 The Chromium 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_LINKER_MODERN_LINKER_JNI_H_
#define BASE_ANDROID_LINKER_MODERN_LINKER_JNI_H_
#include <jni.h>
namespace chromium_android_linker {
// JNI_OnLoad() initialization hook for the modern linker.
// Sets up JNI and other initializations for native linker code.
// |vm| is the Java VM handle passed to JNI_OnLoad().
// |env| is the current JNI environment handle.
// On success, returns true.
extern bool ModernLinkerJNIInit(JavaVM* vm, JNIEnv* env);
} // namespace chromium_android_linker
#endif // BASE_ANDROID_LINKER_MODERN_LINKER_JNI_H_

View file

@ -0,0 +1,34 @@
// Copyright 2014 The Chromium 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/locale_utils.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/base_jni_headers/LocaleUtils_jni.h"
namespace base {
namespace android {
std::string GetDefaultCountryCode() {
JNIEnv* env = base::android::AttachCurrentThread();
return ConvertJavaStringToUTF8(Java_LocaleUtils_getDefaultCountryCode(env));
}
std::string GetDefaultLocaleString() {
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jstring> locale =
Java_LocaleUtils_getDefaultLocaleString(env);
return ConvertJavaStringToUTF8(locale);
}
std::string GetDefaultLocaleListString() {
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jstring> locales =
Java_LocaleUtils_getDefaultLocaleListString(env);
return ConvertJavaStringToUTF8(locales);
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,29 @@
// Copyright 2014 The Chromium 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_LOCALE_UTILS_H_
#define BASE_ANDROID_LOCALE_UTILS_H_
#include <jni.h>
#include <string>
#include "base/base_export.h"
namespace base {
namespace android {
BASE_EXPORT std::string GetDefaultCountryCode();
// Return the current default locale of the device as string.
BASE_EXPORT std::string GetDefaultLocaleString();
// Returns a list of user-selected locales as a comma separated string, ordered
// by decreasing preference.
BASE_EXPORT std::string GetDefaultLocaleListString();
} // namespace android
} // namespace base
#endif // BASE_ANDROID_LOCALE_UTILS_H_

View file

@ -0,0 +1,29 @@
// 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/base_jni_headers/MemoryPressureListener_jni.h"
#include "base/memory/memory_pressure_listener.h"
using base::android::JavaParamRef;
// Defined and called by JNI.
static void JNI_MemoryPressureListener_OnMemoryPressure(
JNIEnv* env,
jint memory_pressure_level) {
base::MemoryPressureListener::NotifyMemoryPressure(
static_cast<base::MemoryPressureListener::MemoryPressureLevel>(
memory_pressure_level));
}
namespace base {
namespace android {
void MemoryPressureListenerAndroid::Initialize(JNIEnv* env) {
Java_MemoryPressureListener_addNativeCallback(env);
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,29 @@
// 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"
#include "base/macros.h"
namespace base {
namespace android {
// Implements the C++ counter part of MemoryPressureListener.java
class BASE_EXPORT MemoryPressureListenerAndroid {
public:
static void Initialize(JNIEnv* env);
// Called by JNI.
static void OnMemoryPressure(int memory_pressure_type);
private:
DISALLOW_COPY_AND_ASSIGN(MemoryPressureListenerAndroid);
};
} // namespace android
} // namespace base
#endif // BASE_ANDROID_MEMORY_PRESSURE_LISTENER_ANDROID_H_

View file

@ -0,0 +1,221 @@
// Copyright 2019 The Chromium 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/android/jni_string.h"
#include "base/base_jni_headers/NativeUmaRecorder_jni.h"
#include "base/lazy_instance.h"
#include "base/macros.h"
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_base.h"
#include "base/metrics/sparse_histogram.h"
#include "base/metrics/user_metrics.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
#include "base/time/time.h"
namespace base {
namespace android {
namespace {
// Simple thread-safe wrapper for caching histograms. This avoids
// relatively expensive JNI string translation for each recording.
class HistogramCache {
public:
HistogramCache() {}
std::string HistogramConstructionParamsToString(HistogramBase* histogram) {
std::string params_str = histogram->histogram_name();
switch (histogram->GetHistogramType()) {
case HISTOGRAM:
case LINEAR_HISTOGRAM:
case BOOLEAN_HISTOGRAM:
case CUSTOM_HISTOGRAM: {
Histogram* hist = static_cast<Histogram*>(histogram);
params_str += StringPrintf("/%d/%d/%d", hist->declared_min(),
hist->declared_max(), hist->bucket_count());
break;
}
case SPARSE_HISTOGRAM:
case DUMMY_HISTOGRAM:
break;
}
return params_str;
}
void CheckHistogramArgs(JNIEnv* env,
jstring j_histogram_name,
int32_t expected_min,
int32_t expected_max,
uint32_t expected_bucket_count,
HistogramBase* histogram) {
std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
bool valid_arguments = Histogram::InspectConstructionArguments(
histogram_name, &expected_min, &expected_max, &expected_bucket_count);
DCHECK(valid_arguments);
DCHECK(histogram->HasConstructionArguments(expected_min, expected_max,
expected_bucket_count))
<< histogram_name << "/" << expected_min << "/" << expected_max << "/"
<< expected_bucket_count << " vs. "
<< HistogramConstructionParamsToString(histogram);
}
HistogramBase* BooleanHistogram(JNIEnv* env,
jstring j_histogram_name,
jlong j_histogram_hint) {
DCHECK(j_histogram_name);
HistogramBase* histogram = HistogramFromHint(j_histogram_hint);
if (histogram)
return histogram;
std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
histogram = BooleanHistogram::FactoryGet(
histogram_name, HistogramBase::kUmaTargetedHistogramFlag);
return histogram;
}
HistogramBase* ExponentialHistogram(JNIEnv* env,
jstring j_histogram_name,
jlong j_histogram_hint,
jint j_min,
jint j_max,
jint j_num_buckets) {
DCHECK(j_histogram_name);
int32_t min = static_cast<int32_t>(j_min);
int32_t max = static_cast<int32_t>(j_max);
int32_t num_buckets = static_cast<int32_t>(j_num_buckets);
HistogramBase* histogram = HistogramFromHint(j_histogram_hint);
if (histogram) {
CheckHistogramArgs(env, j_histogram_name, min, max, num_buckets,
histogram);
return histogram;
}
DCHECK_GE(min, 1) << "The min expected sample must be >= 1";
std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
histogram = Histogram::FactoryGet(histogram_name, min, max, num_buckets,
HistogramBase::kUmaTargetedHistogramFlag);
return histogram;
}
HistogramBase* LinearHistogram(JNIEnv* env,
jstring j_histogram_name,
jlong j_histogram_hint,
jint j_min,
jint j_max,
jint j_num_buckets) {
DCHECK(j_histogram_name);
int32_t min = static_cast<int32_t>(j_min);
int32_t max = static_cast<int32_t>(j_max);
int32_t num_buckets = static_cast<int32_t>(j_num_buckets);
HistogramBase* histogram = HistogramFromHint(j_histogram_hint);
if (histogram) {
CheckHistogramArgs(env, j_histogram_name, min, max, num_buckets,
histogram);
return histogram;
}
std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
histogram =
LinearHistogram::FactoryGet(histogram_name, min, max, num_buckets,
HistogramBase::kUmaTargetedHistogramFlag);
return histogram;
}
HistogramBase* SparseHistogram(JNIEnv* env,
jstring j_histogram_name,
jlong j_histogram_hint) {
DCHECK(j_histogram_name);
HistogramBase* histogram = HistogramFromHint(j_histogram_hint);
if (histogram)
return histogram;
std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name);
histogram = SparseHistogram::FactoryGet(
histogram_name, HistogramBase::kUmaTargetedHistogramFlag);
return histogram;
}
private:
// Convert a jlong |histogram_hint| from Java to a HistogramBase* via a cast.
// The Java side caches these in a map (see NativeUmaRecorder.java), which is
// safe to do since C++ Histogram objects are never freed.
static HistogramBase* HistogramFromHint(jlong j_histogram_hint) {
return reinterpret_cast<HistogramBase*>(j_histogram_hint);
}
DISALLOW_COPY_AND_ASSIGN(HistogramCache);
};
LazyInstance<HistogramCache>::Leaky g_histograms;
} // namespace
jlong JNI_NativeUmaRecorder_RecordBooleanHistogram(
JNIEnv* env,
const JavaParamRef<jstring>& j_histogram_name,
jlong j_histogram_hint,
jboolean j_sample) {
bool sample = static_cast<bool>(j_sample);
HistogramBase* histogram = g_histograms.Get().BooleanHistogram(
env, j_histogram_name, j_histogram_hint);
histogram->AddBoolean(sample);
return reinterpret_cast<jlong>(histogram);
}
jlong JNI_NativeUmaRecorder_RecordExponentialHistogram(
JNIEnv* env,
const JavaParamRef<jstring>& j_histogram_name,
jlong j_histogram_hint,
jint j_sample,
jint j_min,
jint j_max,
jint j_num_buckets) {
int sample = static_cast<int>(j_sample);
HistogramBase* histogram = g_histograms.Get().ExponentialHistogram(
env, j_histogram_name, j_histogram_hint, j_min, j_max, j_num_buckets);
histogram->Add(sample);
return reinterpret_cast<jlong>(histogram);
}
jlong JNI_NativeUmaRecorder_RecordLinearHistogram(
JNIEnv* env,
const JavaParamRef<jstring>& j_histogram_name,
jlong j_histogram_hint,
jint j_sample,
jint j_min,
jint j_max,
jint j_num_buckets) {
int sample = static_cast<int>(j_sample);
HistogramBase* histogram = g_histograms.Get().LinearHistogram(
env, j_histogram_name, j_histogram_hint, j_min, j_max, j_num_buckets);
histogram->Add(sample);
return reinterpret_cast<jlong>(histogram);
}
jlong JNI_NativeUmaRecorder_RecordSparseHistogram(
JNIEnv* env,
const JavaParamRef<jstring>& j_histogram_name,
jlong j_histogram_hint,
jint j_sample) {
int sample = static_cast<int>(j_sample);
HistogramBase* histogram = g_histograms.Get().SparseHistogram(
env, j_histogram_name, j_histogram_hint);
histogram->Add(sample);
return reinterpret_cast<jlong>(histogram);
}
void JNI_NativeUmaRecorder_RecordUserAction(
JNIEnv* env,
const JavaParamRef<jstring>& j_user_action_name,
jlong j_millis_since_event) {
// Time values coming from Java need to be synchronized with TimeTick clock.
RecordComputedActionSince(ConvertJavaStringToUTF8(env, j_user_action_name),
TimeDelta::FromMilliseconds(j_millis_since_event));
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,2 @@
lizeb@chromium.org
pasko@chromium.org

View file

@ -0,0 +1,419 @@
// Copyright (c) 2019 The Chromium 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/orderfile/orderfile_instrumentation.h"
#include <time.h>
#include <unistd.h>
#include <atomic>
#include <cstdio>
#include <cstring>
#include <string>
#include <thread>
#include <vector>
#include "base/android/library_loader/anchor_functions.h"
#include "base/android/orderfile/orderfile_buildflags.h"
#include "base/files/file.h"
#include "base/format_macros.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "build/build_config.h"
#if BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
#include <sstream>
#include "base/command_line.h"
#include "base/time/time.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/memory_dump_provider.h"
#endif // BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
#if !BUILDFLAG(SUPPORTS_CODE_ORDERING)
#error Only supported on architectures supporting code ordering (arm/arm64).
#endif // !BUILDFLAG(SUPPORTS_CODE_ORDERING)
// Must be applied to all functions within this file.
#define NO_INSTRUMENT_FUNCTION __attribute__((no_instrument_function))
#define INLINE_AND_NO_INSTRUMENT_FUNCTION \
__attribute__((always_inline, no_instrument_function))
namespace base {
namespace android {
namespace orderfile {
namespace {
#if BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
// This is defined in content/public/common/content_switches.h, which is not
// accessible in ::base.
constexpr const char kProcessTypeSwitch[] = "type";
#else
// Constant used for StartDelayedDump().
constexpr int kDelayInSeconds = 30;
#endif // BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
constexpr size_t kMaxTextSizeInBytes = 1 << 27;
constexpr size_t kMaxElements = kMaxTextSizeInBytes / 4;
// Native code currently have ~850k symbols, hence recording up to 1M symbols
// can cover all possible callee symbols.
constexpr size_t kMaxReachedSymbols = 1 << 20;
// 3 callers are recorded per callee.
constexpr size_t kCallerBuckets = 3;
// The last bucket is to count for misses and callers from outside the
// native code bounds.
constexpr size_t kMissesBucketIndex = 3;
constexpr size_t kTotalBuckets = 4;
std::atomic<uint32_t> callee_map[kMaxElements];
static_assert(sizeof(callee_map) == 128 * (1 << 20), "");
// Contain caller offsets. 4 buckets of callers per callee where the
// last bucket is for misses.
std::atomic<uint32_t> g_caller_offset[kMaxReachedSymbols * kTotalBuckets];
static_assert(sizeof(g_caller_offset) == 16 * (1 << 20), "");
// Corresponding count of |g_caller_offset|.
std::atomic<uint32_t> g_caller_count[kMaxReachedSymbols * kTotalBuckets];
static_assert(sizeof(g_caller_count) == 16 * (1 << 20), "");
// Index for |g_caller_offset| and |g_caller_count|.
std::atomic<uint32_t> g_callers_index;
std::atomic<uint32_t> g_calls_count;
std::atomic<bool> g_disabled;
#if BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
// Dump offsets when a memory dump is requested. Used only if
// switches::kDevtoolsInstrumentationDumping is set.
class OrderfileMemoryDumpHook : public base::trace_event::MemoryDumpProvider {
NO_INSTRUMENT_FUNCTION bool OnMemoryDump(
const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) override {
if (!Disable())
return true; // A dump has already been started.
std::string process_type =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
kProcessTypeSwitch);
if (process_type.empty())
process_type = "browser";
Dump(process_type);
return true; // If something goes awry, a fatal error will be created
// internally.
}
};
#endif // BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
// This is not racy. It is guaranteed that any number of threads concurrently
// calling this function in any order, will always end up with the same count
// at the end. It returns |element|'s value before the increment.
INLINE_AND_NO_INSTRUMENT_FUNCTION uint32_t
AtomicIncrement(std::atomic<uint32_t>* element) {
return element->fetch_add(1, std::memory_order_relaxed);
}
// Increment the miss bucket for a callee. |index| is the first bucket of
// callers for this callee.
INLINE_AND_NO_INSTRUMENT_FUNCTION void RecordMiss(size_t index) {
AtomicIncrement(g_caller_count + index + kMissesBucketIndex);
}
// Increment the caller count if it has previously been registered.
// If it hasn't, search for an empty bucket and register the caller.
// Otherwise, return false.
// |index| is the first bucket to register callers for a certain callee.
INLINE_AND_NO_INSTRUMENT_FUNCTION bool RecordCaller(size_t index,
size_t caller_offset) {
for (size_t i = index; i < index + kCallerBuckets; i++) {
auto offset = g_caller_offset[i].load(std::memory_order_relaxed);
// This check is racy, a write could have happened between the load and the
// check.
if (offset == caller_offset) {
// Caller already recorded, increment the count.
AtomicIncrement(g_caller_count + i);
return true;
}
}
for (size_t i = index; i < index + kCallerBuckets; i++) {
auto offset = g_caller_offset[i].load(std::memory_order_relaxed);
size_t expected = 0;
if (!offset) {
// This is not racy as the compare and exchange is done atomically.
// It is impossible to reset a bucket if it has already been set. It
// exchanges the value in |g_caller_offset[i]| with |caller_offset| if
// the value in |g_caller_offset[i] == expected|.
// Otherwise, returns false and set |expected = g_caller_offset[i]|.
if (g_caller_offset[i].compare_exchange_strong(
expected, caller_offset, std::memory_order_relaxed,
std::memory_order_relaxed)) {
AtomicIncrement(g_caller_count + i);
return true;
}
}
// This will decrease the chances that we miss something due to unseen
// changes made by another thread.
if (offset == caller_offset || expected == caller_offset) {
AtomicIncrement(g_caller_count + i);
return true;
}
}
return false;
}
template <bool for_testing>
__attribute__((always_inline, no_instrument_function)) void RecordAddress(
size_t callee_address,
size_t caller_address) {
bool disabled = g_disabled.load(std::memory_order_relaxed);
if (disabled)
return;
const size_t start =
for_testing ? kStartOfTextForTesting : base::android::kStartOfText;
const size_t end =
for_testing ? kEndOfTextForTesting : base::android::kEndOfText;
if (UNLIKELY(callee_address < start || callee_address > end)) {
// Only the code in the native library is instrumented. Callees are expected
// to be within the native library bounds.
Disable();
IMMEDIATE_CRASH();
}
size_t offset = callee_address - start;
static_assert(sizeof(int) == 4,
"Collection and processing code assumes that sizeof(int) == 4");
size_t offset_index = offset / 4;
if (UNLIKELY(offset_index >= kMaxElements))
return;
std::atomic<uint32_t>* element = callee_map + offset_index;
uint32_t callers_index = element->load(std::memory_order_relaxed);
// Racy check.
if (callers_index == 0) {
// Fragmentation is possible as we increment the |insertion_index| based on
// a racy check.
uint32_t insertion_index = AtomicIncrement(&g_callers_index) + 1;
if (UNLIKELY(insertion_index >= kMaxReachedSymbols))
return;
uint32_t expected = 0;
// Exchanges the value in |element| with |insertion_index| if the value in
// |element == expected|. Otherwise, set |expected = element|.
element->compare_exchange_strong(expected, insertion_index,
std::memory_order_relaxed,
std::memory_order_relaxed);
// If expected is set, then this callee has previously been seen and already
// has a corresponding index in the callers array.
callers_index = expected == 0 ? insertion_index : expected;
}
AtomicIncrement(&g_calls_count);
callers_index *= kTotalBuckets;
if (caller_address <= start || caller_address > end ||
!RecordCaller(callers_index, caller_address - start)) {
// Record as a Miss, if the caller is not within the bounds of the native
// code or there are no empty buckets to record one more caller for this
// callee.
RecordMiss(callers_index);
}
}
NO_INSTRUMENT_FUNCTION bool DumpToFile(const base::FilePath& path) {
auto file =
base::File(path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
if (!file.IsValid()) {
PLOG(ERROR) << "Could not open " << path;
return false;
}
if (g_callers_index == 0) {
LOG(ERROR) << "No entries to dump";
return false;
}
// This can get very large as it constructs the whole data structure in
// memory before dumping it to the file.
DictionaryValue root;
uint32_t total_calls_count = g_calls_count.load(std::memory_order_relaxed);
root.SetStringKey("total_calls_count",
base::StringPrintf("%" PRIu32, total_calls_count));
ListValue call_graph;
for (size_t i = 0; i < kMaxElements; i++) {
auto caller_index =
callee_map[i].load(std::memory_order_relaxed) * kTotalBuckets;
if (!caller_index)
// This callee was never called.
continue;
DictionaryValue callee_element;
uint32_t callee_offset = i * 4;
callee_element.SetStringKey("index",
base::StringPrintf("%" PRIuS, caller_index));
callee_element.SetStringKey("callee_offset",
base::StringPrintf("%" PRIu32, callee_offset));
std::string offset_str = "";
ListValue callers_list;
for (size_t j = 0; j < kTotalBuckets; j++) {
uint32_t caller_offset =
g_caller_offset[caller_index + j].load(std::memory_order_relaxed);
// The last bucket is for misses or callers outside the native library,
// the caller_offset for this bucket is 0.
if (j != kMissesBucketIndex && !caller_offset)
continue;
uint32_t count =
g_caller_count[caller_index + j].load(std::memory_order_relaxed);
// The count can only be 0 for the misses bucket. Otherwise,
// if |caller_offset| is set then the count must be >= 1.
CHECK_EQ(count || j == kMissesBucketIndex, true);
if (!count)
// No misses.
continue;
DictionaryValue caller_count;
caller_count.SetStringKey("caller_offset",
base::StringPrintf("%" PRIu32, caller_offset));
caller_count.SetStringKey("count", base::StringPrintf("%" PRIu32, count));
callers_list.Append(std::move(caller_count));
}
callee_element.SetKey("caller_and_count", std::move(callers_list));
call_graph.Append(std::move(callee_element));
}
root.SetKey("call_graph", std::move(call_graph));
std::string output_js;
if (!JSONWriter::Write(root, &output_js)) {
LOG(FATAL) << "Error getting JSON string";
}
if (file.WriteAtCurrentPos(output_js.c_str(),
static_cast<int>(output_js.size())) < 0) {
// If the file could be opened, but writing has failed, it's likely that
// data was partially written. Producing incomplete profiling data would
// lead to a poorly performing orderfile, but might not be otherwised
// noticed. So we crash instead.
LOG(FATAL) << "Error writing profile data";
}
return true;
}
// Stops recording, and outputs the data to |path|.
NO_INSTRUMENT_FUNCTION void StopAndDumpToFile(int pid,
uint64_t start_ns_since_epoch,
const std::string& tag) {
std::string tag_str;
if (!tag.empty())
tag_str = base::StringPrintf("%s-", tag.c_str());
auto path = base::StringPrintf(
"/data/local/tmp/chrome/orderfile/profile-hitmap-%s%d-%" PRIu64 ".txt",
tag_str.c_str(), pid, start_ns_since_epoch);
if (!DumpToFile(base::FilePath(path))) {
LOG(ERROR) << "Problem with dump (" << tag << ")";
}
}
} // namespace
// It is safe to call any function after |Disable()| has been called. No risk of
// infinite recursion.
NO_INSTRUMENT_FUNCTION bool Disable() {
bool disabled = g_disabled.exchange(true, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_seq_cst);
return !disabled;
}
NO_INSTRUMENT_FUNCTION void StartDelayedDump() {
#if BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
static auto* g_orderfile_memory_dump_hook = new OrderfileMemoryDumpHook();
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
g_orderfile_memory_dump_hook, "Orderfile", nullptr);
// Return, letting devtools tracing handle any dumping.
#else
// Using std::thread and not using base::TimeTicks() in order to to not call
// too many base:: symbols that would pollute the reached symbol dumps.
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts))
PLOG(FATAL) << "clock_gettime.";
uint64_t start_ns_since_epoch =
static_cast<uint64_t>(ts.tv_sec) * 1000 * 1000 * 1000 + ts.tv_nsec;
int pid = getpid();
std::thread([pid, start_ns_since_epoch]() {
sleep(kDelayInSeconds);
if (Disable())
StopAndDumpToFile(pid, start_ns_since_epoch, "");
}).detach();
#endif // BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
}
NO_INSTRUMENT_FUNCTION void Dump(const std::string& tag) {
// As profiling has been disabled, none of the uses of ::base symbols below
// will enter the symbol dump.
StopAndDumpToFile(
getpid(), (base::Time::Now() - base::Time::UnixEpoch()).InNanoseconds(),
tag);
}
NO_INSTRUMENT_FUNCTION void ResetForTesting() {
Disable();
memset(reinterpret_cast<uint32_t*>(callee_map), 0,
sizeof(uint32_t) * kMaxElements);
memset(reinterpret_cast<uint32_t*>(g_caller_offset), 0,
sizeof(uint32_t) * kMaxReachedSymbols * kTotalBuckets);
memset(reinterpret_cast<uint32_t*>(g_caller_count), 0,
sizeof(uint32_t) * kMaxReachedSymbols * kTotalBuckets);
g_callers_index = 0;
g_disabled = false;
}
NO_INSTRUMENT_FUNCTION void RecordAddressForTesting(size_t callee_address,
size_t caller_address) {
return RecordAddress<true>(callee_address, caller_address);
}
// Returns a flattened vector where each callee is allocated 9 buckets.
// First bucket -> callee offset
// 8 buckets -> [caller offset, count, ...]
NO_INSTRUMENT_FUNCTION std::vector<size_t> GetOrderedOffsetsForTesting() {
std::vector<size_t> result;
for (size_t i = 0; i < kMaxElements; i++) {
auto caller_index =
callee_map[i].load(std::memory_order_relaxed) * kTotalBuckets;
if (!caller_index)
continue;
result.push_back(i * 4);
for (size_t j = 0; j < kTotalBuckets; j++) {
uint32_t count =
g_caller_count[caller_index + j].load(std::memory_order_relaxed);
uint32_t caller_offset =
g_caller_offset[caller_index + j].load(std::memory_order_relaxed);
result.push_back(caller_offset);
result.push_back(count);
}
}
return result;
}
} // namespace orderfile
} // namespace android
} // namespace base
extern "C" {
NO_INSTRUMENT_FUNCTION void __cyg_profile_func_enter_bare() {
base::android::orderfile::RecordAddress<false>(
reinterpret_cast<size_t>(__builtin_return_address(0)),
reinterpret_cast<size_t>(__builtin_return_address(1)));
}
} // extern "C"

View file

@ -0,0 +1,328 @@
// Copyright (c) 2017 The Chromium 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/orderfile/orderfile_instrumentation.h"
#include <time.h>
#include <unistd.h>
#include <atomic>
#include <cstdio>
#include <cstring>
#include <string>
#include <thread>
#include <vector>
#include "base/android/library_loader/anchor_functions.h"
#include "base/android/orderfile/orderfile_buildflags.h"
#include "base/files/file.h"
#include "base/format_macros.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#if BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
#include <sstream>
#include "base/command_line.h"
#include "base/time/time.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/memory_dump_provider.h"
#endif // BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
#if !BUILDFLAG(SUPPORTS_CODE_ORDERING)
#error Only supported on architectures supporting code ordering (arm/arm64).
#endif // !BUILDFLAG(SUPPORTS_CODE_ORDERING)
// Must be applied to all functions within this file.
#define NO_INSTRUMENT_FUNCTION __attribute__((no_instrument_function))
namespace base {
namespace android {
namespace orderfile {
namespace {
// Constants used for StartDelayedDump().
constexpr int kDelayInSeconds = 30;
constexpr int kInitialDelayInSeconds = kPhases == 1 ? kDelayInSeconds : 5;
#if BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
// This is defined in content/public/common/content_switches.h, which is not
// accessible in ::base.
constexpr const char kProcessTypeSwitch[] = "type";
#endif // BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
// These are large overestimates, which is not an issue, as the data is
// allocated in .bss, and on linux doesn't take any actual memory when it's not
// touched.
constexpr size_t kBitfieldSize = 1 << 22;
constexpr size_t kMaxTextSizeInBytes = kBitfieldSize * (4 * 32);
constexpr size_t kMaxElements = 1 << 20;
// Data required to log reached offsets.
struct LogData {
std::atomic<uint32_t> offsets[kBitfieldSize];
std::atomic<size_t> ordered_offsets[kMaxElements];
std::atomic<size_t> index;
};
LogData g_data[kPhases];
std::atomic<int> g_data_index;
#if BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
// Dump offsets when a memory dump is requested. Used only if
// switches::kDevtoolsInstrumentationDumping is set.
class OrderfileMemoryDumpHook : public base::trace_event::MemoryDumpProvider {
NO_INSTRUMENT_FUNCTION bool OnMemoryDump(
const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) override {
// Disable instrumentation now to cut down on orderfile pollution.
if (!Disable()) {
return true; // A dump has already been started.
}
std::stringstream process_type_str;
Dump(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
kProcessTypeSwitch));
return true; // If something goes awry, a fatal error will be created
// internally.
}
};
#endif // BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
// |RecordAddress()| adds an element to a concurrent bitset and to a concurrent
// append-only list of offsets.
//
// Ordering:
// Two consecutive calls to |RecordAddress()| from the same thread will be
// ordered in the same way in the result, as written by
// |StopAndDumpToFile()|. The result will contain exactly one instance of each
// unique offset relative to |kStartOfText| passed to |RecordAddress()|.
//
// Implementation:
// The "set" part is implemented with a bitfield, |g_offset|. The insertion
// order is recorded in |g_ordered_offsets|.
// This is not a class to make sure there isn't a static constructor, as it
// would cause issue with an instrumented static constructor calling this code.
//
// Limitations:
// - Only records offsets to addresses between |kStartOfText| and |kEndOfText|.
// - Capacity of the set is limited by |kMaxElements|.
// - Some insertions at the end of collection may be lost.
// Records that |address| has been reached, if recording is enabled.
// To avoid infinite recursion, this *must* *never* call any instrumented
// function, unless |Disable()| is called first.
template <bool for_testing>
__attribute__((always_inline, no_instrument_function)) void RecordAddress(
size_t address) {
int index = g_data_index.load(std::memory_order_relaxed);
if (index >= kPhases)
return;
const size_t start =
for_testing ? kStartOfTextForTesting : base::android::kStartOfText;
const size_t end =
for_testing ? kEndOfTextForTesting : base::android::kEndOfText;
if (UNLIKELY(address < start || address > end)) {
Disable();
// If the start and end addresses are set incorrectly, this code path is
// likely happening during a static initializer. Logging at this time is
// prone to deadlock. By crashing immediately we at least have a chance to
// get a stack trace from the system to give some clue about the nature of
// the problem.
IMMEDIATE_CRASH();
}
size_t offset = address - start;
static_assert(sizeof(int) == 4,
"Collection and processing code assumes that sizeof(int) == 4");
size_t offset_index = offset / 4;
auto* offsets = g_data[index].offsets;
// Atomically set the corresponding bit in the array.
std::atomic<uint32_t>* element = offsets + (offset_index / 32);
// First, a racy check. This saves a CAS if the bit is already set, and
// allows the cache line to remain shared acoss CPUs in this case.
uint32_t value = element->load(std::memory_order_relaxed);
uint32_t mask = 1 << (offset_index % 32);
if (value & mask)
return;
auto before = element->fetch_or(mask, std::memory_order_relaxed);
if (before & mask)
return;
// We were the first one to set the element, record it in the ordered
// elements list.
// Use relaxed ordering, as the value is not published, or used for
// synchronization.
auto* ordered_offsets = g_data[index].ordered_offsets;
auto& ordered_offsets_index = g_data[index].index;
size_t insertion_index =
ordered_offsets_index.fetch_add(1, std::memory_order_relaxed);
if (UNLIKELY(insertion_index >= kMaxElements)) {
Disable();
LOG(FATAL) << "Too many reached offsets";
}
ordered_offsets[insertion_index].store(offset, std::memory_order_relaxed);
}
NO_INSTRUMENT_FUNCTION bool DumpToFile(const base::FilePath& path,
const LogData& data) {
auto file =
base::File(path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
if (!file.IsValid()) {
PLOG(ERROR) << "Could not open " << path;
return false;
}
if (data.index == 0) {
LOG(ERROR) << "No entries to dump";
return false;
}
size_t count = data.index - 1;
for (size_t i = 0; i < count; i++) {
// |g_ordered_offsets| is initialized to 0, so a 0 in the middle of it
// indicates a case where the index was incremented, but the write is not
// visible in this thread yet. Safe to skip, also because the function at
// the start of text is never called.
auto offset = data.ordered_offsets[i].load(std::memory_order_relaxed);
if (!offset)
continue;
auto offset_str = base::StringPrintf("%" PRIuS "\n", offset);
if (file.WriteAtCurrentPos(offset_str.c_str(),
static_cast<int>(offset_str.size())) < 0) {
// If the file could be opened, but writing has failed, it's likely that
// data was partially written. Producing incomplete profiling data would
// lead to a poorly performing orderfile, but might not be otherwised
// noticed. So we crash instead.
LOG(FATAL) << "Error writing profile data";
}
}
return true;
}
// Stops recording, and outputs the data to |path|.
NO_INSTRUMENT_FUNCTION void StopAndDumpToFile(int pid,
uint64_t start_ns_since_epoch,
const std::string& tag) {
Disable();
for (int phase = 0; phase < kPhases; phase++) {
std::string tag_str;
if (!tag.empty())
tag_str = base::StringPrintf("%s-", tag.c_str());
auto path = base::StringPrintf(
"/data/local/tmp/chrome/orderfile/profile-hitmap-%s%d-%" PRIu64
".txt_%d",
tag_str.c_str(), pid, start_ns_since_epoch, phase);
if (!DumpToFile(base::FilePath(path), g_data[phase])) {
LOG(ERROR) << "Problem with dump " << phase << " (" << tag << ")";
}
}
}
} // namespace
NO_INSTRUMENT_FUNCTION bool Disable() {
auto old_phase = g_data_index.exchange(kPhases, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_seq_cst);
return old_phase != kPhases;
}
NO_INSTRUMENT_FUNCTION void SanityChecks() {
CHECK_LT(base::android::kEndOfText - base::android::kStartOfText,
kMaxTextSizeInBytes);
CHECK(base::android::IsOrderingSane());
}
NO_INSTRUMENT_FUNCTION bool SwitchToNextPhaseOrDump(
int pid,
uint64_t start_ns_since_epoch) {
int before = g_data_index.fetch_add(1, std::memory_order_relaxed);
if (before + 1 == kPhases) {
StopAndDumpToFile(pid, start_ns_since_epoch, "");
return true;
}
return false;
}
NO_INSTRUMENT_FUNCTION void StartDelayedDump() {
// Using std::thread and not using base::TimeTicks() in order to to not call
// too many base:: symbols that would pollute the reached symbol dumps.
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts))
PLOG(FATAL) << "clock_gettime.";
uint64_t start_ns_since_epoch =
static_cast<uint64_t>(ts.tv_sec) * 1000 * 1000 * 1000 + ts.tv_nsec;
int pid = getpid();
#if BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
static auto* g_orderfile_memory_dump_hook = new OrderfileMemoryDumpHook();
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
g_orderfile_memory_dump_hook, "Orderfile", nullptr);
#endif // BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
std::thread([pid, start_ns_since_epoch]() {
sleep(kInitialDelayInSeconds);
#if BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
SwitchToNextPhaseOrDump(pid, start_ns_since_epoch);
// Return, letting devtools tracing handle any post-startup phases.
#else
while (!SwitchToNextPhaseOrDump(pid, start_ns_since_epoch))
sleep(kDelayInSeconds);
#endif // BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
})
.detach();
}
NO_INSTRUMENT_FUNCTION void Dump(const std::string& tag) {
// As profiling has been disabled, none of the uses of ::base symbols below
// will enter the symbol dump.
StopAndDumpToFile(
getpid(), (base::Time::Now() - base::Time::UnixEpoch()).InNanoseconds(),
tag);
}
NO_INSTRUMENT_FUNCTION void ResetForTesting() {
Disable();
g_data_index = 0;
for (int i = 0; i < kPhases; i++) {
memset(reinterpret_cast<uint32_t*>(g_data[i].offsets), 0,
sizeof(uint32_t) * kBitfieldSize);
memset(reinterpret_cast<uint32_t*>(g_data[i].ordered_offsets), 0,
sizeof(uint32_t) * kMaxElements);
g_data[i].index.store(0);
}
}
NO_INSTRUMENT_FUNCTION void RecordAddressForTesting(size_t address) {
return RecordAddress<true>(address);
}
NO_INSTRUMENT_FUNCTION std::vector<size_t> GetOrderedOffsetsForTesting() {
std::vector<size_t> result;
size_t max_index = g_data[0].index.load(std::memory_order_relaxed);
for (size_t i = 0; i < max_index; ++i) {
auto value = g_data[0].ordered_offsets[i].load(std::memory_order_relaxed);
if (value)
result.push_back(value);
}
return result;
}
} // namespace orderfile
} // namespace android
} // namespace base
extern "C" {
NO_INSTRUMENT_FUNCTION void __cyg_profile_func_enter_bare() {
base::android::orderfile::RecordAddress<false>(
reinterpret_cast<size_t>(__builtin_return_address(0)));
}
} // extern "C"

View file

@ -0,0 +1,57 @@
// Copyright 2017 The Chromium 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_ORDERFILE_ORDERFILE_INSTRUMENTATION_H_
#define BASE_ANDROID_ORDERFILE_ORDERFILE_INSTRUMENTATION_H_
#include <cstdint>
#include <vector>
#include "base/android/orderfile/orderfile_buildflags.h"
namespace base {
namespace android {
namespace orderfile {
#if BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
constexpr int kPhases = 2;
#else
constexpr int kPhases = 1;
#endif // BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING)
constexpr size_t kStartOfTextForTesting = 1000;
constexpr size_t kEndOfTextForTesting = kStartOfTextForTesting + 1000 * 1000;
// Stop recording. Returns false if recording was already disabled.
bool Disable();
// Switches to the next recording phase. If called from the last phase, dumps
// the data to disk, and returns |true|. |pid| is the current process pid, and
// |start_ns_since_epoch| the process start timestamp.
bool SwitchToNextPhaseOrDump(int pid, uint64_t start_ns_since_epoch);
// Starts a thread to dump instrumentation after a delay.
void StartDelayedDump();
// Dumps all information for the current process, annotating the dump file name
// with the given tag. Will disable instrumentation. Instrumentation must be
// disabled before this is called.
void Dump(const std::string& tag);
// Record an |address|, if recording is enabled. Only for testing.
void RecordAddressForTesting(size_t address);
// Record |callee_address, caller_address|, if recording is enabled.
// Only for testing.
void RecordAddressForTesting(size_t callee_address, size_t caller_address);
// Resets the state. Only for testing.
void ResetForTesting();
// Returns an ordered list of reached offsets. Only for testing.
std::vector<size_t> GetOrderedOffsetsForTesting();
} // namespace orderfile
} // namespace android
} // namespace base
#endif // BASE_ANDROID_ORDERFILE_ORDERFILE_INSTRUMENTATION_H_

View file

@ -0,0 +1,22 @@
// 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/android/jni_string.h"
#include "base/base_jni_headers/PathService_jni.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
namespace base {
namespace android {
void JNI_PathService_Override(JNIEnv* env,
jint what,
const JavaParamRef<jstring>& path) {
FilePath file_path(ConvertJavaStringToUTF8(env, path));
PathService::Override(what, file_path);
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,82 @@
// 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_array.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/files/file_path.h"
#include "base/base_jni_headers/PathUtils_jni.h"
namespace base {
namespace android {
bool GetDataDirectory(FilePath* result) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> path = Java_PathUtils_getDataDirectory(env);
FilePath data_path(ConvertJavaStringToUTF8(path));
*result = data_path;
return true;
}
bool GetCacheDirectory(FilePath* result) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> path = Java_PathUtils_getCacheDirectory(env);
FilePath cache_path(ConvertJavaStringToUTF8(path));
*result = cache_path;
return true;
}
bool GetThumbnailCacheDirectory(FilePath* result) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> path =
Java_PathUtils_getThumbnailCacheDirectory(env);
FilePath thumbnail_cache_path(ConvertJavaStringToUTF8(path));
*result = thumbnail_cache_path;
return true;
}
bool GetDownloadsDirectory(FilePath* result) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> path = Java_PathUtils_getDownloadsDirectory(env);
FilePath downloads_path(ConvertJavaStringToUTF8(path));
*result = downloads_path;
return true;
}
std::vector<FilePath> GetAllPrivateDownloadsDirectories() {
std::vector<std::string> dirs;
JNIEnv* env = AttachCurrentThread();
auto jarray = Java_PathUtils_getAllPrivateDownloadsDirectories(env);
base::android::AppendJavaStringArrayToStringVector(env, jarray, &dirs);
std::vector<base::FilePath> file_paths;
for (const auto& dir : dirs)
file_paths.emplace_back(dir);
return file_paths;
}
bool GetNativeLibraryDirectory(FilePath* result) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> path =
Java_PathUtils_getNativeLibraryDirectory(env);
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;
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,55 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_ANDROID_PATH_UTILS_H_
#define BASE_ANDROID_PATH_UTILS_H_
#include <jni.h>
#include <vector>
#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 thumbnail cache directory. The result is placed
// in the FilePath pointed to by 'result'.
BASE_EXPORT bool GetThumbnailCacheDirectory(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 paths to all download directories, including default storage
// directory, and a private directory on external SD card.
BASE_EXPORT std::vector<FilePath> GetAllPrivateDownloadsDirectories();
// 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);
} // namespace android
} // namespace base
#endif // BASE_ANDROID_PATH_UTILS_H_

View file

@ -0,0 +1 @@
smaier@chromium.org

View file

@ -0,0 +1,82 @@
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Contains flags that we'd like all Chromium .apks to use.
# Not needed for Android and saves a bit of processing time.
-dontpreverify
# Keep line number information, useful for stack traces.
-keepattributes SourceFile,LineNumberTable
# Keep all CREATOR fields within Parcelable that are kept.
-keepclassmembers class * implements android.os.Parcelable {
public static *** CREATOR;
}
# Keep all default constructors for used Fragments. Required since they are
# called reflectively when fragments are reinflated after the app is killed.
-keepclassmembers class * extends android.app.Fragment {
public <init>();
}
-keepclassmembers class * extends android.support.v4.app.Fragment {
public <init>();
}
-keepclassmembers class * extends androidx.fragment.app.Fragment {
public <init>();
}
# Don't obfuscate Parcelables as they might be marshalled outside Chrome.
# If we annotated all Parcelables that get put into Bundles other than
# for saveInstanceState (e.g. PendingIntents), then we could actually keep the
# names of just those ones. For now, we'll just keep them all.
-keepnames class * implements android.os.Parcelable {}
# Keep all enum values and valueOf methods. See
# http://proguard.sourceforge.net/index.html#manual/examples.html
# for the reason for this. Also, see http://crbug.com/248037.
-keepclassmembers enum * {
public static **[] values();
}
# Allows Proguard freedom in removing these log related calls.
-assumenosideeffects class android.util.Log {
static *** d(...);
static *** v(...);
static boolean isLoggable(...);
}
# The following chart was created on July 20, 2016, to decide on 3 optimization
# passes for Chrome.
# optimization passes | time | .dex size | dirty memory per process
# -----------------------------------------------------------------
# 1 | 0:48 | 5805676 | 488972
# 2 | 1:07 | 5777376 | 487092
# 3 | 1:24 | 5772192 | 486596
# 4 | 1:42 | 5771124 | 486484
# 5 | 1:56 | 5770504 | 486432
-optimizationpasses 3
# Horizontal class merging marginally increases dex size (as of Mar 2018).
-optimizations !class/merging/horizontal
# Allowing Proguard to change modifiers. This change shrinks the .dex size by
# ~1%, and reduces the method count by ~4%.
-allowaccessmodification
# Workaround for crbug/1002847. Methods of BaseGmsClient are incorrectly
# removed even though they are required for the derived class GmsClient
# to correctly implement Api$Client.
# TODO: remove once crbug/1002847 resolved.
-keep public class com.google.android.gms.common.internal.BaseGmsClient {
public void disconnect();
public void dump(java.lang.String,java.io.FileDescriptor,java.io.PrintWriter,java.lang.String[]);
public int getMinApkVersion();
public boolean requiresSignIn();
}
# Protobuf java lite runtime uses reflection
-keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite {
<fields>;
}

View file

@ -0,0 +1,96 @@
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Contains flags that can be safely shared with Cronet, and thus would be
# appropriate for third-party apps to include.
# Keep all annotation related attributes that can affect runtime
-keepattributes RuntimeVisible*Annotations
-keepattributes AnnotationDefault
# Keep the annotations, because if we don't, the ProGuard rules that use them
# will not be respected. These classes then show up in our final dex, which we
# do not want - see crbug.com/628226.
-keep @interface org.chromium.base.annotations.AccessedByNative
-keep @interface org.chromium.base.annotations.CalledByNative
-keep @interface org.chromium.base.annotations.CalledByNativeUnchecked
-keep @interface org.chromium.base.annotations.DoNotInline
-keep @interface org.chromium.base.annotations.RemovableInRelease
-keep @interface org.chromium.base.annotations.UsedByReflection
# Android support library annotations will get converted to androidx ones
# which we want to keep.
-keep @interface androidx.annotation.Keep
-keep @androidx.annotation.Keep class *
-keepclasseswithmembers class * {
@androidx.annotation.Keep <fields>;
}
-keepclasseswithmembers class * {
@androidx.annotation.Keep <methods>;
}
# Keeps for class level annotations.
-keep @org.chromium.base.annotations.UsedByReflection class ** {}
# Keeps for method level annotations.
-keepclasseswithmembers class ** {
@org.chromium.base.annotations.AccessedByNative <fields>;
}
-keepclasseswithmembers,includedescriptorclasses class ** {
@org.chromium.base.annotations.CalledByNative <methods>;
}
-keepclasseswithmembers,includedescriptorclasses class ** {
@org.chromium.base.annotations.CalledByNativeUnchecked <methods>;
}
-keepclasseswithmembers class ** {
@org.chromium.base.annotations.UsedByReflection <methods>;
}
-keepclasseswithmembers class ** {
@org.chromium.base.annotations.UsedByReflection <fields>;
}
# Even unused methods kept due to explicit jni registration:
# https://crbug.com/688465.
-keepclasseswithmembers,includedescriptorclasses class !org.chromium.base.library_loader.**,** {
native <methods>;
}
-keepclasseswithmembernames,includedescriptorclasses class org.chromium.base.library_loader.** {
native <methods>;
}
-assumenosideeffects class ** {
# Remove @RemovableInRelease methods so long as return values are unused.
@org.chromium.base.annotations.RemovableInRelease <methods>;
# Remove object @RemovableInRelease methods even when return value is used.
# Note: * in return type does not match primitives.
@org.chromium.base.annotations.RemovableInRelease * *(...) return null;
# Remove boolean @RemovableInRelease methods even when return value is used.
@org.chromium.base.annotations.RemovableInRelease boolean *(...) return false;
}
# Never inline classes or methods with this annotation, but allow shrinking and
# obfuscation.
-keep,allowobfuscation,allowoptimization @org.chromium.base.annotations.DoNotInline class ** {
<methods>;
}
-keepclassmembers,allowobfuscation,allowoptimization class ** {
@org.chromium.base.annotations.DoNotInline <methods>;
}
# Keep all CREATOR fields within Parcelable that are kept.
-keepclassmembers class org.chromium.** implements android.os.Parcelable {
public static *** CREATOR;
}
# Don't obfuscate Parcelables as they might be marshalled outside Chrome.
# If we annotated all Parcelables that get put into Bundles other than
# for saveInstanceState (e.g. PendingIntents), then we could actually keep the
# names of just those ones. For now, we'll just keep them all.
-keepnames class org.chromium.** implements android.os.Parcelable {}
# Keep all enum values and valueOf methods. See
# http://proguard.sourceforge.net/index.html#manual/examples.html
# for the reason for this. Also, see http://crbug.com/248037.
-keepclassmembers enum org.chromium.** {
public static **[] values();
}

View file

@ -0,0 +1,8 @@
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Disables obfuscation while still allowing optimizations.
-keepnames,allowoptimization class *** {
*;
}

View file

@ -0,0 +1,7 @@
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# As of August 11, 2016, obfuscation was found to save 660kb on our .dex size
# and 53kb memory/process (through shrinking method/string counts).
-repackageclasses ''

View file

@ -0,0 +1,99 @@
// Copyright 2019 The Chromium 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/reached_addresses_bitset.h"
#include "base/android/library_loader/anchor_functions.h"
#include "base/android/library_loader/anchor_functions_buildflags.h"
#include "base/logging.h"
#include "base/no_destructor.h"
namespace base {
namespace android {
namespace {
constexpr size_t kBitsPerElement = sizeof(uint32_t) * 8;
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
// Enough for 1 << 29 bytes of code, 512MB.
constexpr size_t kTextBitfieldSize = 1 << 20;
std::atomic<uint32_t> g_text_bitfield[kTextBitfieldSize];
#endif
} // namespace
// static
ReachedAddressesBitset* ReachedAddressesBitset::GetTextBitset() {
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
static base::NoDestructor<ReachedAddressesBitset> text_bitset(
kStartOfText, kEndOfText, g_text_bitfield, kTextBitfieldSize);
return text_bitset.get();
#else
return nullptr;
#endif
}
void ReachedAddressesBitset::RecordAddress(uintptr_t address) {
// |address| is outside of the range.
if (address < start_address_ || address >= end_address_)
return;
uint32_t offset = address - start_address_;
uint32_t offset_index = offset / kBytesGranularity;
// Atomically set the corresponding bit in the array.
std::atomic<uint32_t>* element = reached_ + (offset_index / kBitsPerElement);
// First, a racy check. This saves a CAS if the bit is already set, and
// allows the cache line to remain shared across CPUs in this case.
uint32_t value = element->load(std::memory_order_relaxed);
uint32_t mask = 1 << (offset_index % kBitsPerElement);
if (value & mask)
return;
element->fetch_or(mask, std::memory_order_relaxed);
}
std::vector<uint32_t> ReachedAddressesBitset::GetReachedOffsets() const {
std::vector<uint32_t> offsets;
const size_t elements = NumberOfReachableElements();
for (size_t i = 0; i < elements; ++i) {
uint32_t element = reached_[i].load(std::memory_order_relaxed);
// No reached addresses at this element.
if (element == 0)
continue;
for (size_t j = 0; j < 32; ++j) {
if (!((element >> j) & 1))
continue;
uint32_t offset_index = i * 32 + j;
uint32_t offset = offset_index * kBytesGranularity;
offsets.push_back(offset);
}
}
return offsets;
}
ReachedAddressesBitset::ReachedAddressesBitset(
uintptr_t start_address,
uintptr_t end_address,
std::atomic<uint32_t>* storage_ptr,
size_t storage_size)
: start_address_(start_address),
end_address_(end_address),
reached_(storage_ptr) {
DCHECK_LE(start_address_, end_address_);
DCHECK_LE(NumberOfReachableElements(), storage_size * kBitsPerElement);
}
size_t ReachedAddressesBitset::NumberOfReachableElements() const {
size_t reachable_bits =
(end_address_ + kBytesGranularity - 1) / kBytesGranularity -
start_address_ / kBytesGranularity;
return (reachable_bits + kBitsPerElement - 1) / kBitsPerElement;
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,75 @@
// Copyright 2019 The Chromium 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_REACHED_ADDRESSES_BITSET_H_
#define BASE_ANDROID_REACHED_ADDRESSES_BITSET_H_
#include <atomic>
#include <vector>
#include "base/base_export.h"
namespace base {
template <typename T>
class NoDestructor;
namespace android {
// ReachedAddressesBitset is a set that stores addresses for the
// ReachedCodeProfiler in compact form. Its main features are lock-free
// thread-safety and fast adding of elements.
//
// The addresses are kept with |kBytesGranularity| to save the storage space.
//
// Once insterted, elements cannot be erased from the set.
//
// All methods can be called from any thread.
class BASE_EXPORT ReachedAddressesBitset {
public:
// Returns an instance of ReachedAddressesBitset having enough storage space
// to keep all addresses from the .text section.
// Returns nullptr if SUPPORTS_CODE_ORDERING isn't defined.
// This instance is stored in the .bss section of the binary, meaning that it
// doesn't incur the binary size overhead and it doesn't increase the resident
// memory footprint when not used.
static ReachedAddressesBitset* GetTextBitset();
// Inserts |address| into the bitset iff |address| lies in the range between
// |start_address_| and |end_address_|.
void RecordAddress(uintptr_t address);
// Returns a list of recorded addresses in the form of offsets from
// |start_address_|.
std::vector<uint32_t> GetReachedOffsets() const;
private:
friend class ReachedAddressesBitsetTest;
friend class NoDestructor<ReachedAddressesBitset>;
// Represents the number of bytes that are mapped into the same bit in the
// bitset.
static constexpr size_t kBytesGranularity = 4;
// Constructs a ReachedAddressesBitset on top of an external storage of
// |storage_size| pointed by |storage_ptr|. This external storage must outlive
// the constructed bitset instance. The size of storage must be large enough
// to fit all addresses in the range between |start_address| and
// |end_address|.
ReachedAddressesBitset(uintptr_t start_address,
uintptr_t end_address,
std::atomic<uint32_t>* storage_ptr,
size_t storage_size);
size_t NumberOfReachableElements() const;
uintptr_t start_address_;
uintptr_t end_address_;
std::atomic<uint32_t>* reached_;
};
} // namespace android
} // namespace base
#endif // BASE_ANDROID_REACHED_ADDRESSES_BITSET_H_

View file

@ -0,0 +1,283 @@
// Copyright 2019 The Chromium 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/reached_code_profiler.h"
#include <signal.h>
#include <sys/time.h>
#include <ucontext.h>
#include <unistd.h>
#include <atomic>
#include "base/android/library_loader/anchor_functions.h"
#include "base/android/orderfile/orderfile_buildflags.h"
#include "base/android/reached_addresses_bitset.h"
#include "base/base_switches.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/important_file_writer.h"
#include "base/linux_util.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/optional.h"
#include "base/path_service.h"
#include "base/scoped_generic.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread.h"
#include "base/timer/timer.h"
#if !BUILDFLAG(SUPPORTS_CODE_ORDERING)
#error Code ordering support is required for the reached code profiler.
#endif
namespace base {
namespace android {
namespace {
#if !defined(NDEBUG) || defined(COMPONENT_BUILD)
// Always disabled for debug builds to avoid hitting a limit of signal
// interrupts that can get delivered into a single HANDLE_EINTR. Also
// debugging experience would be bad if there are a lot of signals flying
// around.
// Always disabled for component builds because in this case the code is not
// organized in one contiguous region which is required for the reached code
// profiler.
constexpr const bool kConfigurationSupported = false;
#else
constexpr const bool kConfigurationSupported = true;
#endif
constexpr const char kDumpToFileFlag[] = "reached-code-profiler-dump-to-file";
constexpr uint64_t kIterationsBeforeSkipping = 50;
constexpr uint64_t kIterationsBetweenUpdates = 100;
constexpr int kProfilerSignal = SIGURG;
constexpr base::TimeDelta kSamplingInterval =
base::TimeDelta::FromMilliseconds(10);
constexpr base::TimeDelta kDumpInterval = base::TimeDelta::FromSeconds(30);
void HandleSignal(int signal, siginfo_t* info, void* context) {
if (signal != kProfilerSignal)
return;
ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
uint32_t address = ucontext->uc_mcontext.arm_pc;
ReachedAddressesBitset::GetTextBitset()->RecordAddress(address);
}
struct ScopedTimerCloseTraits {
static base::Optional<timer_t> InvalidValue() { return base::nullopt; }
static void Free(base::Optional<timer_t> x) { timer_delete(*x); }
};
// RAII object holding an interval timer.
using ScopedTimer =
base::ScopedGeneric<base::Optional<timer_t>, ScopedTimerCloseTraits>;
void DumpToFile(const base::FilePath& path,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
DCHECK(task_runner->BelongsToCurrentThread());
auto dir_path = path.DirName();
if (!base::DirectoryExists(dir_path) && !base::CreateDirectory(dir_path)) {
PLOG(ERROR) << "Could not create " << dir_path;
return;
}
std::vector<uint32_t> reached_offsets =
ReachedAddressesBitset::GetTextBitset()->GetReachedOffsets();
base::StringPiece contents(
reinterpret_cast<const char*>(reached_offsets.data()),
reached_offsets.size());
if (!base::ImportantFileWriter::WriteFileAtomically(path, contents,
"ReachedDump")) {
LOG(ERROR) << "Could not write reached dump into " << path;
}
task_runner->PostDelayedTask(
FROM_HERE, base::BindOnce(&DumpToFile, path, task_runner), kDumpInterval);
}
class ReachedCodeProfiler {
public:
static ReachedCodeProfiler* GetInstance() {
static base::NoDestructor<ReachedCodeProfiler> instance;
return instance.get();
}
// Starts to periodically send |kProfilerSignal| to all threads.
void Start(LibraryProcessType library_process_type) {
if (is_enabled_)
return;
// Set |kProfilerSignal| signal handler.
// TODO(crbug.com/916263): consider restoring |old_handler| after the
// profiler gets stopped.
struct sigaction old_handler;
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = &HandleSignal;
sa.sa_flags = SA_RESTART | SA_SIGINFO;
int ret = sigaction(kProfilerSignal, &sa, &old_handler);
if (ret) {
PLOG(ERROR) << "Error setting signal handler. The reached code profiler "
"is disabled";
return;
}
// Create a new interval timer.
struct sigevent sevp;
memset(&sevp, 0, sizeof(sevp));
sevp.sigev_notify = SIGEV_THREAD;
sevp.sigev_notify_function = &OnTimerNotify;
timer_t timerid;
ret = timer_create(CLOCK_PROCESS_CPUTIME_ID, &sevp, &timerid);
if (ret) {
PLOG(ERROR)
<< "timer_create() failed. The reached code profiler is disabled";
return;
}
timer_.reset(timerid);
// Start the interval timer.
struct itimerspec its;
memset(&its, 0, sizeof(its));
its.it_interval.tv_nsec = kSamplingInterval.InNanoseconds();
its.it_value = its.it_interval;
ret = timer_settime(timerid, 0, &its, nullptr);
if (ret) {
PLOG(ERROR)
<< "timer_settime() failed. The reached code profiler is disabled";
return;
}
if (library_process_type == PROCESS_BROWSER)
StartDumpingReachedCode();
is_enabled_ = true;
}
// Stops profiling.
void Stop() {
timer_.reset();
dumping_thread_.reset();
is_enabled_ = false;
}
// Returns whether the profiler is currently enabled.
bool IsEnabled() { return is_enabled_; }
private:
ReachedCodeProfiler()
: current_pid_(getpid()), iteration_number_(0), is_enabled_(false) {}
static void OnTimerNotify(sigval_t ignored) {
ReachedCodeProfiler::GetInstance()->SendSignalToAllThreads();
}
void SendSignalToAllThreads() {
// This code should be thread-safe.
base::AutoLock scoped_lock(lock_);
++iteration_number_;
if (iteration_number_ <= kIterationsBeforeSkipping ||
iteration_number_ % kIterationsBetweenUpdates == 0) {
tids_.clear();
if (!base::GetThreadsForProcess(current_pid_, &tids_)) {
LOG(WARNING) << "Failed to get a list of threads for process "
<< current_pid_;
return;
}
}
pid_t current_tid = gettid();
for (pid_t tid : tids_) {
if (tid != current_tid)
tgkill(current_pid_, tid, kProfilerSignal);
}
}
void StartDumpingReachedCode() {
const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
if (!cmdline->HasSwitch(kDumpToFileFlag))
return;
base::FilePath dir_path(cmdline->GetSwitchValueASCII(kDumpToFileFlag));
if (dir_path.empty()) {
if (!base::PathService::Get(base::DIR_CACHE, &dir_path)) {
LOG(WARNING) << "Failed to get cache dir path.";
return;
}
}
auto file_path =
dir_path.Append(base::StringPrintf("reached-code-%d.txt", getpid()));
dumping_thread_ =
std::make_unique<base::Thread>("ReachedCodeProfilerDumpingThread");
base::Thread::Options options;
options.priority = base::ThreadPriority::BACKGROUND;
dumping_thread_->StartWithOptions(options);
dumping_thread_->task_runner()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&DumpToFile, file_path, dumping_thread_->task_runner()),
kDumpInterval);
}
base::Lock lock_;
std::vector<pid_t> tids_;
const pid_t current_pid_;
uint64_t iteration_number_;
ScopedTimer timer_;
std::unique_ptr<base::Thread> dumping_thread_;
bool is_enabled_;
friend class NoDestructor<ReachedCodeProfiler>;
DISALLOW_COPY_AND_ASSIGN(ReachedCodeProfiler);
};
bool ShouldEnableReachedCodeProfiler() {
if (!kConfigurationSupported)
return false;
const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
return cmdline->HasSwitch(switches::kEnableReachedCodeProfiler);
}
} // namespace
void InitReachedCodeProfilerAtStartup(LibraryProcessType library_process_type) {
// The profiler shouldn't be run as part of webview.
CHECK(library_process_type == PROCESS_BROWSER ||
library_process_type == PROCESS_CHILD);
if (!ShouldEnableReachedCodeProfiler())
return;
ReachedCodeProfiler::GetInstance()->Start(library_process_type);
}
bool IsReachedCodeProfilerEnabled() {
return ReachedCodeProfiler::GetInstance()->IsEnabled();
}
bool IsReachedCodeProfilerSupported() {
return kConfigurationSupported;
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,34 @@
// Copyright 2019 The Chromium 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_REACHED_CODE_PROFILER_H_
#define BASE_ANDROID_REACHED_CODE_PROFILER_H_
#include "base/android/library_loader/library_loader_hooks.h"
#include "base/base_export.h"
namespace base {
namespace android {
// Initializes and starts the reached code profiler for |library_process_type|.
// Reached symbols are not recorded before calling this function, so it has to
// be called as early in startup as possible. This has to be called before the
// process creates any thread.
// TODO(crbug.com/916263): Currently, the reached code profiler must be
// initialized before the tracing profiler. If we want to start it at later
// point, we need to check that the tracing profiler isn't initialized first.
BASE_EXPORT void InitReachedCodeProfilerAtStartup(
LibraryProcessType library_process_type);
// Returns whether the reached code profiler is enabled.
BASE_EXPORT bool IsReachedCodeProfilerEnabled();
// Returns whether the reached code profiler can be possibly enabled for the
// current build configuration.
BASE_EXPORT bool IsReachedCodeProfilerSupported();
} // namespace android
} // namespace base
#endif // BASE_ANDROID_REACHED_CODE_PROFILER_H_

View file

@ -0,0 +1,22 @@
// Copyright 2019 The Chromium 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/reached_code_profiler.h"
namespace base {
namespace android {
void InitReachedCodeProfilerAtStartup(LibraryProcessType library_process_type) {
}
bool IsReachedCodeProfilerEnabled() {
return false;
}
bool IsReachedCodeProfilerSupported() {
return false;
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,52 @@
// Copyright 2014 The Chromium 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 <stdint.h>
#include <map>
#include <string>
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/base_jni_headers/RecordHistogram_jni.h"
#include "base/metrics/histogram.h"
#include "base/metrics/statistics_recorder.h"
namespace base {
namespace android {
// This backs a Java test util for testing histograms -
// MetricsUtils.HistogramDelta. It should live in a test-specific file, but we
// currently can't have test-specific native code packaged in test-specific Java
// targets - see http://crbug.com/415945.
jint JNI_RecordHistogram_GetHistogramValueCountForTesting(
JNIEnv* env,
const JavaParamRef<jstring>& histogram_name,
jint sample) {
HistogramBase* histogram = StatisticsRecorder::FindHistogram(
android::ConvertJavaStringToUTF8(env, histogram_name));
if (histogram == nullptr) {
// No samples have been recorded for this histogram (yet?).
return 0;
}
std::unique_ptr<HistogramSamples> samples = histogram->SnapshotSamples();
return samples->GetCount(static_cast<int>(sample));
}
jint JNI_RecordHistogram_GetHistogramTotalCountForTesting(
JNIEnv* env,
const JavaParamRef<jstring>& histogram_name) {
HistogramBase* histogram = StatisticsRecorder::FindHistogram(
android::ConvertJavaStringToUTF8(env, histogram_name));
if (histogram == nullptr) {
// No samples have been recorded for this histogram.
return 0;
}
return histogram->SnapshotSamples()->TotalCount();
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,49 @@
// Copyright 2015 The Chromium 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/base_jni_headers/RecordUserAction_jni.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/metrics/user_metrics.h"
namespace {
struct ActionCallbackWrapper {
base::ActionCallback action_callback;
};
} // namespace
namespace base {
namespace android {
static void OnActionRecorded(const JavaRef<jobject>& callback,
const std::string& action,
TimeTicks action_time) {
JNIEnv* env = AttachCurrentThread();
Java_UserActionCallback_onActionRecorded(
env, callback, ConvertUTF8ToJavaString(env, action));
}
static jlong JNI_RecordUserAction_AddActionCallbackForTesting(
JNIEnv* env,
const JavaParamRef<jobject>& callback) {
// Create a wrapper for the ActionCallback, so it can life on the heap until
// RemoveActionCallbackForTesting() is called.
auto* wrapper = new ActionCallbackWrapper{base::BindRepeating(
&OnActionRecorded, ScopedJavaGlobalRef<jobject>(env, callback))};
base::AddActionCallback(wrapper->action_callback);
return reinterpret_cast<intptr_t>(wrapper);
}
static void JNI_RecordUserAction_RemoveActionCallbackForTesting(
JNIEnv* env,
jlong callback_id) {
DCHECK(callback_id);
auto* wrapper = reinterpret_cast<ActionCallbackWrapper*>(callback_id);
base::RemoveActionCallback(wrapper->action_callback);
delete wrapper;
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,28 @@
// Copyright 2019 The Chromium 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_hardware_buffer_fence_sync.h"
#include <utility>
namespace base {
namespace android {
ScopedHardwareBufferFenceSync::ScopedHardwareBufferFenceSync(
ScopedHardwareBufferHandle handle,
ScopedFD fence_fd)
: handle_(std::move(handle)), fence_fd_(std::move(fence_fd)) {}
ScopedHardwareBufferFenceSync::~ScopedHardwareBufferFenceSync() = default;
ScopedHardwareBufferHandle ScopedHardwareBufferFenceSync::TakeBuffer() {
return std::move(handle_);
}
ScopedFD ScopedHardwareBufferFenceSync::TakeFence() {
return std::move(fence_fd_);
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,41 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_ANDROID_SCOPED_HARDWARE_BUFFER_FENCE_SYNC_H_
#define BASE_ANDROID_SCOPED_HARDWARE_BUFFER_FENCE_SYNC_H_
#include "base/android/scoped_hardware_buffer_handle.h"
#include "base/base_export.h"
#include "base/files/scoped_file.h"
namespace base {
namespace android {
// This class provides a ScopedHardwareBufferHandle and may include a fence
// which will be signaled when all pending work for the buffer has been finished
// and it can be safely read from.
class BASE_EXPORT ScopedHardwareBufferFenceSync {
public:
ScopedHardwareBufferFenceSync(
base::android::ScopedHardwareBufferHandle handle,
base::ScopedFD fence_fd);
virtual ~ScopedHardwareBufferFenceSync();
AHardwareBuffer* buffer() const { return handle_.get(); }
ScopedHardwareBufferHandle TakeBuffer();
ScopedFD TakeFence();
// Provides fence which is signaled when the reads for this buffer are done
// and it can be reused. Must only be called once.
virtual void SetReadFence(base::ScopedFD fence_fd, bool has_context) = 0;
private:
ScopedHardwareBufferHandle handle_;
ScopedFD fence_fd_;
};
} // namespace android
} // namespace base
#endif // BASE_ANDROID_SCOPED_HARDWARE_BUFFER_FENCE_SYNC_H_

View file

@ -0,0 +1,121 @@
// Copyright 2018 The Chromium 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_hardware_buffer_handle.h"
#include "base/android/android_hardware_buffer_compat.h"
#include "base/logging.h"
#include "base/posix/unix_domain_socket.h"
namespace base {
namespace android {
ScopedHardwareBufferHandle::ScopedHardwareBufferHandle() = default;
ScopedHardwareBufferHandle::ScopedHardwareBufferHandle(
ScopedHardwareBufferHandle&& other) {
*this = std::move(other);
}
ScopedHardwareBufferHandle::~ScopedHardwareBufferHandle() {
reset();
}
// static
ScopedHardwareBufferHandle ScopedHardwareBufferHandle::Adopt(
AHardwareBuffer* buffer) {
return ScopedHardwareBufferHandle(buffer);
}
// static
ScopedHardwareBufferHandle ScopedHardwareBufferHandle::Create(
AHardwareBuffer* buffer) {
AndroidHardwareBufferCompat::GetInstance().Acquire(buffer);
return ScopedHardwareBufferHandle(buffer);
}
ScopedHardwareBufferHandle& ScopedHardwareBufferHandle::operator=(
ScopedHardwareBufferHandle&& other) {
reset();
std::swap(buffer_, other.buffer_);
return *this;
}
bool ScopedHardwareBufferHandle::is_valid() const {
return buffer_ != nullptr;
}
AHardwareBuffer* ScopedHardwareBufferHandle::get() const {
return buffer_;
}
void ScopedHardwareBufferHandle::reset() {
if (buffer_) {
AndroidHardwareBufferCompat::GetInstance().Release(buffer_);
buffer_ = nullptr;
}
}
AHardwareBuffer* ScopedHardwareBufferHandle::Take() {
AHardwareBuffer* buffer = nullptr;
std::swap(buffer, buffer_);
return buffer;
}
ScopedHardwareBufferHandle ScopedHardwareBufferHandle::Clone() const {
DCHECK(buffer_);
AndroidHardwareBufferCompat::GetInstance().Acquire(buffer_);
return ScopedHardwareBufferHandle(buffer_);
}
ScopedFD ScopedHardwareBufferHandle::SerializeAsFileDescriptor() const {
DCHECK(is_valid());
ScopedFD reader, writer;
if (!CreateSocketPair(&reader, &writer)) {
PLOG(ERROR) << "socketpair";
return ScopedFD();
}
// NOTE: SendHandleToUnixSocket does NOT acquire or retain a reference to the
// buffer object. The caller is therefore responsible for ensuring that the
// buffer remains alive through the lifetime of this file descriptor.
int result =
AndroidHardwareBufferCompat::GetInstance().SendHandleToUnixSocket(
buffer_, writer.get());
if (result < 0) {
PLOG(ERROR) << "send";
return ScopedFD();
}
return reader;
}
// static
ScopedHardwareBufferHandle
ScopedHardwareBufferHandle::DeserializeFromFileDescriptor(ScopedFD fd) {
DCHECK(fd.is_valid());
DCHECK(AndroidHardwareBufferCompat::IsSupportAvailable());
AHardwareBuffer* buffer = nullptr;
// NOTE: Upon success, RecvHandleFromUnixSocket acquires a new reference to
// the AHardwareBuffer.
int result =
AndroidHardwareBufferCompat::GetInstance().RecvHandleFromUnixSocket(
fd.get(), &buffer);
if (result < 0) {
PLOG(ERROR) << "recv";
return ScopedHardwareBufferHandle();
}
return ScopedHardwareBufferHandle(buffer);
}
ScopedHardwareBufferHandle::ScopedHardwareBufferHandle(AHardwareBuffer* buffer)
: buffer_(buffer) {
DCHECK(AndroidHardwareBufferCompat::IsSupportAvailable());
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,90 @@
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_ANDROID_SCOPED_HARDWARE_BUFFER_HANDLE_H_
#define BASE_ANDROID_SCOPED_HARDWARE_BUFFER_HANDLE_H_
#include "base/base_export.h"
#include "base/files/scoped_file.h"
#include "base/macros.h"
extern "C" typedef struct AHardwareBuffer AHardwareBuffer;
namespace base {
namespace android {
// Owns a single reference to an AHardwareBuffer object.
class BASE_EXPORT ScopedHardwareBufferHandle {
public:
ScopedHardwareBufferHandle();
// Takes ownership of |other|'s buffer reference. Does NOT acquire a new one.
ScopedHardwareBufferHandle(ScopedHardwareBufferHandle&& other);
// Releases this handle's reference to the underlying buffer object if still
// valid.
~ScopedHardwareBufferHandle();
// Assumes ownership of an existing reference to |buffer|. This does NOT
// acquire a new reference.
static ScopedHardwareBufferHandle Adopt(AHardwareBuffer* buffer);
// Adds a reference to |buffer| managed by this handle.
static ScopedHardwareBufferHandle Create(AHardwareBuffer* buffer);
// Takes ownership of |other|'s buffer reference. Does NOT acquire a new one.
ScopedHardwareBufferHandle& operator=(ScopedHardwareBufferHandle&& other);
bool is_valid() const;
AHardwareBuffer* get() const;
// Releases this handle's reference to the underlying buffer object if still
// valid. Invalidates this handle.
void reset();
// Passes implicit ownership of this handle's reference over to the caller,
// invalidating |this|. Returns the raw buffer handle.
//
// The caller is responsible for eventually releasing this reference to the
// buffer object.
AHardwareBuffer* Take() WARN_UNUSED_RESULT;
// Creates a new handle with its own newly acquired reference to the
// underlying buffer object. |this| must be a valid handle.
ScopedHardwareBufferHandle Clone() const;
// Consumes a handle and returns a file descriptor which can be used to
// transmit the handle over IPC. A subsequent receiver may use
// |DeserializeFromFileDescriptor()| to recover the buffer handle.
//
// NOTE: The returned file descriptor DOES NOT own a reference to the
// underlying AHardwareBuffer. When using this for IPC, the caller is
// responsible for retaining at least one reference to the buffer object to
// keep it alive while the descriptor is in transit.
ScopedFD SerializeAsFileDescriptor() const;
// Consumes the supplied single-use file descriptor (which must have been
// returned by a previous call to |SerializeAsFileDescriptor()|, perhaps in
// a different process), and recovers an AHardwareBuffer object from it.
//
// This acquires a new reference to the AHardwareBuffer, with ownership passed
// to the caller via the returned ScopedHardwareBufferHandle.
static ScopedHardwareBufferHandle DeserializeFromFileDescriptor(ScopedFD fd)
WARN_UNUSED_RESULT;
private:
// Assumes ownership of an existing reference to |buffer|. This does NOT
// acquire a new reference.
explicit ScopedHardwareBufferHandle(AHardwareBuffer* buffer);
AHardwareBuffer* buffer_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(ScopedHardwareBufferHandle);
};
} // namespace android
} // namespace base
#endif // BASE_ANDROID_SCOPED_HARDWARE_BUFFER_HANDLE_H_

View file

@ -0,0 +1,99 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/android/scoped_java_ref.h"
#include "base/android/jni_android.h"
#include "base/logging.h"
#include <tgnet/FileLog.h>
namespace base {
namespace android {
namespace {
const int kDefaultLocalFrameCapacity = 16;
} // namespace
ScopedJavaLocalFrame::ScopedJavaLocalFrame(JNIEnv* env) : env_(env) {
int failed = env_->PushLocalFrame(kDefaultLocalFrameCapacity);
DCHECK(!failed);
}
ScopedJavaLocalFrame::ScopedJavaLocalFrame(JNIEnv* env, int capacity)
: env_(env) {
int failed = env_->PushLocalFrame(capacity);
DCHECK(!failed);
}
ScopedJavaLocalFrame::~ScopedJavaLocalFrame() {
env_->PopLocalFrame(nullptr);
}
#if DCHECK_IS_ON()
// This constructor is inlined when DCHECKs are disabled; don't add anything
// else here.
JavaRef<jobject>::JavaRef(JNIEnv* env, jobject obj) : obj_(obj) {
if (obj) {
DCHECK(env && env->GetObjectRefType(obj) == JNILocalRefType);
}
}
#endif
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) {
DEBUG_REF("scoped_java_ref.cc");
obj = env->NewGlobalRef(obj);
}
if (obj_) {
DEBUG_DELREF("scoped_java_ref.cc");
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_ = nullptr;
}
}
void JavaRef<jobject>::ResetGlobalRef() {
if (obj_) {
DEBUG_DELREF("webrtc ResetGlobalRef");
AttachCurrentThread()->DeleteGlobalRef(obj_);
obj_ = nullptr;
}
}
jobject JavaRef<jobject>::ReleaseInternal() {
jobject obj = obj_;
obj_ = nullptr;
return obj;
}
} // namespace android
} // namespace base

View file

@ -0,0 +1,541 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_ANDROID_SCOPED_JAVA_REF_H_
#define BASE_ANDROID_SCOPED_JAVA_REF_H_
#include <jni.h>
#include <stddef.h>
#include <type_traits>
#include <utility>
#include "base/base_export.h"
#include "base/logging.h"
#include "base/macros.h"
namespace base {
namespace android {
// Creates a new local reference frame, in which at least a given number of
// local references can be created. Note that local references already created
// in previous local frames are still valid in the current local frame.
class BASE_EXPORT ScopedJavaLocalFrame {
public:
explicit ScopedJavaLocalFrame(JNIEnv* env);
ScopedJavaLocalFrame(JNIEnv* env, int capacity);
~ScopedJavaLocalFrame();
private:
// This class is only good for use on the thread it was created on so
// it's safe to cache the non-threadsafe JNIEnv* inside this object.
JNIEnv* env_;
DISALLOW_COPY_AND_ASSIGN(ScopedJavaLocalFrame);
};
// Forward declare the generic java reference template class.
template <typename T>
class JavaRef;
// Template specialization of JavaRef, which acts as the base class for all
// other JavaRef<> template types. This allows you to e.g. pass
// ScopedJavaLocalRef<jstring> into a function taking const JavaRef<jobject>&
template <>
class BASE_EXPORT JavaRef<jobject> {
public:
// Initializes a null reference.
constexpr JavaRef() {}
// Allow nullptr to be converted to JavaRef. This avoids having to declare an
// empty JavaRef just to pass null to a function, and makes C++ "nullptr" and
// Java "null" equivalent.
constexpr JavaRef(std::nullptr_t) {}
// Public to allow destruction of null JavaRef objects.
~JavaRef() {}
// TODO(torne): maybe rename this to get() for consistency with unique_ptr
// once there's fewer unnecessary uses of it in the codebase.
jobject obj() const { return obj_; }
explicit operator bool() const { return obj_ != nullptr; }
// Deprecated. Just use bool conversion.
// TODO(torne): replace usage and remove this.
bool is_null() const { return obj_ == nullptr; }
protected:
// Takes ownership of the |obj| reference passed; requires it to be a local
// reference type.
#if DCHECK_IS_ON()
// Implementation contains a DCHECK; implement out-of-line when DCHECK_IS_ON.
JavaRef(JNIEnv* env, jobject obj);
#else
JavaRef(JNIEnv* env, jobject obj) : obj_(obj) {}
#endif
// Used for move semantics. obj_ must have been released first if non-null.
void steal(JavaRef&& other) {
obj_ = other.obj_;
other.obj_ = nullptr;
}
// The following are implementation detail convenience methods, for
// use by the sub-classes.
JNIEnv* SetNewLocalRef(JNIEnv* env, jobject obj);
void SetNewGlobalRef(JNIEnv* env, jobject obj);
void ResetLocalRef(JNIEnv* env);
void ResetGlobalRef();
jobject ReleaseInternal();
private:
jobject obj_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(JavaRef);
};
// Forward declare the object array reader for the convenience function.
template <typename T>
class JavaObjectArrayReader;
// Generic base class for ScopedJavaLocalRef and ScopedJavaGlobalRef. Useful
// for allowing functions to accept a reference without having to mandate
// whether it is a local or global type.
template <typename T>
class JavaRef : public JavaRef<jobject> {
public:
constexpr JavaRef() {}
constexpr JavaRef(std::nullptr_t) {}
~JavaRef() {}
T obj() const { return static_cast<T>(JavaRef<jobject>::obj()); }
// Get a JavaObjectArrayReader for the array pointed to by this reference.
// Only defined for JavaRef<jobjectArray>.
// You must pass the type of the array elements (usually jobject) as the
// template parameter.
template <typename ElementType,
typename T_ = T,
typename = std::enable_if_t<std::is_same<T_, jobjectArray>::value>>
JavaObjectArrayReader<ElementType> ReadElements() const {
return JavaObjectArrayReader<ElementType>(*this);
}
protected:
JavaRef(JNIEnv* env, T obj) : JavaRef<jobject>(env, obj) {}
private:
DISALLOW_COPY_AND_ASSIGN(JavaRef);
};
// Holds a local reference to a JNI method parameter.
// Method parameters should not be deleted, and so this class exists purely to
// wrap them as a JavaRef<T> in the JNI binding generator. Do not create
// instances manually.
template <typename T>
class JavaParamRef : public JavaRef<T> {
public:
// Assumes that |obj| is a parameter passed to a JNI method from Java.
// Does not assume ownership as parameters should not be deleted.
JavaParamRef(JNIEnv* env, T obj) : JavaRef<T>(env, obj) {}
// Allow nullptr to be converted to JavaParamRef. Some unit tests call JNI
// methods directly from C++ and pass null for objects which are not actually
// used by the implementation (e.g. the caller object); allow this to keep
// working.
JavaParamRef(std::nullptr_t) {}
~JavaParamRef() {}
// TODO(torne): remove this cast once we're using JavaRef consistently.
// http://crbug.com/506850
operator T() const { return JavaRef<T>::obj(); }
private:
DISALLOW_COPY_AND_ASSIGN(JavaParamRef);
};
// Holds a local reference to a Java object. The local reference is scoped
// to the lifetime of this object.
// Instances of this class may hold onto any JNIEnv passed into it until
// destroyed. Therefore, since a JNIEnv is only suitable for use on a single
// thread, objects of this class must be created, used, and destroyed, on a
// single thread.
// Therefore, this class should only be used as a stack-based object and from a
// single thread. If you wish to have the reference outlive the current
// callstack (e.g. as a class member) or you wish to pass it across threads,
// use a ScopedJavaGlobalRef instead.
template <typename T>
class ScopedJavaLocalRef : public JavaRef<T> {
public:
// Take ownership of a bare jobject. This does not create a new reference.
// This should only be used by JNI helper functions, or in cases where code
// must call JNIEnv methods directly.
static ScopedJavaLocalRef Adopt(JNIEnv* env, T obj) {
return ScopedJavaLocalRef(env, obj);
}
constexpr ScopedJavaLocalRef() {}
constexpr ScopedJavaLocalRef(std::nullptr_t) {}
// Copy constructor. This is required in addition to the copy conversion
// constructor below.
ScopedJavaLocalRef(const ScopedJavaLocalRef& other) : env_(other.env_) {
JavaRef<T>::SetNewLocalRef(env_, other.obj());
}
// Copy conversion constructor.
template <typename U,
typename = std::enable_if_t<std::is_convertible<U, T>::value>>
ScopedJavaLocalRef(const ScopedJavaLocalRef<U>& other) : env_(other.env_) {
JavaRef<T>::SetNewLocalRef(env_, other.obj());
}
// Move constructor. This is required in addition to the move conversion
// constructor below.
ScopedJavaLocalRef(ScopedJavaLocalRef&& other) : env_(other.env_) {
JavaRef<T>::steal(std::move(other));
}
// Move conversion constructor.
template <typename U,
typename = std::enable_if_t<std::is_convertible<U, T>::value>>
ScopedJavaLocalRef(ScopedJavaLocalRef<U>&& other) : env_(other.env_) {
JavaRef<T>::steal(std::move(other));
}
// Constructor for other JavaRef types.
explicit ScopedJavaLocalRef(const JavaRef<T>& other) { Reset(other); }
// Assumes that |obj| is a local reference to a Java object and takes
// ownership of this local reference.
// TODO(torne): make legitimate uses call Adopt() instead, and make this
// private.
ScopedJavaLocalRef(JNIEnv* env, T obj) : JavaRef<T>(env, obj), env_(env) {}
~ScopedJavaLocalRef() { Reset(); }
// Null assignment, for disambiguation.
ScopedJavaLocalRef& operator=(std::nullptr_t) {
Reset();
return *this;
}
// Copy assignment.
ScopedJavaLocalRef& operator=(const ScopedJavaLocalRef& other) {
Reset(other);
return *this;
}
// Copy conversion assignment.
template <typename U,
typename = std::enable_if_t<std::is_convertible<U, T>::value>>
ScopedJavaLocalRef& operator=(const ScopedJavaLocalRef<U>& other) {
Reset(other);
return *this;
}
// Move assignment.
template <typename U,
typename = std::enable_if_t<std::is_convertible<U, T>::value>>
ScopedJavaLocalRef& operator=(ScopedJavaLocalRef<U>&& other) {
env_ = other.env_;
Reset();
JavaRef<T>::steal(std::move(other));
return *this;
}
// Assignment for other JavaRef types.
ScopedJavaLocalRef& operator=(const JavaRef<T>& other) {
Reset(other);
return *this;
}
void Reset() { JavaRef<T>::ResetLocalRef(env_); }
template <typename U,
typename = std::enable_if_t<std::is_convertible<U, T>::value>>
void Reset(const ScopedJavaLocalRef<U>& other) {
// We can copy over env_ here as |other| instance must be from the same
// thread as |this| local ref. (See class comment for multi-threading
// limitations, and alternatives).
Reset(other.env_, other.obj());
}
void Reset(const JavaRef<T>& other) {
// If |env_| was not yet set (is still null) it will be attached to the
// current thread in SetNewLocalRef().
Reset(env_, other.obj());
}
// Creates a new local reference to the Java object, unlike the constructor
// with the same parameters that takes ownership of the existing reference.
// Deprecated. Don't use bare jobjects; use a JavaRef as the input.
// TODO(torne): fix existing usage and remove this.
void Reset(JNIEnv* env, T obj) {
env_ = JavaRef<T>::SetNewLocalRef(env, obj);
}
// Releases the local reference to the caller. The caller *must* delete the
// local reference when it is done with it. Note that calling a Java method
// is *not* a transfer of ownership and Release() should not be used.
T Release() { return static_cast<T>(JavaRef<T>::ReleaseInternal()); }
private:
// This class is only good for use on the thread it was created on so
// it's safe to cache the non-threadsafe JNIEnv* inside this object.
JNIEnv* env_ = nullptr;
// Prevent ScopedJavaLocalRef(JNIEnv*, T obj) from being used to take
// ownership of a JavaParamRef's underlying object - parameters are not
// allowed to be deleted and so should not be owned by ScopedJavaLocalRef.
// TODO(torne): this can be removed once JavaParamRef no longer has an
// implicit conversion back to T.
ScopedJavaLocalRef(JNIEnv* env, const JavaParamRef<T>& other);
// Friend required to get env_ from conversions.
template <typename U>
friend class ScopedJavaLocalRef;
// Avoids JavaObjectArrayReader having to accept and store its own env.
template <typename U>
friend class JavaObjectArrayReader;
};
// Holds a global reference to a Java object. The global reference is scoped
// to the lifetime of this object. This class does not hold onto any JNIEnv*
// passed to it, hence it is safe to use across threads (within the constraints
// imposed by the underlying Java object that it references).
template <typename T>
class ScopedJavaGlobalRef : public JavaRef<T> {
public:
constexpr ScopedJavaGlobalRef() {}
constexpr ScopedJavaGlobalRef(std::nullptr_t) {}
// Copy constructor. This is required in addition to the copy conversion
// constructor below.
ScopedJavaGlobalRef(const ScopedJavaGlobalRef& other) { Reset(other); }
// Copy conversion constructor.
template <typename U,
typename = std::enable_if_t<std::is_convertible<U, T>::value>>
ScopedJavaGlobalRef(const ScopedJavaGlobalRef<U>& other) {
Reset(other);
}
// Move constructor. This is required in addition to the move conversion
// constructor below.
ScopedJavaGlobalRef(ScopedJavaGlobalRef&& other) {
JavaRef<T>::steal(std::move(other));
}
// Move conversion constructor.
template <typename U,
typename = std::enable_if_t<std::is_convertible<U, T>::value>>
ScopedJavaGlobalRef(ScopedJavaGlobalRef<U>&& other) {
JavaRef<T>::steal(std::move(other));
}
// Conversion constructor for other JavaRef types.
explicit ScopedJavaGlobalRef(const JavaRef<T>& other) { Reset(other); }
// Create a new global reference to the object.
// Deprecated. Don't use bare jobjects; use a JavaRef as the input.
ScopedJavaGlobalRef(JNIEnv* env, T obj) { Reset(env, obj); }
~ScopedJavaGlobalRef() { Reset(); }
// Null assignment, for disambiguation.
ScopedJavaGlobalRef& operator=(std::nullptr_t) {
Reset();
return *this;
}
// Copy assignment.
ScopedJavaGlobalRef& operator=(const ScopedJavaGlobalRef& other) {
Reset(other);
return *this;
}
// Copy conversion assignment.
template <typename U,
typename = std::enable_if_t<std::is_convertible<U, T>::value>>
ScopedJavaGlobalRef& operator=(const ScopedJavaGlobalRef<U>& other) {
Reset(other);
return *this;
}
// Move assignment.
template <typename U,
typename = std::enable_if_t<std::is_convertible<U, T>::value>>
ScopedJavaGlobalRef& operator=(ScopedJavaGlobalRef<U>&& other) {
Reset();
JavaRef<T>::steal(std::move(other));
return *this;
}
// Assignment for other JavaRef types.
ScopedJavaGlobalRef& operator=(const JavaRef<T>& other) {
Reset(other);
return *this;
}
void Reset() { JavaRef<T>::ResetGlobalRef(); }
template <typename U,
typename = std::enable_if_t<std::is_convertible<U, T>::value>>
void Reset(const ScopedJavaGlobalRef<U>& other) {
Reset(nullptr, other.obj());
}
void Reset(const JavaRef<T>& other) { Reset(nullptr, other.obj()); }
// Deprecated. You can just use Reset(const JavaRef&).
void Reset(JNIEnv* env, const JavaParamRef<T>& other) {
Reset(env, other.obj());
}
// Deprecated. Don't use bare jobjects; use a JavaRef as the input.
void Reset(JNIEnv* env, T obj) { JavaRef<T>::SetNewGlobalRef(env, obj); }
// Releases the global reference to the caller. The caller *must* delete the
// global reference when it is done with it. Note that calling a Java method
// is *not* a transfer of ownership and Release() should not be used.
T Release() { return static_cast<T>(JavaRef<T>::ReleaseInternal()); }
};
// Wrapper for a jobjectArray which supports input iteration, allowing Java
// arrays to be iterated over with a range-based for loop, or used with
// <algorithm> functions that accept input iterators.
//
// The iterator returns each object in the array in turn, wrapped in a
// ScopedJavaLocalRef<T>. T will usually be jobject, but if you know that the
// array contains a more specific type (such as jstring) you can use that
// instead. This does not check the type at runtime!
//
// The wrapper holds a local reference to the array and only queries the size of
// the array once, so must only be used as a stack-based object from the current
// thread.
//
// Note that this does *not* update the contents of the array if you mutate the
// returned ScopedJavaLocalRef.
template <typename T>
class JavaObjectArrayReader {
public:
class iterator {
public:
// We can only be an input iterator, as all richer iterator types must
// implement the multipass guarantee (always returning the same object for
// the same iterator position), which is not practical when returning
// temporary objects.
using iterator_category = std::input_iterator_tag;
using difference_type = ptrdiff_t;
using value_type = ScopedJavaLocalRef<T>;
// It doesn't make sense to return a reference type as the iterator creates
// temporary wrapper objects when dereferenced. Fortunately, it's not
// required that input iterators actually use references, and defining it
// as value_type is valid.
using reference = value_type;
// This exists to make operator-> work as expected: its return value must
// resolve to an actual pointer (otherwise the compiler just keeps calling
// operator-> on the return value until it does), so we need an extra level
// of indirection. This is sometimes called an "arrow proxy" or similar, and
// this version is adapted from base/value_iterators.h.
class pointer {
public:
explicit pointer(const reference& ref) : ref_(ref) {}
pointer(const pointer& ptr) = default;
pointer& operator=(const pointer& ptr) = delete;
reference* operator->() { return &ref_; }
private:
reference ref_;
};
iterator(const iterator&) = default;
~iterator() = default;
iterator& operator=(const iterator&) = default;
bool operator==(const iterator& other) const {
DCHECK(reader_ == other.reader_);
return i_ == other.i_;
}
bool operator!=(const iterator& other) const {
DCHECK(reader_ == other.reader_);
return i_ != other.i_;
}
reference operator*() const {
DCHECK(i_ < reader_->size_);
// JNIEnv functions return unowned local references; take ownership with
// Adopt so that ~ScopedJavaLocalRef will release it automatically later.
return value_type::Adopt(
reader_->array_.env_,
static_cast<T>(reader_->array_.env_->GetObjectArrayElement(
reader_->array_.obj(), i_)));
}
pointer operator->() const { return pointer(operator*()); }
iterator& operator++() {
DCHECK(i_ < reader_->size_);
++i_;
return *this;
}
iterator operator++(int) {
iterator old = *this;
++*this;
return old;
}
private:
iterator(const JavaObjectArrayReader* reader, jsize i)
: reader_(reader), i_(i) {}
const JavaObjectArrayReader* reader_;
jsize i_;
friend JavaObjectArrayReader;
};
JavaObjectArrayReader(const JavaRef<jobjectArray>& array) : array_(array) {
size_ = array_.env_->GetArrayLength(array_.obj());
}
// Copy constructor to allow returning it from JavaRef::ReadElements().
JavaObjectArrayReader(const JavaObjectArrayReader& other) = default;
// Assignment operator for consistency with copy constructor.
JavaObjectArrayReader& operator=(const JavaObjectArrayReader& other) =
default;
// Allow move constructor and assignment since this owns a local ref.
JavaObjectArrayReader(JavaObjectArrayReader&& other) = default;
JavaObjectArrayReader& operator=(JavaObjectArrayReader&& other) = default;
bool empty() const { return size_ == 0; }
jsize size() const { return size_; }
iterator begin() const { return iterator(this, 0); }
iterator end() const { return iterator(this, size_); }
private:
ScopedJavaLocalRef<jobjectArray> array_;
jsize size_;
friend iterator;
};
} // namespace android
} // namespace base
#endif // BASE_ANDROID_SCOPED_JAVA_REF_H_

View file

@ -0,0 +1,28 @@
// Copyright 2016 The Chromium 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 <string>
#include "base/android/jni_string.h"
#include "base/base_jni_headers/StatisticsRecorderAndroid_jni.h"
#include "base/metrics/histogram_base.h"
#include "base/metrics/statistics_recorder.h"
#include "base/system/sys_info.h"
using base::android::JavaParamRef;
using base::android::ConvertUTF8ToJavaString;
namespace base {
namespace android {
static ScopedJavaLocalRef<jstring> JNI_StatisticsRecorderAndroid_ToJson(
JNIEnv* env,
jint verbosityLevel) {
return ConvertUTF8ToJavaString(
env, base::StatisticsRecorder::ToJSON(
static_cast<JSONVerbosityLevel>(verbosityLevel)));
}
} // namespace android
} // namespace base

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