Sakshi SDK (rajnishkmehta.sakshi.sdk) is a lightweight, headless Android client and IPC library designed for local-first inter-process communication between trusted client applications (such as Sakshi Camera or Sakshi Audio) and the Sakshi Vault application.
Warning
Early Beta: The SDK supports photo functionality and incremental audio/video synchronization. APIs and behavior may change in future releases.
Sakshi SDK provides clean, idiomatic, coroutine-powered Kotlin public APIs for:
- Client Developers (Camera, Audio, Viewer): Send photos, manage incremental audio/video sync, ping Vault, query recording status, and observe file copy completion acknowledgements (
CopyDoneAck). - Vault Developers: Implement remote AIDL service binding and send structured responses, sync progress, copy completion acknowledgements, and error events using
VaultResponder.
+------------------------------------+ +-----------------------------------+
| Client Application | | Sakshi Vault App |
| (e.g., Camera / Audio / Viewer) | | (External Service) |
| | | |
| +------------------------------+ | Android IPC | +-----------------------------+ |
| | SakshiClient | | <=============> | | Incremental Copy Engine | |
| +------------------------------+ | (AIDL/Binder) | | VaultResponder Helper | |
| (Copy Files & Video Sync) | | | Media Records Database | |
+------------------------------------+ +-----------------------------------+
- Inter-process communication bridge with Vault service.
- Hiding Binder proxy transactions, AIDL interfaces, and death recipient handling.
- Providing
SakshiClientfor client applications andVaultResponderfor Vault applications.
- File storage, incremental byte copying, and persistence.
- Interval timers, queue scheduling, and non-overlapping copy pass execution.
- Emitting
CopyDoneAck(including File ID, optional original source URI, and total copied bytes) upon completing file transfers.
Add the dependency to your app or library module's build.gradle.kts:
repositories {
mavenCentral()
}
dependencies {
implementation("io.github.rajnishkmehta.sakshi:sakshi-sdk:1.0.0-RC.3")
}repositories {
maven {
url = uri("https://maven.pkg.github.com/RajnishKMehta/sakshi-sdk")
credentials {
username = providers.gradleProperty("gpr.user").orNull ?: System.getenv("GITHUB_ACTOR")
password = providers.gradleProperty("gpr.key").orNull ?: System.getenv("GITHUB_TOKEN")
}
}
}
dependencies {
implementation("io.github.rajnishkmehta.sakshi:sakshi-sdk:1.0.0-RC.3")
}Note: GitHub Packages requires authentication using a Personal Access Token (PAT), even for many public packages.
import rajnishkmehta.sakshi.sdk.api.SakshiClient
import rajnishkmehta.sakshi.sdk.api.models.*
val client = SakshiClient.create(context)
// 1. Copy File (returns SakshiResult<CopyDoneAck>)
coroutineScope.launch {
val result = client.copyFile(FileCopyRequest(fileId = "photo_001", uri = photoUri, mediaType = "PHOTO", fileExtension = "jpg"))
when (result) {
is SakshiResult.Success -> {
val ack = result.data
println("File Copied! File ID: ${ack.fileId}, Copied Bytes: ${ack.totalCopiedBytes}")
}
is SakshiResult.Failure -> {
println("File ingestion failed: ${result.error.message}")
}
}
}
// 2. Start AVSync (returns Flow<SakshiResult<AVSyncStatus>>)
coroutineScope.launch {
client.startAVSync(AVSyncRequest(fileId = "rec_999", uri = avUri, mediaType = "VIDEO", fileExtension = "mp4")).collect { result ->
when (result) {
is SakshiResult.Success -> {
val status = result.data
println("Sync State: ${status.state}, Copied Bytes: ${status.lastCopiedOffsetBytes}")
}
is SakshiResult.Failure -> {
println("Sync Error: ${result.error.message}")
}
}
}
}
// 3. Pause & Resume AVSync
coroutineScope.launch {
client.pauseAVSync("rec_999") // Pauses ongoing sync
// ... later ...
client.resumeAVSync("rec_999") // Resumes sync
}
// 4. Stop AVSync and Receive Copy Completion Acknowledgement
coroutineScope.launch {
val result: SakshiResult<CopyDoneAck> = client.stopAVSync("rec_999")
when (result) {
is SakshiResult.Success -> {
val ack = result.data
println("Copy Completed! File ID: ${ack.fileId}, Original URI: ${ack.originalUri}, Copied Bytes: ${ack.totalCopiedBytes}")
}
is SakshiResult.Failure -> {
println("Stop AVSync error: ${result.error.message}")
}
}
}
// 5. Retrieve Thumbnail & Media URI template
coroutineScope.launch {
val thumbnailResult = client.getThumbnail() // content://<authority>/media/{mediaType}/thumbnail/{fileId}
val mediaResult = client.getMedia("photo_001")
if (mediaResult is SakshiResult.Success) {
val details = mediaResult.data
// details.contentUri => content://<authority>/media/photo/photo_001
// details.fileExtension => jpg
}
}
// 6. List Media
coroutineScope.launch {
val result = client.listMedia()
// To get only files scheduled for deletion, use client.listDeletedMedia()
if (result is SakshiResult.Success) {
println("Media JSON: ${result.data}")
}
}
// 7. Disconnect in lifecycle cleanup
override fun onDestroy() {
super.onDestroy()
client.disconnect()
}import android.app.Service
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.IBinder
import rajnishkmehta.sakshi.sdk.api.vault.VaultResponder
import rajnishkmehta.sakshi.sdk.api.models.CopyDoneAck
import rajnishkmehta.sakshi.sdk.internal.ipc.ISakshiVaultCallback
import rajnishkmehta.sakshi.sdk.internal.ipc.ISakshiVaultService
class SakshiVaultServiceBinder : ISakshiVaultService.Stub() {
override fun startAVSync(avSyncBundle: Bundle, callback: ISakshiVaultCallback) {
val fileId = avSyncBundle.getString("file_id", "")
val sourceUriStr = avSyncBundle.getString("uri", "")
// Vault copies bytes incrementally...
val totalCopied = performVaultCopy(fileId)
// Vault sends CopyDoneAck back to Client app
VaultResponder.sendCopyDone(
callback,
CopyDoneAck(
fileId = fileId,
originalUri = if (sourceUriStr.isNullOrEmpty()) null else Uri.parse(sourceUriStr),
totalCopiedBytes = totalCopied
)
)
}
// ...
}
class SakshiVaultRemoteService : Service() {
private var binder: SakshiVaultServiceBinder? = null
override fun onCreate() {
super.onCreate()
binder = SakshiVaultServiceBinder()
}
override fun onBind(intent: Intent?): IBinder? = binder
}Detailed technical specifications are available in the /docs directory:
- 📑 System Architecture
- 📑 Client API Usage
- 📑 Vault Integration Guide
- 📑 AIDL IPC Specification
- 📑 Development Guide