Skip to content

About

Open-source Android SDK for secure IPC between Sākṣī ecosystem-compatible apps.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Sakshi SDK 📱⚡

Release API License

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.


💡 What is Sakshi SDK?

Sakshi SDK provides clean, idiomatic, coroutine-powered Kotlin public APIs for:

  1. Client Developers (Camera, Audio, Viewer): Send photos, manage incremental audio/video sync, ping Vault, query recording status, and observe file copy completion acknowledgements (CopyDoneAck).
  2. Vault Developers: Implement remote AIDL service binding and send structured responses, sync progress, copy completion acknowledgements, and error events using VaultResponder.

🏗️ Dual-Side Architecture & Responsibilities

+------------------------------------+                 +-----------------------------------+
|       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    |  |
+------------------------------------+                 +-----------------------------------+

SDK Responsibilities

  • Inter-process communication bridge with Vault service.
  • Hiding Binder proxy transactions, AIDL interfaces, and death recipient handling.
  • Providing SakshiClient for client applications and VaultResponder for Vault applications.

Vault Responsibilities (External App)

  • 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.

📦 Installation & Gradle Integration

Add the dependency to your app or library module's build.gradle.kts:

Maven Central

repositories {
    mavenCentral()
}

dependencies {
    implementation("io.github.rajnishkmehta.sakshi:sakshi-sdk:1.0.0-RC.3")
}

GitHub Packages

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.


🚀 Quick Start Examples

A. Client App Example (Camera / Audio)

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()
}

B. Vault App Example (Service Side)

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
}

📄 Documentation

Detailed technical specifications are available in the /docs directory:


📄 License

This project is licensed under the LICENSE.

About

Open-source Android SDK for secure IPC between Sākṣī ecosystem-compatible apps.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages