package link.roadio.riff.shared.config import android.content.ContentProvider import android.content.ContentValues import android.database.Cursor import android.net.Uri import android.os.Binder import android.os.Bundle import android.os.Process import link.roadio.riff.storage.root.ApplicationDataStorage /** One Indexer-process configuration authority, available before catalog/service startup. */ class ConfigurationSnapshotProvider : ContentProvider() { override fun onCreate(): Boolean = true override fun call( method: String, arg: String?, extras: Bundle?, ): Bundle { check(Binder.getCallingUid() == Process.myUid()) val app = checkNotNull(context) if (method == "reset-internal") { check( android.app.Application .getProcessName() .endsWith(":indexer"), ) val fields = checkNotNull(extras) val expected = link.roadio.riff.storage.root.ApplicationDataSelection( link.roadio.riff.storage.root.ApplicationDataLocation .valueOf(checkNotNull(fields.getString("location"))), fields.getString("volume"), ) val result = link.roadio.riff.userstate.android.IndexerUserState .resetToInternal(app, expected) return Bundle().apply { putBoolean("accepted", result.accepted) putBoolean("completed", result.completed) putInt("ownerPid", Process.myPid()) } } check(ApplicationDataStorage(app).prepareForStartup()) { "Selected storage is unavailable" } if (method == "user-state") { val confirmed = extras?.keySet().orEmpty().filter { it.startsWith("confirmed.") }.associate { key -> val scope = key.removePrefix("confirmed.").split(':') require(scope.size == 2) (scope[0].toInt() to scope[1].toInt()) to checkNotNull(extras).getLong(key) } return link.roadio.riff.userstate.android.IndexerUserState.open( app, extras ?.takeIf { it.containsKey("writer") }?.getLong("writer"), confirmed, extras?.takeIf { it.containsKey("root") }?.getLong("root"), ) } if (method == "user-state-drain") { check( link.roadio.riff.userstate.android.IndexerUserState .drain(), ) return link.roadio.riff.userstate.android.IndexerUserState .open(app) } require(method == "snapshot") val loaded = AndroidProjectConfiguration.load(app) return Bundle().apply { ConfigurationHandoff.put(this, AndroidProjectConfiguration.aggregateText().takeIf { loaded.problem == null }) putString("problem", loaded.problem?.name) } } override fun query( uri: Uri, projection: Array?, selection: String?, selectionArgs: Array?, sortOrder: String?, ): Cursor? = null override fun getType(uri: Uri): String? = null override fun insert( uri: Uri, values: ContentValues?, ): Uri? = throw UnsupportedOperationException() override fun delete( uri: Uri, selection: String?, selectionArgs: Array?, ): Int = throw UnsupportedOperationException() override fun update( uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array?, ): Int = throw UnsupportedOperationException() }