Fix root service connection race condition#1496
Merged
Conversation
…eption Add Mutex to RootServiceConnection.ensureConnected() to prevent concurrent bind attempts. During app startup, AppPermissionMonitor could call ensureConnected() twice in quick succession, both seeing service==null and both calling RootService.bind() with different ServiceConnection instances. The second bind interfered with the first, causing neither onServiceConnected callback to fire. Also add auto-reconnect in RootApiController.switchComponent() so that if the root service disconnects after initialization, it attempts to reconnect before throwing RootUnavailableException.
The permissionStatus Flow in AppPermissionMonitor was a cold Flow, meaning each collector independently triggered the entire chain including initController(). During app startup, multiple collectors (BlockerAppState, AppListViewModel) would each trigger RootService.bind() concurrently with different ServiceConnection instances. The second bind interfered with the first, causing neither onServiceConnected callback to fire, leaving rootService permanently null. Fix by converting permissionStatus to a hot Flow using shareIn() with application scope, so initController() executes only once regardless of how many collectors subscribe. Also revert the Mutex approach which made things worse by permanently blocking all callers when the first bind hung.
RootServer.onBind() used EntryPointAccessors.fromApplication() to obtain PackageInfoDataSource via Hilt, but the root service runs in a separate process (com.merxury.blocker.debug:root:0) started by libsu's app_process which has no Hilt-initialized Application context. This caused IllegalStateException on every bind attempt, preventing onServiceConnected from ever being called. Fix by removing the Hilt EntryPoint dependency and directly using ApplicationUtil.isSystemApp() instead, which was the only method from PackageInfoDataSource actually used in the root process.
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.
Summary
MutextoRootServiceConnection.ensureConnected()to prevent concurrent bind attempts that cause neitheronServiceConnectedcallback to fireRootApiController.switchComponent()when root service is disconnectedProblem
During app startup,
AppPermissionMonitor.permissionStatusFlow emits the controller type twice in quick succession (~65ms apart), causing two concurrent calls toensureConnected(). Both calls seeservice == nulland both invokeRootService.bind()with differentServiceConnectioninstances. The second bind interferes with the first, resulting in neitheronServiceConnectedcallback being triggered. This leavesrootServicepermanently null, causing all subsequent component control operations to fail withRootUnavailableException.Test plan