From 8a89a6410d331eb58860cbeda94d2a107968410c Mon Sep 17 00:00:00 2001
From: Electric1447
Date: Tue, 11 Aug 2026 21:48:13 +0300
Subject: [PATCH] Add auto approve scope requests & add corresponding settings
in manager
---
.../org/matrix/vector/daemon/VectorService.kt | 92 ++-----------------
.../vector/daemon/data/ModuleDatabase.kt | 71 +++++++++++++-
.../vector/daemon/data/PreferenceStore.kt | 20 +++-
.../vector/daemon/ipc/ManagerService.kt | 18 ++++
.../vector/daemon/ipc/ModuleAppService.kt | 6 +-
.../vector/manager/demo/FakeManagerService.kt | 14 +++
.../matrix/vector/manager/ipc/DaemonClient.kt | 19 ++++
.../ui/components/PackageActionMenu.kt | 58 +++++++++++-
manager/src/main/res/values-iw/strings.xml | 3 +
manager/src/main/res/values/strings.xml | 3 +
.../matrix/vector/ipc/IManagerService.aidl | 16 +++-
11 files changed, 230 insertions(+), 90 deletions(-)
diff --git a/daemon/src/main/kotlin/org/matrix/vector/daemon/VectorService.kt b/daemon/src/main/kotlin/org/matrix/vector/daemon/VectorService.kt
index 24bdf1f8b..7de80785e 100644
--- a/daemon/src/main/kotlin/org/matrix/vector/daemon/VectorService.kt
+++ b/daemon/src/main/kotlin/org/matrix/vector/daemon/VectorService.kt
@@ -467,72 +467,12 @@ object VectorService : IVectorDaemon.Stub() {
}
when (action) {
- "approve" -> {
- // "system" is the framework and not a package: it names system_server, which belongs
- // to no package and resolves for nobody. The lookup below therefore came back null
- // for every framework prompt, and the approval the user had just given was answered
- // "Package not found" — the request was closed, its notification cancelled, and no
- // row written. The normalisation to user 0 further down could never once have run.
- //
- // Package by package rather than all-or-nothing: the prompt may have been up for an
- // hour and one of the packages it named can have been uninstalled in the meantime,
- // which is no reason to throw away the user's answer about the rest.
- //
- // Only under "approve", because only an approval has to name something real. Deny
- // and the timeout used to be refused here too, so dismissing a prompt for a package
- // that had since been uninstalled told the module "Package not found" when what had
- // actually happened was that the user turned it down.
- val granted =
- scopePackageNames.filter {
- it == "system" || packageManager?.getPackageInfoCompat(it, 0, userId) != null
- }
- if (granted.isEmpty()) {
- // Logged, because until now this said nothing anywhere: the module was told
- // "Package not found", the user was told nothing at all, and the daemon kept no
- // record that the press had even arrived. The framework-scope failure above was
- // invisible for exactly that reason.
- Log.w(
- TAG,
- "None of ${scopePackageNames.joinToString()} resolve for user $userId;" +
- " refusing the scope request of $packageName")
- // Leaving the whole function here skipped the cancel below, which used to be
- // merely untidy and is now a prompt nobody can use: the request has been answered,
- // so every later press of its buttons is dropped. The request is over either way,
- // so the notification goes with it.
- iCallback.onScopeRequestFailed("Package not found")
+ "approve" -> ModuleDatabase.approveModuleScope(packageName, userId, scopePackageNames)
+ .onSuccess { granted -> iCallback.onScopeRequestApproved(granted) }
+ .onFailure {
+ iCallback.onScopeRequestFailed(it.message)
return@runCatching
}
- val scopes = ModuleDatabase.getModuleScope(packageName) ?: mutableListOf()
- var added = false
- granted.forEach { scopePackageName ->
- // Compared against where the row will land, not against the user who asked: the
- // framework is stored under user 0 whoever requested it, so for "system" this test
- // never matched and every approval appended a duplicate and rewrote the whole
- // table.
- val storedUserId = if (scopePackageName == "system") 0 else userId
- val present =
- scopes.any { it.packageName == scopePackageName && it.userId == storedUserId }
- if (!present) {
- scopes.add(
- ScopeEntry().apply {
- this.packageName = scopePackageName
- this.userId = storedUserId
- })
- added = true
- }
- }
- // One write for the whole prompt, and none at all when the user approved what the
- // module already had. `setModuleScope` replaces the module's rows wholesale and
- // enables the module on the way through, so writing per package would rewrite the
- // table once per package — leaving a window after each in which the scope is only
- // partly what was agreed to — and writing unconditionally would let a module enable
- // itself by asking again for what it has.
- if (added) ModuleDatabase.setModuleScope(packageName, scopes)
- Log.i(TAG, "Approved ${granted.joinToString()} for $packageName on user $userId")
- // The packages that were granted, which is what the list in this callback is for. A
- // module comparing it against what it asked for can see what it did not get.
- iCallback.onScopeRequestApproved(granted)
- }
"deny" -> iCallback.onScopeRequestFailed("Request denied by user")
"delete" -> iCallback.onScopeRequestFailed("Request timeout")
}
@@ -545,23 +485,8 @@ object VectorService : IVectorDaemon.Stub() {
NotificationManager.cancelScopeRequest(packageName, userId, scopePackageNames)
}
- /**
- * The modules that may not ask for scope again.
- *
- * Filed under "lspd" rather than under the module it names, because it records the user's decision
- * about a module rather than that module's own configuration. That is also why uninstalling a
- * module does not take it away — `deleteModulePrefs` deletes what is filed under the module's own
- * name — and why [unblockScopeRequests] has to exist.
- */
- @Suppress("UNCHECKED_CAST")
- private fun blockedScopeRequests(): Set =
- PreferenceStore.getModulePrefs("lspd", 0, "config")["scope_request_blocked"] as? Set
- ?: emptySet()
-
- private fun blockScopeRequests(packageName: String) {
- PreferenceStore.updateModulePref(
- "lspd", 0, "config", "scope_request_blocked", blockedScopeRequests() + packageName)
- }
+ private fun blockScopeRequests(packageName: String) =
+ PreferenceStore.setBlockedScopeRequests(PreferenceStore.getBlockedScopeRequests() + packageName)
/**
* Lets an uninstalled module ask again if it comes back.
@@ -573,10 +498,9 @@ object VectorService : IVectorDaemon.Stub() {
* back — and a module that is gone has no decision left to honour.
*/
private fun unblockScopeRequests(packageName: String) {
- val blocked = blockedScopeRequests()
+ val blocked = PreferenceStore.getBlockedScopeRequests()
if (packageName !in blocked) return
- PreferenceStore.updateModulePref(
- "lspd", 0, "config", "scope_request_blocked", blocked - packageName)
+ PreferenceStore.setBlockedScopeRequests(blocked - packageName)
Log.i(TAG, "$packageName was uninstalled; it may ask for scope again if it returns")
}
}
diff --git a/daemon/src/main/kotlin/org/matrix/vector/daemon/data/ModuleDatabase.kt b/daemon/src/main/kotlin/org/matrix/vector/daemon/data/ModuleDatabase.kt
index 32a0acc85..9746db019 100644
--- a/daemon/src/main/kotlin/org/matrix/vector/daemon/data/ModuleDatabase.kt
+++ b/daemon/src/main/kotlin/org/matrix/vector/daemon/data/ModuleDatabase.kt
@@ -3,8 +3,10 @@ package org.matrix.vector.daemon.data
import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
import android.util.Log
-import org.matrix.vector.ipc.ScopeEntry
import org.matrix.vector.daemon.system.NotificationManager
+import org.matrix.vector.daemon.system.getPackageInfoCompat
+import org.matrix.vector.daemon.system.packageManager
+import org.matrix.vector.ipc.ScopeEntry
private const val TAG = "VectorModuleDatabase"
@@ -403,4 +405,71 @@ object ModuleDatabase {
return changed
}
+
+ fun approveModuleScope(packageName: String, userId: Int, requested: List): Result> {
+ // "system" is the framework and not a package: it names system_server, which belongs
+ // to no package and resolves for nobody. The lookup below therefore came back null
+ // for every framework prompt, and the approval the user had just given was answered
+ // "Package not found" — the request was closed, its notification canceled, and no
+ // row written. The normalization to user 0 further down could never once have run.
+ //
+ // Package by package rather than all-or-nothing: the prompt may have been up for an
+ // hour and one of the packages it named can have been uninstalled in the meantime,
+ // which is no reason to throw away the user's answer about the rest.
+ //
+ // Only under "approve", because only an approval has to name something real. Deny
+ // and the timeout used to be refused here too, so dismissing a prompt for a package
+ // that had since been uninstalled told the module "Package not found" when what had
+ // actually happened was that the user turned it down.
+ val granted =
+ requested.filter {
+ it == "system" || packageManager?.getPackageInfoCompat(it, 0, userId) != null
+ }
+ if (granted.isEmpty()) {
+ // Logged, because until now this said nothing anywhere: the module was told
+ // "Package not found", the user was told nothing at all, and the daemon kept no
+ // record that the press had even arrived. The framework-scope failure above was
+ // invisible for exactly that reason.
+ Log.w(
+ TAG,
+ "None of ${requested.joinToString()} resolve for user $userId;" +
+ " refusing the scope request of $packageName"
+ )
+ // Leaving the whole function here skipped the cancel below, which used to be
+ // merely untidy and is now a prompt nobody can use: the request has been answered,
+ // so every later press of its buttons is dropped. The request is over either way,
+ // so the notification goes with it.
+ return Result.failure(Exception("Package not found"))
+ }
+ val scopes = getModuleScope(packageName) ?: mutableListOf()
+ var added = false
+ granted.forEach { scopePackageName ->
+ // Compared against where the row will land, not against the user who asked: the
+ // framework is stored under user 0 whoever requested it, so for "system" this test
+ // never matched and every approval appended a duplicate and rewrote the whole
+ // table.
+ val storedUserId = if (scopePackageName == "system") 0 else userId
+ val present =
+ scopes.any { it.packageName == scopePackageName && it.userId == storedUserId }
+ if (!present) {
+ scopes.add(
+ ScopeEntry().apply {
+ this.packageName = scopePackageName
+ this.userId = storedUserId
+ })
+ added = true
+ }
+ }
+ // One write for the whole prompt, and none at all when the user approved what the
+ // module already had. `setModuleScope` replaces the module's rows wholesale and
+ // enables the module on the way through, so writing per package would rewrite the
+ // table once per package — leaving a window after each in which the scope is only
+ // partly what was agreed to — and writing unconditionally would let a module enable
+ // itself by asking again for what it has.
+ if (added) setModuleScope(packageName, scopes)
+ Log.i(TAG, "Approved ${granted.joinToString()} for $packageName on user $userId")
+ // The packages that were granted, which is what the list in this callback is for. A
+ // module comparing it against what it asked for can see what it did not get.
+ return Result.success(granted)
+ }
}
diff --git a/daemon/src/main/kotlin/org/matrix/vector/daemon/data/PreferenceStore.kt b/daemon/src/main/kotlin/org/matrix/vector/daemon/data/PreferenceStore.kt
index bf1f4905f..de7c40b96 100644
--- a/daemon/src/main/kotlin/org/matrix/vector/daemon/data/PreferenceStore.kt
+++ b/daemon/src/main/kotlin/org/matrix/vector/daemon/data/PreferenceStore.kt
@@ -97,7 +97,23 @@ object PreferenceStore {
fun setVerboseLog(enabled: Boolean) =
updateModulePref("lspd", 0, "config", "enable_verbose_log", enabled)
+ @Suppress("UNCHECKED_CAST")
+ fun getBlockedScopeRequests(): Set =
+ getModulePrefs("lspd", 0, "config")["scope_request_blocked"] as? Set ?: emptySet()
+
+ fun setBlockedScopeRequests(scopes: Set) =
+ updateModulePref("lspd", 0, "config", "scope_request_blocked", scopes)
+
fun isScopeRequestBlocked(pkg: String): Boolean =
- (getModulePrefs("lspd", 0, "config")["scope_request_blocked"] as? Set<*>)?.contains(pkg) ==
- true
+ getBlockedScopeRequests().contains(pkg)
+
+ @Suppress("UNCHECKED_CAST")
+ fun getApprovedScopeRequests(): Set =
+ getModulePrefs("lspd", 0, "config")["scope_request_approved"] as? Set ?: emptySet()
+
+ fun setApprovedScopeRequests(scopes: Set) =
+ updateModulePref("lspd", 0, "config", "scope_request_approved", scopes)
+
+ fun isScopeRequestApproved(pkg: String): Boolean =
+ getApprovedScopeRequests().contains(pkg)
}
diff --git a/daemon/src/main/kotlin/org/matrix/vector/daemon/ipc/ManagerService.kt b/daemon/src/main/kotlin/org/matrix/vector/daemon/ipc/ManagerService.kt
index 96251dffc..c2e84a714 100644
--- a/daemon/src/main/kotlin/org/matrix/vector/daemon/ipc/ManagerService.kt
+++ b/daemon/src/main/kotlin/org/matrix/vector/daemon/ipc/ManagerService.kt
@@ -272,6 +272,24 @@ object ManagerService : IManagerService.Stub() {
override fun getModuleScope(packageName: String) = ModuleDatabase.getModuleScope(packageName)
+ override fun isScopeRequestBlocked(packageName: String) =
+ PreferenceStore.isScopeRequestBlocked(packageName)
+
+ override fun setModuleScopeRequestBlocked(packageName: String, block: Boolean) {
+ val blocked = PreferenceStore.getBlockedScopeRequests()
+ if (block xor (packageName !in blocked)) return
+ PreferenceStore.setBlockedScopeRequests(if (block) blocked + packageName else blocked - packageName)
+ }
+
+ override fun isScopeRequestApproved(packageName: String) =
+ PreferenceStore.isScopeRequestApproved(packageName)
+
+ override fun setModuleScopeRequestApproved(packageName: String, approve: Boolean) {
+ val approved = PreferenceStore.getApprovedScopeRequests()
+ if (approve xor (packageName !in approved)) return
+ PreferenceStore.setApprovedScopeRequests(if (approve) approved + packageName else approved - packageName)
+}
+
// Reports the setting, not the setting OR'd with the build type. It used to be
// `|| BuildConfig.DEBUG`, which made the value unwritable on a debug daemon: the manager could
// never read false, so its switch snapped back on every tap and had to be greyed out. The OR was
diff --git a/daemon/src/main/kotlin/org/matrix/vector/daemon/ipc/ModuleAppService.kt b/daemon/src/main/kotlin/org/matrix/vector/daemon/ipc/ModuleAppService.kt
index 916ac2cb4..8d2b7bc39 100644
--- a/daemon/src/main/kotlin/org/matrix/vector/daemon/ipc/ModuleAppService.kt
+++ b/daemon/src/main/kotlin/org/matrix/vector/daemon/ipc/ModuleAppService.kt
@@ -474,7 +474,11 @@ class ModuleAppService(private val loadedModule: LoadedModule) : IXposedService.
return
}
}
- if (!PreferenceStore.isScopeRequestBlocked(loadedModule.packageName)) {
+ if (PreferenceStore.isScopeRequestApproved(loadedModule.packageName)) {
+ ModuleDatabase.approveModuleScope(loadedModule.packageName, userId, requested)
+ .onSuccess { granted -> callback.onScopeRequestApproved(granted) }
+ .onFailure { callback.onScopeRequestFailed(it.message) }
+ } else if (!PreferenceStore.isScopeRequestBlocked(loadedModule.packageName)) {
NotificationManager.requestModuleScope(loadedModule.packageName, userId, requested, callback)
} else {
callback.onScopeRequestFailed("Scope request blocked by user configuration")
diff --git a/manager/src/debug/kotlin/org/matrix/vector/manager/demo/FakeManagerService.kt b/manager/src/debug/kotlin/org/matrix/vector/manager/demo/FakeManagerService.kt
index a0e7964f0..4a60fef29 100644
--- a/manager/src/debug/kotlin/org/matrix/vector/manager/demo/FakeManagerService.kt
+++ b/manager/src/debug/kotlin/org/matrix/vector/manager/demo/FakeManagerService.kt
@@ -218,6 +218,20 @@ class FakeManagerService(
override fun getModuleScope(packageName: String?): MutableList? =
if (real == null) mutableListOf() else real.getModuleScope(packageName)
+ override fun isScopeRequestBlocked(packageName: String?): Boolean =
+ real?.isScopeRequestBlocked(packageName) ?: false
+
+ override fun setModuleScopeRequestBlocked(packageName: String?, block: Boolean) {
+ real?.setModuleScopeRequestBlocked(packageName, block)
+ }
+
+ override fun isScopeRequestApproved(packageName: String?): Boolean =
+ real?.isScopeRequestApproved(packageName) ?: false
+
+ override fun setModuleScopeRequestApproved(packageName: String?, approve: Boolean) {
+ real?.setModuleScopeRequestApproved(packageName, approve)
+ }
+
override fun isVerboseLogEnabled(): Boolean = real?.isVerboseLogEnabled ?: false
override fun setVerboseLogEnabled(enabled: Boolean) {
diff --git a/manager/src/main/kotlin/org/matrix/vector/manager/ipc/DaemonClient.kt b/manager/src/main/kotlin/org/matrix/vector/manager/ipc/DaemonClient.kt
index b4c0705f8..bb44905dd 100644
--- a/manager/src/main/kotlin/org/matrix/vector/manager/ipc/DaemonClient.kt
+++ b/manager/src/main/kotlin/org/matrix/vector/manager/ipc/DaemonClient.kt
@@ -205,6 +205,25 @@ class DaemonClient(private val serviceState: StateFlow) {
?: throw IllegalArgumentException("$packageName has no scope to read")
}
+
+ suspend fun isScopeRequestBlocked(packageName: String): Result = runIpc {
+ it.isScopeRequestBlocked(packageName)
+ }
+
+ suspend fun setModuleScopeRequestBlocked(
+ packageName: String,
+ block: Boolean,
+ ): Result = runIpc { it.setModuleScopeRequestBlocked(packageName, block) }
+
+ suspend fun isScopeRequestApproved(packageName: String): Result = runIpc {
+ it.isScopeRequestApproved(packageName)
+ }
+
+ suspend fun setModuleScopeRequestApproved(
+ packageName: String,
+ approve: Boolean,
+ ): Result = runIpc { it.setModuleScopeRequestApproved(packageName, approve) }
+
suspend fun isStatusNotificationEnabled(): Result = runIpc {
it.isStatusNotificationEnabled
}
diff --git a/manager/src/main/kotlin/org/matrix/vector/manager/ui/components/PackageActionMenu.kt b/manager/src/main/kotlin/org/matrix/vector/manager/ui/components/PackageActionMenu.kt
index b25b2ca01..098b5d4e7 100644
--- a/manager/src/main/kotlin/org/matrix/vector/manager/ui/components/PackageActionMenu.kt
+++ b/manager/src/main/kotlin/org/matrix/vector/manager/ui/components/PackageActionMenu.kt
@@ -52,6 +52,7 @@ import org.matrix.vector.manager.ui.theme.LocalizedOverlay
import org.matrix.vector.manager.R
import android.text.format.Formatter
import androidx.compose.material.icons.rounded.ArrowCircleUp
+import androidx.compose.material.icons.rounded.Block
import androidx.compose.material.icons.rounded.CloudDownload
import androidx.compose.material.icons.rounded.CloudOff
import androidx.compose.material.icons.rounded.NotificationsOff
@@ -65,6 +66,7 @@ import org.matrix.vector.manager.data.repository.ModuleUpdateQueue
import org.matrix.vector.manager.ui.screens.repo.StoreChannel
import org.matrix.vector.manager.ui.screens.repo.releasesOn
import androidx.compose.material.icons.rounded.RestartAlt
+import androidx.compose.material.icons.rounded.VerifiedUser
import androidx.compose.material3.TextButton
import org.matrix.vector.manager.ui.screens.modules.ScopeViewModel
import org.matrix.vector.manager.ui.screens.modules.ScopeViewModel.Companion.SYSTEM_FRAMEWORK_PACKAGE
@@ -123,6 +125,9 @@ fun PackageActionSheet(
// Asked once, when the sheet opens. Most modules have neither a companion nor a launcher entry,
// and a row that exists only to report that it has nothing to do is worse than no row.
var openable by remember(packageName, userId) { mutableStateOf(null) }
+ var isScopeRequestBlocked by remember(packageName) { mutableStateOf(false) }
+ var isScopeRequestApproved by remember(packageName) { mutableStateOf(false) }
+
LaunchedEffect(packageName, userId) {
openable =
ServiceLocator.daemon
@@ -131,6 +136,12 @@ fun PackageActionSheet(
logW("actions: launch target lookup for $packageName u$userId failed", e)
}
.getOrNull() != null
+
+ isScopeRequestBlocked =
+ ServiceLocator.daemon.isScopeRequestBlocked(packageName).getOrDefault(false)
+
+ isScopeRequestApproved =
+ ServiceLocator.daemon.isScopeRequestApproved(packageName).getOrDefault(false)
}
var confirmSoftReboot by remember { mutableStateOf(false) }
@@ -181,7 +192,10 @@ fun PackageActionSheet(
// thing a drag on a sheet can *do* other than dismiss it, so a sheet taller than half the
// screen would open at full height and could not be made smaller. Material caps the stop at
// the sheet's own height, so short sheets still open at that height and gain no useless drag.
- val sheetState = rememberBottomSheetState(initialValue = SheetValue.Hidden)
+ val sheetState = rememberBottomSheetState(
+ initialValue = SheetValue.Hidden,
+ enabledValues = setOf(SheetValue.Hidden, SheetValue.Expanded),
+ )
// `Dispatchers.Main` because [onResult] reaches a snackbar on the screen underneath, and
// because that is the thread the composition scope this replaces used to resume on.
@@ -363,6 +377,48 @@ LocalizedOverlay {
}
}
+ if (isModule) {
+ HorizontalDivider(Modifier.padding(horizontal = 24.dp, vertical = 4.dp))
+
+ if (!isScopeRequestApproved)
+ ActionToggleRow(
+ title = stringResource(R.string.action_block_scope_requests),
+ icon = Icons.Rounded.Block,
+ checked = isScopeRequestBlocked,
+ onCheckedChange = { checked ->
+ isScopeRequestBlocked = checked
+
+ scope.launch(Dispatchers.Main) {
+ daemon.setModuleScopeRequestBlocked(packageName, checked).onFailure {
+ isScopeRequestBlocked = !checked
+ }
+ }
+ },
+ )
+
+ ActionToggleRow(
+ title = stringResource(R.string.action_approve_scope_requests),
+ subtitle = stringResource(R.string.action_approve_scope_requests_summary),
+ icon = Icons.Rounded.VerifiedUser,
+ checked = isScopeRequestApproved,
+ onCheckedChange = { checked ->
+ isScopeRequestApproved = checked
+
+ scope.launch(Dispatchers.Main) {
+ daemon.setModuleScopeRequestApproved(packageName, checked).onFailure {
+ isScopeRequestApproved = !checked
+ }.onSuccess {
+ if (checked && isScopeRequestBlocked) {
+ daemon.setModuleScopeRequestBlocked(packageName, false).onSuccess {
+ isScopeRequestBlocked = false
+ }
+ }
+ }
+ }
+ },
+ )
+ }
+
if (isModule) {
HorizontalDivider(Modifier.padding(horizontal = 24.dp, vertical = 4.dp))
ActionRow(
diff --git a/manager/src/main/res/values-iw/strings.xml b/manager/src/main/res/values-iw/strings.xml
index c7f45c39b..739be470a 100644
--- a/manager/src/main/res/values-iw/strings.xml
+++ b/manager/src/main/res/values-iw/strings.xml
@@ -213,6 +213,9 @@
לא ניתן היה לבצע אופטימיזציה מחדש ל-%1$s.
%1$s הוסרה.
לא ניתן היה להסיר את %1$s.
+ חסמית בקשות תחום
+ אישור בקשות תחום
+ אישור בקשות תחום באופן אוטומטי
חזרה
ניסיון נוסף
diff --git a/manager/src/main/res/values/strings.xml b/manager/src/main/res/values/strings.xml
index 7a1d4dc1b..befdca82c 100644
--- a/manager/src/main/res/values/strings.xml
+++ b/manager/src/main/res/values/strings.xml
@@ -308,6 +308,9 @@
Could not re-optimize %1$s.
Uninstalled %1$s.
Could not uninstall %1$s.
+ Block scope requests
+ Approve scope requests
+ Approve scope requests automatically
Back
diff --git a/services/manager-service/src/main/aidl/org/matrix/vector/ipc/IManagerService.aidl b/services/manager-service/src/main/aidl/org/matrix/vector/ipc/IManagerService.aidl
index 19319ba12..2baad4c26 100644
--- a/services/manager-service/src/main/aidl/org/matrix/vector/ipc/IManagerService.aidl
+++ b/services/manager-service/src/main/aidl/org/matrix/vector/ipc/IManagerService.aidl
@@ -73,7 +73,7 @@ interface IManagerService {
* transaction ids follow declaration order, this number is the only thing standing between a
* mismatched pair and a call that lands on the wrong method.
*/
- const int PROTOCOL_VERSION = 1;
+ const int PROTOCOL_VERSION = 2;
/**
* Which generation of this interface the daemon implements, never below 1.
@@ -311,6 +311,20 @@ interface IManagerService {
*/
boolean setModuleScope(String packageName, in List scope);
+ /**
+ * Whether a module's scope requests are blocked.
+ */
+ boolean isScopeRequestBlocked(String packageName);
+
+ void setModuleScopeRequestBlocked(String packageName, boolean block);
+
+ /**
+ * Whether a module's scope requests are approved automatically.
+ */
+ boolean isScopeRequestApproved(String packageName);
+
+ void setModuleScopeRequestApproved(String packageName, boolean approve);
+
/**
* Whether a module is given each newly installed app automatically.
*