Android

Introduction

The Android NDK is a set of tools for building native C++ applications for Android. BugSplat recommends using Crashpad to handle native crashes in Android NDK applications. Before integrating your application with BugSplat, make sure to review the Getting Started resources and complete the simple startup tasks listed below.

Building Crashpad

BugSplat leverages Crashpad to provide crash reporting for Android NDK applications. This tutorial provides a quick overview of how to build Crashpad. For an in depth guide that discusses how to build Crashpad, please see this article.
To build crashpad you'll first need to download a copy of the Chromium depot_tools. Once you have downloaded depot_tools, you'll need to add the parent folder to your system's PATH environment variable. After adding depot_tools to your systems PATH, run the following commands to download the Crashpad repository:
1
mkdir ~/crashpad
2
cd ~/crashpad
3
fetch crashpad
4
cd crashpad
Copied!
Next you'll need to generate Crashpad build configurations for each Android ABI your application supports. You can view the source of gyp_crashpad_android.py to see a list of supported ABIs. Take note of the --api-level value you use here as you'll need this information when configuring your project. The following command will create a Crashpad Android config for the x86 ABI:
1
ninja -C out/android_x86_api21/out/Debug all
Copied!

Integrating Crashpad

Once Crashpad has been built you'll need to add the relevant include directories to your project. Copy all of the Crashpad .h files to the directory app/src/main/cpp/crashpad/include. Next, add the include directories your project's CMakeLists.txt file:
1
# Crashpad Headers
2
include_directories(${PROJECT_SOURCE_DIR}/crashpad/include/ ${PROJECT_SOURCE_DIR}/crashpad/include/third_party/mini_chromium/mini_chromium/)
Copied!
After adding the include directories, you'll need to add the Crashpad static libraries to your project. You'll need to add a set of Crashpad libraries for each ABI your application supports. From the /crashpad/out/Debug/{{ABI}} directory you'll want to copy the client, util and third_party/mini_chromium/mini_chromium/base folders to app/src/main/cpp/crashpad/lib/{{ABI}}.
BugSplat Android Libs Crashpad Folders
Once all of the Crashpad libraries have been copied to your project directory, add the following to your project's CMakeLists.txt file to link the Crashpad libraries:
1
# Crashpad Libraries
2
add_library(crashpad_client STATIC IMPORTED)
3
set_property(TARGET crashpad_client PROPERTY IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/crashpad/lib/${ANDROID_ABI}/client/libcrashpad_client.a)
4
โ€‹
5
add_library(crashpad_util STATIC IMPORTED)
6
set_property(TARGET crashpad_util PROPERTY IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/crashpad/lib/${ANDROID_ABI}/util/libcrashpad_util.a)
7
โ€‹
8
add_library(base STATIC IMPORTED)
9
set_property(TARGET base PROPERTY IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/crashpad/lib/${ANDROID_ABI}/base/libbase.a)
10
โ€‹
11
# Specifies the target library
12
target_link_libraries(
13
native-lib
14
crashpad_client
15
crashpad_util
16
base
17
)
Copied!
Additionally, you'll need ship a copy of the crashpad_handler executable with your application. To do this, you'll need to rename crashpad_handler to libcrashpad_handler.so otherwise it will be ignored by the APK bundler. Copy libcrashpad_handler.so to app/src/main/cpp/crashpad/lib/{{ABI}} for each Crashpad ABI architecture. Add the following snippet to CMakeLists.txt so that libcrashpad_handler.so is copied to your device and made available at runtime:
1
# Crashpad Handler
2
add_library(crashpad_handler SHARED IMPORTED)
3
set_property(TARGET crashpad_handler PROPERTY IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/crashpad/lib/${ANDROID_ABI}/libcrashpad_handler.so)
Copied!

Configuring Crashpad

