This project provides Android bindings for the C2PA (Coalition for Content Provenance and Authenticity) libraries. It wraps the C2PA Rust implementation (c2pa-rs) using its C API bindings to provide native Android support via an AAR library.
C2PA Android offers:
- Android support via AAR library with Kotlin APIs
- Comprehensive support for C2PA manifest reading, validation, and creation
- Support for callback-based signing and web service signers
- Stream-based operations for efficient memory usage
- Pre-built binaries for fast development
- Hardware security integration (Android Keystore, StrongBox)
# Clone the repository
git clone https://github.com/contentauth/c2pa-android.git
cd c2pa-android
# Build the library
make library
# Run the test app
make run-test-app
/library
- Android library module with C2PA Kotlin APIs and JNI bindings/src/main/kotlin
- Kotlin wrapper classes (C2PA.kt, HardwareSecurity.kt)/src/main/jni
- JNI C implementation (c2pa_jni.c) and C2PA headers/src/androidTest
- Instrumented tests for the library
/test-app
- Test application with comprehensive test UI for running all C2PA tests/example-app
- Example Android application (placeholder for future camera app)/Makefile
- Build system commands for downloading binaries and building/.github/workflows
- GitHub Actions for CI/CD with integrated test coverage
- Android API level 28+ (Android 9.0+)
- Android Studio Hedgehog (2023.1.1) or newer
- JDK 17
- JDK 17 (for Android builds)
- Android SDK (for Android builds)
- Android NDK (any recent version - see note below)
- Make
The project will use your default NDK version. If you need to use a specific NDK version, add it to your local.properties
file:
ndk.version=29.0.13599879
You can add C2PA Android as a Gradle dependency:
dependencies {
implementation "org.contentauth:c2pa:1.0.0"
}
Make sure to add the GitHub Packages repository to your project:
// In your root build.gradle
allprojects {
repositories {
google()
mavenCentral()
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/contentauth/c2pa-android")
credentials {
username = System.getenv("GITHUB_USER") ?: project.findProperty("GITHUB_USER")
password = System.getenv("GITHUB_TOKEN") ?: project.findProperty("GITHUB_TOKEN")
}
}
}
}
For local development without using a released version:
- Build the library with
make library
- The AAR will be available at
library/build/outputs/aar/c2pa-release.aar
- Add the AAR to your project:
// In app/build.gradle
dependencies {
implementation files('path/to/c2pa-release.aar')
// Also add required dependencies
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
implementation 'net.java.dev.jna:jna:5.13.0@aar'
}
import org.contentauth.c2pa.*
// Read a manifest from a file
try {
val manifest = C2PA.readFile("/path/to/image.jpg")
println("Manifest: $manifest")
} catch (e: C2PAError) {
println("Error reading manifest: $e")
}
// Read from a stream
val imageStream = FileStream(File("/path/to/image.jpg"), FileStream.Mode.READ)
try {
val reader = Reader.fromStream("image/jpeg", imageStream)
val manifestJson = reader.json()
println("Manifest JSON: $manifestJson")
reader.close()
} finally {
imageStream.close()
}
// Sign with built-in signer
val signerInfo = SignerInfo(
algorithm = SigningAlgorithm.ES256,
certificatePEM = certsPem,
privateKeyPEM = privateKeyPem,
tsaURL = "https://timestamp.server.com"
)
val manifest = """{
"claim_generator": "my_app/1.0",
"assertions": [
{"label": "c2pa.actions", "data": {"actions": [{"action": "c2pa.created"}]}}
]
}"""
try {
C2PA.signFile(
sourcePath = "/path/to/input.jpg",
destPath = "/path/to/output.jpg",
manifest = manifest,
signerInfo = signerInfo
)
} catch (e: C2PAError) {
println("Signing failed: $e")
}
// Create a callback signer for custom signing implementations
val callbackSigner = Signer.withCallback(
algorithm = SigningAlgorithm.ES256,
certificateChainPEM = certsPem,
tsaURL = null
) { data ->
// Custom signing logic here
myCustomSigningFunction(data)
}
// Use with Builder API
val builder = Builder.fromJson(manifestJson)
val sourceStream = FileStream(File("/path/to/input.jpg"), FileStream.Mode.READ)
val destStream = FileStream(File("/path/to/output.jpg"), FileStream.Mode.WRITE)
try {
val result = builder.sign("image/jpeg", sourceStream, destStream, callbackSigner)
println("Signed successfully, size: ${result.size}")
} finally {
builder.close()
sourceStream.close()
destStream.close()
callbackSigner.close()
}
-
Clone this repository:
git clone https://github.com/contentauth/c2pa-android.git cd c2pa-android
-
Set up the required dependencies:
-
Set up JDK 17:
# macOS with Homebrew: brew install openjdk@17
-
Set up Android SDK
-
Set up environment variables (add to your shell profile):
export JAVA_HOME=$(/usr/libexec/java_home -v 17) export ANDROID_HOME=$HOME/Library/Android/sdk
-
-
(Optional) Update C2PA version:
- Edit
library/gradle.properties
and changec2paVersion
- See available versions at https://github.com/contentauth/c2pa-rs/releases
- Edit
-
Build the library:
# Complete build: setup, download binaries, and build AAR make library
-
Check built outputs:
# Android Library AAR ls -la library/build/outputs/aar/ # Run test app with comprehensive test suite make run-test-app
The project includes a comprehensive Makefile with various targets:
setup
- Create necessary directoriesdownload-binaries
- Download pre-built binaries from GitHub releaseslibrary
- Complete library build: setup, download, and build AARlibrary-gradle
- Run Gradle build to generate AAR filetests
- Run library instrumented tests (requires device/emulator)coverage
- Generate instrumented test coverage reportrun-test-app
- Install and run the test apprun-example-app
- Install and run the example app
Note: Both test-app and example-app must be built from the root directory. They cannot be built independently as they depend on the library module.
publish
- Publish Android library to GitHub packagesall
- Complete library build (default, same as library)clean
- Remove build artifactshelp
- Show all available targets
This project uses GitHub Actions for continuous integration and release management:
The release process is automated through a single workflow:
-
Start a Release:
- Trigger the "Release" workflow from the Actions tab
- Enter the version number (e.g.,
v1.0.0
)
-
Automated Build and Release:
- Downloads pre-built C2PA binaries
- Builds the Android AAR package
- Creates a GitHub release with the specified version
- Attaches the Android AAR artifact
- Publishes documentation for integration
A comprehensive test application that runs all C2PA functionality tests with a visual UI:
- Run all 33 tests covering manifest reading, writing, signing, and validation
- Visual test results with success/failure indicators
- Detailed error messages for debugging
- Access via
make run-test-app
or open in Android Studio
A placeholder application for future camera integration demonstrating C2PA usage in real-world scenarios.
- C2PA - Main entry point for static operations (reading files, signing)
- Reader - For reading and validating C2PA manifests from streams
- Builder - For creating and signing new C2PA manifests
- Signer - For signing manifests with various key types and methods
- Stream - Base class for stream operations
- FileStream - File-based stream implementation
- MemoryStream - Memory-based stream implementation
- Direct Signing - Using private key and certificate PEM strings
- Callback Signing - Custom signing implementations (HSM, cloud KMS, etc.)
- Web Service Signing - Remote signing via HTTP endpoints
- Android Keystore - Hardware-backed key storage (future)
The project includes comprehensive instrumented tests that validate the C2PA functionality through the JNI bridge:
Run instrumented tests on a connected device or emulator:
make tests
Generate test coverage reports:
make coverage
Coverage reports will be available at:
- HTML:
library/build/reports/jacoco/jacocoInstrumentedTestReport/html/index.html
- XML:
library/build/reports/jacoco/jacocoInstrumentedTestReport/jacocoInstrumentedTestReport.xml
The project uses JaCoCo for coverage reporting. Coverage reports are generated during CI builds and stored as artifacts.
The Android library uses JNI (Java Native Interface) with enhanced memory safety:
- C API headers:
library/src/main/jni/c2pa.h
- JNI implementation:
library/src/main/jni/c2pa_jni.c
(thread-safe, proper cleanup) - Kotlin wrapper:
library/src/main/kotlin/org/contentauth/c2pa/C2PA.kt
- Hardware security:
library/src/main/kotlin/org/contentauth/c2pa/HardwareSecurity.kt
This project is licensed under the Apache License, Version 2.0 and MIT - see the LICENSE-APACHE and LICENSE-MIT files for details.