Skip to content

Commit

Permalink
Merge branch 'release/5.200.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
CDRussell committed May 13, 2024
2 parents 85266ae + d34f33f commit 886d06e
Show file tree
Hide file tree
Showing 63 changed files with 1,620 additions and 79 deletions.
2 changes: 1 addition & 1 deletion .maestro/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ appStartup:
enabled: false
appSize:
enabled: false
disableRetries: true
disableRetries: false

flows:
- "**"
29 changes: 29 additions & 0 deletions anrs/anrs-impl/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
project(crash-ndk)
cmake_minimum_required(VERSION 3.4.1)

add_library( # Sets the name of the library.
crash-ndk

# Sets the library as a shared library.
SHARED

# Provides a relative path to your source file(s).
src/main/cpp/ndk-crash.cpp
src/main/cpp/jni.cpp
src/main/cpp/pixel.cpp
)

find_library( # Sets the name of the path variable.
log-lib

# Specifies the name of the NDK library that
# you want CMake to locate.
log)

target_link_libraries(
# Specifies the target library.
crash-ndk

# Links the target library to the log library
# included in the NDK.
${log-lib} )
15 changes: 14 additions & 1 deletion anrs/anrs-impl/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ plugins {
apply from: "$rootProject.projectDir/gradle/android-library.gradle"

dependencies {
anvil project(':anvil-compiler')
implementation project(':anvil-annotations')

implementation project(':app-build-config-api')
implementation project(':anrs-api')
implementation project(':anrs-store')
Expand All @@ -31,6 +34,8 @@ dependencies {
implementation project(':browser-api')
implementation project(':statistics')
implementation project(':verified-installation-api')
implementation project(':library-loader-api')
implementation project(':feature-toggles-api')

implementation AndroidX.core.ktx
implementation KotlinX.coroutines.core
Expand All @@ -56,8 +61,16 @@ android {
anvil {
generateDaggerFactories = true // default is false
}
namespace 'com.duckduckgo.app.anr'

ndkVersion '21.4.7075529'
namespace 'com.duckduckgo.app.anr'
compileOptions {
coreLibraryDesugaringEnabled = true
}

externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
18 changes: 18 additions & 0 deletions anrs/anrs-impl/src/main/cpp/android.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef ANDROID_JNI_H
#define ANDROID_JNI_H

#include <cstdio>
#include <android/log.h>

void __platform_log_print(int prio, const char *tag, const char *fmt, ...);

// loglevel will be assigned during library initialisation, it always has a default value
extern int loglevel;
extern char pname[256];
extern char appVersion[256];
extern bool isCustomTab;

// Use this method to print log messages into the console
#define log_print(prio, format, ...) do { if (prio >= loglevel) __platform_log_print(prio, "ndk-crash", format, ##__VA_ARGS__); } while (0)

#endif // ANDROID_JNI_H
146 changes: 146 additions & 0 deletions anrs/anrs-impl/src/main/cpp/jni.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#include <jni.h>
#include <android/log.h>
#include <exception>
#include <string.h> // strncpy

#include "android.h"
#include "ndk-crash.h"
#include "pixel.h"

///////////////////////////////////////////////////////////////////////////

static JavaVM *JVM = NULL;
jclass clsCrash;
jobject CLASS_JVM_CRASH = NULL;


static jobject jniGlobalRef(JNIEnv *env, jobject cls);
static jclass jniFindClass(JNIEnv *env, const char *name);
static jmethodID jniGetMethodID(JNIEnv *env, jclass cls, const char *name, const char *signature);

int loglevel = 0;
char appVersion[256];
char pname[256];
bool isCustomTab = false;

///////////////////////////////////////////////////////////////////////////


void __platform_log_print(int prio, const char *tag, const char *fmt, ...) {
char line[1024];
va_list argptr;
va_start(argptr, fmt);
vsprintf(line, fmt, argptr);
__android_log_print(prio, tag, "%s", line);
va_end(argptr);
}

///////////////////////////////////////////////////////////////////////////
// JNI utils
///////////////////////////////////////////////////////////////////////////

static jobject jniGlobalRef(JNIEnv *env, jobject cls) {
jobject gcls = env->NewGlobalRef(cls);
if (gcls == NULL)
log_print(ANDROID_LOG_ERROR, "Global ref failed (out of memory?)");
return gcls;
}

static jclass jniFindClass(JNIEnv *env, const char *name) {
jclass cls = env->FindClass(name);
if (cls == NULL)
log_print(ANDROID_LOG_ERROR, "Class %s not found", name);
return cls;
}

static jmethodID jniGetMethodID(JNIEnv *env, jclass cls, const char *name, const char *signature) {
jmethodID method = env->GetMethodID(cls, name, signature);
if (method == NULL) {
log_print(ANDROID_LOG_ERROR, "Method %s %s not found", name, signature);
}
return method;
}

///////////////////////////////////////////////////////////////////////////
// JNI lifecycle
///////////////////////////////////////////////////////////////////////////

jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
if ((vm)->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
log_print(ANDROID_LOG_INFO, "JNI load GetEnv failed");
return -1;
}

jint rs = env->GetJavaVM(&JVM);
if (rs != JNI_OK) {
log_print(ANDROID_LOG_ERROR, "Could not get JVM");
return -1;
}

return JNI_VERSION_1_6;
}

void JNI_OnUnload(JavaVM *vm, void *reserved) {
log_print(ANDROID_LOG_INFO, "JNI unload");

JNIEnv *env;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK)
log_print(ANDROID_LOG_INFO, "JNI load GetEnv failed");
else {
env->DeleteGlobalRef(clsCrash);
}
}

///////////////////////////////////////////////////////////////////////////
// native<>JVM interface
///////////////////////////////////////////////////////////////////////////

extern "C" JNIEXPORT void JNICALL
Java_com_duckduckgo_app_anr_ndk_NativeCrashInit_jni_1register_1sighandler(
JNIEnv* env,
jobject instance,
jint loglevel_,
jstring version_,
jstring pname_,
jboolean customtab_
) {

if (!native_crash_handler_init()) {
log_print(ANDROID_LOG_ERROR, "Error initialising crash handler.");
return;
}

// get and set loglevel
loglevel = loglevel_;

// get and set app vesrion
const char *versionChars = env->GetStringUTFChars(version_, nullptr);
strncpy(appVersion, versionChars, sizeof(appVersion) - 1);
appVersion[sizeof(appVersion) - 1] = '\0'; // Ensure null-termination
env->ReleaseStringUTFChars(version_, versionChars);

// get and set process name
const char *pnameChars = env->GetStringUTFChars(pname_, nullptr);
strncpy(pname, pnameChars, sizeof(pname) - 1);
pname[sizeof(pname) - 1] = '\0'; // Ensure null-termination
env->ReleaseStringUTFChars(pname_, pnameChars);

// get and set isCustomTabs
isCustomTab = customtab_;

clsCrash = env->GetObjectClass(instance);
const char *emptyParamVoidSig = "()V";
CLASS_JVM_CRASH = env->NewGlobalRef(instance);

send_crash_handle_init_pixel();

log_print(ANDROID_LOG_ERROR, "Native crash handler successfully initialized.");
}

extern "C" JNIEXPORT void JNICALL
Java_com_duckduckgo_app_anr_ndk_NativeCrashInit_jni_1unregister_sighandler(
JNIEnv* env,
jobject /* this */) {
native_crash_handler_fini();
}
129 changes: 129 additions & 0 deletions anrs/anrs-impl/src/main/cpp/ndk-crash.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#include "jni.h"
#include "android.h"
#include "pixel.h"

#include <csignal>
#include <cstdio>
#include <cstring>
#include <memory>
#include <cxxabi.h>
#include <unistd.h>

#define sizeofa(array) sizeof(array) / sizeof(array[0])

// sometimes signal handlers inlinux consume crashes entirely. For those cases we trigger a signal so that we ultimately
// crash properly
#define __NR_tgkill 270

// Caught signals
static const int SIGNALS_TO_CATCH[] = {
SIGABRT,
SIGBUS,
SIGFPE,
SIGSEGV,
SIGILL,
SIGSTKFLT,
SIGTRAP,
};

// Signal handler context
struct CrashInContext {
// Old handlers of signals that we restore on de-initialization. Keep values for all possible
// signals, for unused signals nullptr value is stored.
struct sigaction old_handlers[NSIG];
};

// Crash handler function signature
typedef void (*CrashSignalHandler)(int, siginfo*, void*);

// Global instance of context. As the app can only crash once per process lifetime, this can be global
static CrashInContext* crashInContext = nullptr;


// Main signal handling function.
static void native_crash_sig_handler(int signo, siginfo* siginfo, void* ctxvoid) {
// Restoring an old handler to make built-in Android crash mechanism work.
sigaction(signo, &crashInContext->old_handlers[signo], nullptr);

// Log crash message
__android_log_print(ANDROID_LOG_ERROR, "ndk-crash", "Terminating with uncaught exception of type %d", signo);
send_crash_pixel();

// sometimes signal handlers inlinux consume crashes entirely. For those cases we trigger a signal so that we ultimately
// crash properly, ie. to run standard bionic handler
if (siginfo->si_code <= 0 || signo == SIGABRT) {
if (syscall(__NR_tgkill, getpid(), gettid(), signo) < 0) {
_exit(1);
}
}
}

// Register signal handler for crashes
static bool register_sig_handler(CrashSignalHandler handler, struct sigaction old_handlers[NSIG]) {
struct sigaction sigactionstruct;
memset(&sigactionstruct, 0, sizeof(sigactionstruct));
sigactionstruct.sa_flags = SA_SIGINFO;
sigactionstruct.sa_sigaction = handler;

// Register new handlers for all signals
for (int index = 0; index < sizeofa(SIGNALS_TO_CATCH); ++index) {
const int signo = SIGNALS_TO_CATCH[index];

if (sigaction(signo, &sigactionstruct, &old_handlers[signo])) {
return false;
}
}

return true;
}

// Unregister already register signal handler
static void unregister_sig_handler(struct sigaction old_handlers[NSIG]) {
// Recover old handler for all signals
for (int signo = 0; signo < NSIG; ++signo) {
const struct sigaction* old_handler = &old_handlers[signo];

if (!old_handler->sa_handler) {
continue;
}

sigaction(signo, old_handler, nullptr);
}
}

bool native_crash_handler_fini() {
// Check if already deinitialized
if (!crashInContext) return false;

// Unregister signal handlers
unregister_sig_handler(crashInContext->old_handlers);

// Free singleton crash handler context
free(crashInContext);
crashInContext = nullptr;

log_print(ANDROID_LOG_ERROR, "Native crash handler successfully deinitialized.");

return true;
}

bool native_crash_handler_init() {
// Check if already initialized
if (crashInContext) {
log_print(ANDROID_LOG_INFO, "Native crash handler is already initialized.");
return false;
}

// Initialize singleton crash handler context
crashInContext = static_cast<CrashInContext *>(malloc(sizeof(CrashInContext)));
memset(crashInContext, 0, sizeof(CrashInContext));

// Trying to register signal handler.
if (!register_sig_handler(&native_crash_sig_handler, crashInContext->old_handlers)) {
native_crash_handler_fini();
log_print(ANDROID_LOG_ERROR, "Native crash handler initialization failed.");
return false;
}

return true;
}
10 changes: 10 additions & 0 deletions anrs/anrs-impl/src/main/cpp/ndk-crash.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Java
#ifndef NDK_CRASH_H
#define NDK_CRASH_H

// Call this method to register native crash handling
bool native_crash_handler_init();
// Call this method to de-register native crash handling
bool native_crash_handler_fini();

#endif // NDK_CRASH_H
Loading

0 comments on commit 886d06e

Please sign in to comment.