To enable Crashpad in your application you'll need to configure the Crashpad handler with your BugSplat database, application name and application version. The following snippet will configure the Crashpad handler:
1
#include <jni.h>
2
#include <string>
3
#include <unistd.h>
4
#include "client/crashpad_client.h"
5
#include "client/crash_report_database.h"
6
#include "client/settings.h"
7
โ€‹
8
using namespace base;
9
using namespace crashpad;
10
using namespace std;
11
โ€‹
12
extern "C" JNIEXPORT jboolean JNICALL
13
Java_com_example_androidcrasher_MainActivity_initializeCrashpad(
14
JNIEnv* env,
15
jobject /* this */
16
) {
17
โ€‹
18
string dataDir = "/data/data/com.example.androidcrasher";
19
โ€‹
20
// Crashpad file paths
21
FilePath handler(dataDir + "/lib/libcrashpad_handler.so");
22
FilePath reportsDir(dataDir + "/crashpad");
23
FilePath metricsDir(dataDir + "/crashpad");
24
โ€‹
25
// Crashpad upload URL for BugSplat database
26
string url = "http://{{database}}.bugsplat.com/post/bp/crash/crashpad.php";
27
โ€‹
28
// Crashpad annotations
29
map<string, string> annotations;
30
annotations["format"] = "minidump"; // Required: Crashpad setting to save crash as a minidump
31
annotations["database"] = "{{database}}"; // Required: BugSplat database
32
annotations["product"] = "{{appName}}"; // Required: BugSplat appName
33
annotations["version"] = "{{appVersion}}"; // Required: BugSplat appVersion
34
annotations["key"] = "Key"; // Optional: BugSplat key field
35
annotations["user"] = "[email protected]"; // Optional: BugSplat user email
36
annotations["list_annotations"] = "Sample comment"; // Optional: BugSplat crash description
37
โ€‹
38
// Crashpad arguments
39
vector<string> arguments;
40
arguments.push_back("--no-rate-limit");
41
โ€‹
42
// Crashpad local database
43
unique_ptr<CrashReportDatabase> crashReportDatabase = CrashReportDatabase::Initialize(reportsDir);
44
if (crashReportDatabase == NULL) return false;
45
โ€‹
46
// Enable automated crash uploads
47
Settings *settings = crashReportDatabase->GetSettings();
48
if (settings == NULL) return false;
49
settings->SetUploadsEnabled(true);
50
โ€‹
51
// File paths of attachments to be uploaded with the minidump file at crash time - default bundle limit is 20MB
52
vector<FilePath> attachments;
53
FilePath attachment(dataDir + "/files/attachment.txt");
54
attachments.push_back(attachment);
55
โ€‹
56
// Start Crashpad crash handler
57
static CrashpadClient *client = new CrashpadClient();
58
bool status = client->StartHandlerAtCrash(handler, reportsDir, metricsDir, url, annotations, arguments, attachments);
59
return status;
60
}
Copied!
Be sure to update the values for database, appName, appVersion and packageNameto values specific to your application. Next add a call to initializeCrashpad at the entry point of your application. The following example defines initializeCrashpad as a native C++ function and calls it using Kotlin from MainActivity:
1
// MainActivity entry point
2
override fun onCreate(savedInstanceState: Bundle?) {
3
super.onCreate(savedInstanceState)
4
setContentView(R.layout.activity_main)
5
โ€‹
6
// Example of a call to a native method
7
sample_text.text = if (initializeCrashpad()) "initialized" else "fail"
8
}
9
โ€‹
10
// Declare native C++ method
11
external fun initializeCrashpad(): Boolean
Copied!
To test the attachments feature, use Java or Kotlin to write a text file. The following is a Kotlin snippet that creates a file attachment.txt:
1
// MainActivity entry point
2
override fun onCreate(savedInstanceState: Bundle?) {
3
writeLogFile()
4
...
5
}
6
โ€‹
7
private fun writeLogFile() {
8
try {
9
val outputStreamWriter = OutputStreamWriter(
10
applicationContext.openFileOutput(
11
"attachment.txt",
12
Context.MODE_PRIVATE
13
)
14
)
15
outputStreamWriter.write("BugSplat rocks!")
16
outputStreamWriter.close()
17
} catch (e: IOException) {
18
Log.e("Exception", "File write failed: " + e.toString())
19
}
20
}
Copied!

Symbols

To ensure that your crash reports contain function names and line numbers you'll need to add an option to your build configuration that prevents symbolic information from being stripped. To prevent symbols from being stripped, add the following to your build.gradle file:
1
android {
2
...
3
// Add this directly under the android section in app/src/build.gradle
4
packagingOptions{
5
doNotStrip "*/armeabi/*.so"
6
doNotStrip "*/armeabi-v7a/*.so"
7
doNotStrip "*/x86/*.so"
8
doNotStrip "*/x86_64/*.so"
9
}
10
}
Copied!
Next, you will need to generate and upload .sym files to BugSplat. To generate symbols for your Android NDK library you will need to build the Breakpad tool dump_syms on a Linux machine. For more information about building Breakpad tools on Linux please see this document.
Once you've built dump_syms on a Linux system, run dump_syms on a Linux machine passing it the path to your Android library:
1
./dump_syms path/to/app/build/intermediates/merged_native_libs/debug/out/lib/x86/my-lib.so > /my-lib.so.sym
Copied!
You can also run the Linux version of dump_syms using compatible Linux emulator on macOS or Windows. Examples of how to run a Linux build of dump_syms on macOS and Windows can be found in the tools section of the AndroidCrasher repository.
If you're developing on an macOS or Linux system, build the Breakpad tool symupload on your local system. Upload the generated .sym file by running symupload. Be sure to replace the {{database}}, {{application}} and {{version}} with the values you used in the Configuring Crashpad section:
1
symupload "/path/to/my-lib.so.sym" "https://{{database}}.bugsplat.com/post/bp/symbol/breakpadsymbols.php?appName={{application}}&appVer={{version}}"
Copied!
If you're developing on Windows, symupload will not upload your Android .sym files. As an alternative, the AndroidCrasher tools folder provides and example PowerShell script that will upload .sym files to BugSplat.
After each release build you'll need to generate and upload .sym files making sure to increment the version number each time. The version number from the Configuring Crashpad section must match the version number in your upload URL.

Generating a Crash Report

Force a crash in your application after Crashpad has been initialized:
1
*(volatile int *)0 = 0;
Copied!
After you've submitted a crash report, navigate to the Crashes page. Click the link in the ID column to see the details of your crash report. The following image is from our sample AndroidCrasher application:
BugSplat Android NDK Crash
Last modified 4mo ago