Skip to content

Android integration

Android · supported

Supported baseline: Android API 23+, Java 11 bytecode. The supplied AAR contains Kotlin/Java wrappers, consumer R8 rules, and native libraries for arm64-v8a, armeabi-v7a, x86, and x86_64.

Your onboarding instructions provide a private Maven repository or a local repository directory.

settings.gradle.kts
dependencyResolutionManagement {
repositories {
maven {
url = uri(requireNotNull(System.getenv("P2PSDK_MAVEN_URL")))
credentials {
username = System.getenv("P2PSDK_MAVEN_USER")
password = System.getenv("P2PSDK_MAVEN_TOKEN")
}
}
google()
mavenCentral()
}
}
app/build.gradle.kts
dependencies {
implementation("com.p2psdk:p2p-sdk-android:1.0.0")
}

For a local onboarding repository, replace the private repository block with maven { url = uri("../p2psdk-maven") }. Do not add mavenLocal() to production builds unless your release process intentionally publishes there.

The consent URL, revoke URL, version, and public verification key are compiled into the delivered AAR. Application code cannot replace them at runtime.

Every integration needs:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Background mode additionally requires:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application>
<service
android:name="com.p2psdk.P2PSdkBackgroundService"
android:exported="false"
android:foregroundServiceType="specialUse">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="Maintains the user-visible P2P SDK connection while app UI is minimized." />
</service>
</application>

Review Google Play foreground-service declarations when distributing an app that enables this mode. On Android 13+, request notification permission where your product flow requires it.

Create from a visible Activity whenever consent may need to be shown. Own the accepted instance in an application-level controller or service, not a short-lived composable.

class SdkController(
private val scope: CoroutineScope,
) {
private var sdk: P2PSdkKt? = null
suspend fun start(activity: Activity, apiKey: String) {
when (val result = P2PSdkKt.create(activity, apiKey) {
debug(BuildConfig.DEBUG)
operationDispatcher(Dispatchers.IO)
callbackDispatcher(Dispatchers.Main.immediate)
onConnect { Log.i("P2PSDK", "connected") }
onDisconnect { reason -> Log.w("P2PSDK", "disconnected: $reason") }
callbackErrorHandler { callback, error ->
Log.e("P2PSDK", "callback failed: $callback", error)
}
}) {
is P2PSdkKt.InitResult.Accepted -> {
sdk = result.sdk
scope.launch {
result.sdk.events.collect { event ->
// Drive product state from lifecycle events.
}
}
check(result.sdk.connect()) { "P2PSDK supervisor did not start" }
}
else -> Log.i("P2PSDK", "consent result: $result")
}
}
fun close() {
sdk?.close()
sdk = null
}
}

connect() is a suspend function in the Kotlin wrapper. A successful return does not mean online; wait for LifecycleEvent.Connected or observe the wrapper state.

public final class SdkController implements AutoCloseable {
private P2PSdk sdk;
public void start(Activity activity, String apiKey) {
P2PSdk.builder(activity, apiKey)
.debug(BuildConfig.DEBUG)
.callbackExecutor(activity.getMainExecutor())
.onConnect(() -> Log.i("P2PSDK", "connected"))
.onDisconnect(reason -> Log.w("P2PSDK", "disconnected: " + reason))
.callbackErrorHandler((callback, error) ->
Log.e("P2PSDK", "callback failed: " + callback, error))
.create(result -> {
if (!result.isAccepted()) {
Log.i("P2PSDK", "consent result: " + result.status());
return;
}
sdk = result.sdk();
if (!sdk.connect()) {
sdk.close();
sdk = null;
throw new IllegalStateException("P2PSDK supervisor did not start");
}
});
}
@Override public void close() {
if (sdk != null) {
sdk.close();
sdk = null;
}
}
}

Background mode is disabled by default. Enable it only when product policy and store review allow an ongoing foreground service.

P2PSdk.BackgroundNotificationOptions notification =
P2PSdk.BackgroundNotificationOptions.builder()
.channelId("p2psdk_connection")
.channelName("SDK connection")
.title("Connection active")
.text("Background network participation is active")
.smallIconResId(R.drawable.ic_stat_p2psdk)
.build();
P2PSdk.builder(activity, apiKey)
.enableBackgroundMode(true)
.backgroundNotificationOptions(notification)
.create(callback);

Call connect() from an OS-allowed foreground path. Android 12+ can reject background foreground-service starts. The SDK does not survive force-stop, add boot autostart, or keep the device awake.

sdk.revokeConsent(result -> {
if (result.isRevoked()) {
sdk = null; // The successful revoke closed the instance.
} else {
Log.e("P2PSDK", "revoke failed: " + result.status(), result.error());
}
});

Use disconnect() to pause a reusable instance and close() for terminal release. Do not call methods after close() or successful revoke.

  • Test phone and tablet consent layouts, rotation, cancel, decline, accept, and an invalid/expired decision.
  • Confirm callbacks arrive on the configured executor and exceptions reach the error handler.
  • Test minimize, screen off, denied notification permission, and rejected service start.
  • Verify close() removes callbacks, stops any SDK foreground-service session, and frees the native handle.