API reference

PakManager Object

Read-only access to mounted .pak archives via the pak:// URI scheme.
A pak is a plain ZIP file whose root contains a pak.manifest.json declaring schemaVersion, plus arbitrary entries (glXF, glTF, textures, audio, splat, etc.). Apps download a .pak to writable storage themselves (e.g. via NetworkedAssetLoader or any HTTP client) and then call PakManager.mount to make its contents addressable as pak://<name>/<path>.
Once mounted, every component URI that takes a pak:// value (Audio, Mesh, GLXF, particles, etc.) loads transparently from the pak. The lower-level choke points in C++ (androidFOpen / the native asset reader) and in Kotlin (PakManager.openInputStream) handle scheme dispatch — so call sites that already accept apk:// work for pak:// without further code.
Example:
PakManager.mount("content", File(context.filesDir, "content.pak"))
glxfManager.inflateGLXF(
    Uri.parse("pak://content/scenes/Composition.glxf"),
    rootEntity, "main")

Multiple paks can be mounted concurrently under distinct names. The same .pak file can be mounted under different names — useful when an authored glXF references children via the #qualify shorthand.
Intentionally NOT annotated with @DoNotStripNative — apps that never reference any PakManager.* symbol (e.g. media-only apps that only ever load apk:// and file://) let R8 strip the entire class, the EntryRegion holder, and every external fun from their DEX. The native side (registerPakManager in JavaAPIs_Pak.cpp) is defensive: if FindClass fails because R8 stripped the class, registration silently no-ops instead of crashing JNI_OnLoad.

Signature

object PakManager

Methods

exists ( uri )
Returns true if PakManager.exists resolves to a real entry in a mounted pak.
Signature
fun exists(uri: String): Boolean
Parameters
uri: String
Returns
Boolean
exists ( context , uri )
Existence check that mirrors PakManager.openInputStream's scheme dispatch.
Signature
fun exists(context: Context, uri: Uri): Boolean
Parameters
context: Context
uri: Uri
Returns
Boolean
getEntryRegion ( uri )
Returns the on-disk location of an entry, or null if the URI is malformed, the mount is unknown, or the entry is missing. O(1) hash lookup — no I/O.
Signature
fun getEntryRegion(uri: String): PakManager.EntryRegion?
Parameters
uri: String
Returns
PakManager.EntryRegion?
hasSignatureKey ()
Returns true if a non-empty signature key is currently registered.
Signature
fun hasSignatureKey(): Boolean
Returns
Boolean
isMounted ( name )
Signature
fun isMounted(name: String): Boolean
Parameters
name: String
Returns
Boolean
mapWhole ( uri )
Returns a read-only MappedByteBuffer over the entry's bytes via FileChannel.map(). Returns null for DEFLATE entries (no contiguous mmap region exists), missing entries, or unknown mounts — callers fall back to PakManager.readWhole in those cases.
The returned buffer's lifetime is owned by the JVM: when it is GC'd, FileChannel's internal cleaner unmaps the region. No app-side Cleaner is needed (this works on minSdk 21 unlike java.lang.ref.Cleaner which requires API 24).
Hot consumers (200+ MiB splats, multi-MiB OGG audio, KTX/Basis textures) should read directly from this buffer — for splat in particular, this collapses three allocations (native vector, JVM ByteArray, native pinned copy) into zero on the load path.
Signature
fun mapWhole(uri: String): MappedByteBuffer?
Parameters
uri: String
Returns
MappedByteBuffer?
mount ( name , absPath )
Mounts the .pak file at PakManager.mount under PakManager.mount. The file is opened read-only and its central directory is indexed once. The native runtime keeps the file open until PakManager.unmount is called; in-flight reads are lifetime-safe across unmount.
Signature
fun mount(name: String, absPath: String): PakManager.MountStatus
Parameters
name: String
absPath: String
Returns
PakManager.MountStatus PakManager.MountStatus.Ok on success. On failure, returns one of the typed error codes — apps may want to prompt the user to update on PakManager.MountStatus.SchemaTooNew.
mount ( name , file )
Convenience overload that takes a File.
Signature
fun mount(name: String, file: File): PakManager.MountStatus
Parameters
name: String
file: File
Returns
PakManager.MountStatus
openAssetFileDescriptor ( uri )
Returns an AssetFileDescriptor over the entry's bytes — a slice of the .pak file at the entry's offset+length. Returns null for DEFLATE entries (no contiguous file region exists), missing entries, or unknown mounts.
The textbook Android handoff for "play this slice of a packaged file": MediaPlayer, ExoPlayer.FileDataSource, SoundPool, and Bitmap decoders all accept AssetFileDescriptor directly and optimize for it (kernel-side seeks, no userspace copy of the payload).
The caller owns the returned descriptor and must close() it when done. Most media APIs dup the underlying FD so closing the AssetFileDescriptor is safe even while playback continues.
Signature
fun openAssetFileDescriptor(uri: String): AssetFileDescriptor?
Parameters
uri: String
Returns
AssetFileDescriptor?
openInputStream ( context , uri )
Scheme-aware InputStream opener for use by Kotlin asset loaders. Replaces ad-hoc apk:// / file:// switches at call sites so they pick up pak:// for free.
Throws when the scheme is unsupported or the asset cannot be opened — the caller is expected to bracket with try/catch the same way it would for context.assets.open.
Signature
fun openInputStream(context: Context, uri: Uri): InputStream
Parameters
context: Context
uri: Uri
Returns
InputStream
qualify ( pakName , uri )
Rewrites a pak://./<path> shorthand against the supplied PakManager.qualify, returning a fully-qualified pak://<pakName>/<path> URI. Inputs that do not start with pak://./ are returned unchanged. This is the explicit Kotlin counterpart to load-time rewrites the C++ runtime does inside the glXF parser.
Example:
audio.uri = PakManager.qualify("content", "pak://./audio/ping.wav")
// → "pak://content/audio/ping.wav"

Signature
fun qualify(pakName: String, uri: String): String
Parameters
pakName: String
uri: String
Returns
String
readChunk ( uri , dst , dstOffset , srcOffset , count )
Streams up to PakManager.readChunk bytes from the entry at PakManager.readChunk, starting at byte offset PakManager.readChunk, into PakManager.readChunk[PakManager.readChunk..PakManager.readChunk+PakManager.readChunk). Returns the number of bytes actually copied (0 at EOF; fewer than PakManager.readChunk when PakManager.readChunk+PakManager.readChunk would overrun the entry), or -1 on error (unknown URI, missing entry, out-of-bounds destination range, CRC mismatch on the source, or DEFLATE entry — see below).
Currently STORE-mode entries only — chunked reads of compressed entries would need to carry inflate state across calls and aren't implemented yet. Callers that need to stream a DEFLATE entry should fall back to PakManager.readWhole. Most pak content (glTF/.glb, textures, audio, splat) is STORE for the mmap fast path, so this restriction rarely bites.
Use this for large assets (multi-MiB splat / audio) to avoid the readWhole double allocation (native vector + JVM ByteArray copy).
Signature
fun readChunk(uri: String, dst: ByteArray, dstOffset: Int, srcOffset: Long, count: Int): Int
Parameters
uri: String
dst: ByteArray
dstOffset: Int
srcOffset: Long
count: Int
Returns
Int
readWhole ( uri )
Returns the raw bytes of the entry at PakManager.readWhole, or null if the URI is malformed, the mount is unknown, or the entry is missing.
Signature
fun readWhole(uri: String): ByteArray?
Parameters
uri: String
Returns
ByteArray?
setSchemaCeiling ( ceiling )
Sets the runtime BCV ceiling for schemaVersion. Paks whose manifest declares a higher version are rejected at PakManager.mount time. Default is Long.MAX_VALUE (accept everything).
Signature
fun setSchemaCeiling(ceiling: Long)
Parameters
ceiling: Long
setSignatureKey ( key )
Registers an HMAC-SHA256 key for manifest signing. When set, every subsequent PakManager.mount call requires the pak to ship a pak.manifest.sig entry whose contents are the raw 32-byte HMAC over the pak.manifest.json bytes computed with this key. Mounts that fail verification surface as PakManager.MountStatus.SignatureInvalid / PakManager.MountStatus.SignatureMissing / PakManager.MountStatus.SignatureKeyMissing.
Pass an empty array to disable signing checks (the pre-D11 default).
Apps that distribute downloadable content packs typically call this once at startup, before any mount(), with a key derived from the app's signing certificate or a per-app secret stored in NDK strings / a server-side handshake.
Signature
fun setSignatureKey(key: ByteArray)
Parameters
key: ByteArray
unmount ( name )
Unmounts PakManager.unmount. Returns false if no mount was registered under PakManager.unmount.
Signature
fun unmount(name: String): Boolean
Parameters
name: String
Returns
Boolean

Inner Class

EntryRegion Class

Modifiers: final
Location of an entry's data within its mounted .pak file. Returned by PakManager.getEntryRegion so callers can FileChannel.map() the bytes themselves — used by PakManager.mapWhole for the splat / texture / audio zero-copy paths and by AssetFileDescriptor-based media playback.
The class name is referenced by JNI (constructor signature (Ljava/lang/String;JJZ)V) — keep the field order and types in lock-step with JavaAPIs_Pak.cpp.

Signature

data class EntryRegion(val filePath: String, val offset: Long, val size: Long, val isStore: Boolean)

Constructors

EntryRegion ( filePath , offset , size , isStore )
Signature
constructor(filePath: String, offset: Long, size: Long, isStore: Boolean)
Parameters
filePath: String  Absolute path to the .pak file on disk (the same path passed to PakManager.mount).
offset: Long  Byte offset of the entry's data within filePath.
size: Long  Declared uncompressed size, in bytes.
isStore: Boolean  True for STORE-mode entries (safe to slice / mmap directly); false for DEFLATE entries (use PakManager.readWhole instead).
Returns
PakManager.EntryRegion

Properties

filePath : String
[Get]
Absolute path to the .pak file on disk (the same path passed to PakManager.mount).
Signature
val filePath: String
isStore : Boolean
[Get]
True for STORE-mode entries (safe to slice / mmap directly); false for DEFLATE entries (use PakManager.readWhole instead).
Signature
val isStore: Boolean
offset : Long
[Get]
Byte offset of the entry's data within filePath.
Signature
val offset: Long
size : Long
[Get]
Declared uncompressed size, in bytes.
Signature
val size: Long

Inner Enum

MountStatus Enum

Outcome of a PakManager.mount call. Mirrors the native MountStatus enum.

Signature

enum MountStatus : Enum<PakManager.MountStatus> 

Enumeration Constants

MemberDescription
Ok
AlreadyMounted
OpenFailed
ManifestMissing
ManifestInvalid
SchemaTooNew
SchemaTooOld
EntryTooLarge
A single entry's declared uncompressed size exceeds the per-entry cap.
AggregateTooLarge
Sum of declared uncompressed sizes exceeds the per-mount cap.
UnsafeEntryPath
An entry's name fails path-safety (e.g. contains .. or is absolute).
EntryNameTooLong
An entry's name exceeds the registry's filename length budget.
SignatureKeyMissing
The pak ships a pak.manifest.sig but no signature key is registered. Either the pak was built signed for a key the embedder hasn't loaded, or PakManager.setSignatureKey needs to be called before PakManager.mount.
SignatureMissing
A signature key is registered but the pak doesn't ship a pak.manifest.sig. Apps that opt into signing don't accept unsigned content.
SignatureInvalid
HMAC-SHA256 over pak.manifest.json doesn't match the signature shipped in pak.manifest.sig. The pak is corrupt or tampered.
EntryCrcMismatch
A STORE entry's bytes failed CRC-32 verification at mount time (Diff 18). The pak file is corrupt or tampered. Apps may want to delete the file and re-download.
Unknown

Properties

code : Int
[Get]
Signature
val code: Int
isOk : Boolean
[Get]
Signature
val isOk: Boolean