Fix the scope requests that cannot be granted and the answers that are dropped - #890
Merged
Conversation
…e dropped "system" is the framework rather than a package: it names system_server, which belongs to no package and resolves for nobody. The receiver looked the requested package up before acting on the button, so every framework prompt died there -- the approval the user had just given was answered "Package not found", the request was closed, its notification cancelled and no row written. The approve branch normalises "system" to user 0, and that line could never once have run. Accept the framework name without asking the package manager, and move the lookup under "approve", which is the only answer that has to name something real: deny and the one-hour timeout were reported as "Package not found" too when a package had been uninstalled while the prompt was up, which told the module the opposite of what had happened. One request is now one prompt. The interface takes a list and a single IXposedScopeCallback for it, and the client library's listener is documented to run "when the request is completed", but the daemon put one prompt per package on screen and answered each in its own right: a module asking for three packages made the user answer three questions and then fired that one listener three times, and a module that took the first answer as the answer acted on a third of it. The whole list goes up as one prompt whose Approve answers for all of it, deduplicated and sorted so that the same set asked twice replaces its own prompt rather than stacking a second copy of the same question, and the per-module ceiling now bounds calls rather than packages. What the user gives away in one press is what the prompt lists, so it lists all of them; the notification reuses the string it always did, with the packages joined into it, so no translation changes. An approval was thrown away when the module's process had died. A prompt sits for an hour and the app a module runs inside can be killed at any point in it, and the receiver returned on a dead callback binder before claiming the answer or cancelling the notification: the user pressed Approve, nothing was written, and the prompt stayed on screen with live buttons that did nothing for the rest of the hour. An approval is a decision about the module's scope and is recorded whether or not the module is still there to be told, deny and the timeout still take the prompt down, and the only call that can fail against a dead module is caught where it is made. None of this said anything anywhere. The refusal path had no log line at all, so a scope request that could never be granted left no trace in the daemon log, in logcat or on screen, and the framework case above went unnoticed for as long as it did for that reason. Name the packages that did not resolve, and the ones that were approved.
JingMatrix
added a commit
that referenced
this pull request
Aug 7, 2026
Four faults on the daemon side of a module's scope request. The framework could never be granted. "system" names system_server and belongs to no package, but the receiver resolved the requested package before acting on the button, so every framework prompt was answered "Package not found", closed and cancelled with no row written. Accept the framework name without asking the package manager, and do the lookup only under Approve, the one answer that has to name something real; deny and the one-hour timeout no longer report a lookup failure for a package uninstalled while the prompt was up. One request is now one prompt. The interface takes a list and a single IXposedScopeCallback for it, but the daemon put one prompt per package on screen and answered each in its own right, so a module asking for three packages made the user answer three questions and fired that one listener three times. The whole list goes up as one prompt whose Approve answers for all of it, deduplicated and sorted so the same set asked twice replaces its own prompt, and the per-module ceiling bounds calls rather than packages. The notification reuses the string it always did with the packages joined into it, so no translation changes. An approval survives a dead module. A prompt sits for an hour and the app a module runs inside can be killed in it, and the receiver returned on a dead callback binder before claiming the answer, leaving nothing written and the prompt on screen with buttons that did nothing. The decision is recorded whether or not the module is still there to be told, and the only call that can fail against it is caught where it is made. The refusal path is logged. It had no log line at all, so a request that could never be granted left no trace anywhere, which is why the framework case went unnoticed for as long as it did. Name the packages that did not resolve, and the ones that were approved.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
"system"is the framework rather than a package: it names system_server, which belongs to no package and resolves for nobody.dispatchModuleScopelooked the requested package up before acting on the button, so every framework prompt died at that lookup — the approval the user had just given was answered "Package not found", the request was closed, its notification cancelled and no row written. The approve branch normalises"system"to user 0, and that line could never once have run. The framework name is now accepted without asking the package manager, and the lookup moved under"approve", the only answer that has to name something real: deny and the one-hour timeout were reported as "Package not found" as well when a package had been uninstalled while the prompt was up, which told the module the opposite of what had happened.One request is now one prompt.
IXposedService.requestScopetakes a list and a singleIXposedScopeCallbackfor it, andOnScopeEventListeneris documented to run "when the request is completed", butModuleAppServicefanned the list out into one prompt per package and each was answered in its own right — a module asking for three packages made the user answer three questions and then fired that one listener three times, and a module that took the first answer as the answer acted on a third of it.NotificationManager.withdrawScopeRequestsalready carried a note about that cost. The whole list now goes up as one prompt whose Approve answers for all of it, deduplicated and sorted so that the same set asked twice replaces its own prompt rather than stacking a second copy of the same question, andMAX_OPEN_SCOPE_REQUESTS_PER_MODULEbounds calls rather than packages. What the user gives away in one press is what the prompt lists, so it lists all of them.An approval was thrown away when the module's process had died. A prompt sits for an hour and the app a module runs inside can be killed at any point in it, and the receiver returned on
!callbackBinder.isBinderAliveabove both the claim and the cancel: the user pressed Approve, nothing was written, and the prompt stayed on screen with live buttons that did nothing for the rest of the hour. An approval is a decision about the module's scope and is recorded whether or not the module is still there to be told; deny and the timeout still take the prompt down; and the one call that can fail against a dead module is caught where it is made.setModuleScopeis skipped when the approval adds no row, since it rewrites the table and enables the module on the way through.None of this said anything anywhere. The refusal path had no log line at all, so a scope request that could never be granted left no trace in the daemon log, in logcat or on screen — which is why the framework case went unnoticed for as long as it did. The packages that did not resolve and the ones that were approved are both named now.
No new strings:
xposed_module_request_scope_contenttakes the requested package as%3$sand accepts the list joined into it unchanged, so no translation changes.