Skip to content

Conversation

codeOwlAI
Copy link
Owner

@codeOwlAI codeOwlAI commented Jun 29, 2025

PR Summary

Enhance File Operations Handling with Event-based Architecture

Overview

This PR improves file operations handling by introducing an event-based architecture using eventbus and channels, particularly for Android file sharing. It also refactors device unlock handling in QuickToggleService for better security.

Change Types

Type Description
Enhancement Added event-based architecture for file operations
Refactor Improved device unlock handling in QuickToggleService

Affected Modules

Module / File Change Description
src/main/java/com/tailscale/ipn/QuickToggleService.java Refactored onClick() to use unlockAndRun() for secure device unlock
backend.go Added eventbus integration, removed directFileRoot parameter, added file operations monitoring
callbacks.go Added channel variables for ShareFileHelper and SAF paths
fileops.go Added AndroidFileOps implementation wrapping ShareFileHelper
interfaces.go Added SetShareFileHelper and SetDirectFileRoot functions
tailscale.go Added backend restart channel and watchFileOpsChanges goroutine

Notes for Reviewers

  • Verify that the removal of directFileRoot parameter from newBackend() doesn't cause runtime issues
  • Confirm that synchronization primitives are correctly implemented to avoid race conditions
  • Test the device unlock handling in Android to ensure security is maintained

kari-ts and others added 15 commits April 21, 2025 10:48
…ation (tailscale#642)

* android/src: ktfmt

Signed-off-by: Jakub Meysner <[email protected]>

* android/src/main: show exit node information in the permanent notification

Displays exit node status (including the name of the exit node) in the permanent connection notification's content (moving the overall connected/disconnected status to the title).

Fixes tailscale/tailscale#14438

Signed-off-by: Jakub Meysner <[email protected]>

* docker: fix invalid instruction in Dockerfile not using trailing slash for files destination directory

> If the source is a file, and the destination doesn't end with a trailing slash, the source file will be written to the destination path as a file.
~ https://docs.docker.com/reference/dockerfile/#destination

Signed-off-by: Jakub Meysner <[email protected]>

---------

Signed-off-by: Jakub Meysner <[email protected]>
OSS and Version updated to 1.83.162-ta9b3e09a1-g8683c789f

Fixes a breaking change in the NetMon constructor.

Signed-off-by: Jonathan Nobels <[email protected]>
OSS and Version updated to 1.83.190-tfd263adc1-g5b4eff216

Signed-off-by: Jonathan Nobels <[email protected]>
…#622)

* android: ensure in secure state to interact with quicktile

Updates tailscale/tailscale#14628

Signed-off-by: davfsa <[email protected]>

* Update android/src/main/java/com/tailscale/ipn/QuickToggleService.java

Signed-off-by: davfsa <[email protected]>

---------

Signed-off-by: davfsa <[email protected]>
Right now, we register the launcher in MainActivity.onCreate(), inject this into the ViewModel, then show the launcher in MainView.
There is no guarantee that the activity is in RESUMED when the Composable runs, showing the launcher. This can lead to a silent RESULT_CANCELED on some OEMs.
The fix is to add a lifecycle-aware wrapper that defers the launch.

Updates tailscale/tailscale#15419

Signed-off-by: kari-ts <[email protected]>
OSS and Version updated to 1.83.223-t336b3b7df-gd3f34c579

Signed-off-by: kari-ts <[email protected]>
&& takes precedence over ?:, so fix isExitNode to check both IPv4 and IPv6

Updates tailscale/tailscale#15785

Signed-off-by: kari-ts <[email protected]>
We were previously calling startService(intent), which is a direct call consumed by IPNService, but restartVPN was not working as intended because the broadcast receiver was never triggered.
Rather than use a broadcast receiver, directly start the service in restartVPN as we do in stopVPN. Also, batch changes to excluded apps so that we don't restart the VPN each time the user toggles an app.

Fixes tailscale/corp#28668

Signed-off-by: kari-ts <[email protected]>
OSS and Version updated to 1.83.237-tc4fb380f3-g7f56d0c0f

Signed-off-by: Jonathan Nobels <[email protected]>
OSS and Version updated to 1.83.240-t5a8b99e97-gd3f34c579

Signed-off-by: kari-ts <[email protected]>
Signed-off-by: kari-ts <[email protected]>
Use Android Storage Access Framework for receiving Taildropped files.

-Add a picker to allow users to select where Taildropped files go
-If no directory is selected, internal app storage is used
-Provide SAF API for Go to use when writing and renaming files
-Provide Android FileOps implementation

Updates tailscale/tailscale#15263

Signed-off-by: kari-ts <[email protected]>
OSS and Version updated to 1.85.8-t09582bdc0-gbd5191363

Signed-off-by: Jonathan Nobels <[email protected]>
updates tailscale/corp#29045

We ran into an issue where the current detection logic was not
sufficient to filter out mullvad nodes.  This modifies the logic so we
scan both the Name and ComputedName for the mullvad domain and
also treat all nodes with location info as mullvad nodes.  While all
of these conditions *should* be true for any mullvad node, in practice
it's possible that they aren't so we or them together for some redundancy
and define a mullvad exit node to be any node where any of these
conditions is true.

Signed-off-by: Jonathan Nobels <[email protected]>
}

func SetDirectFileRoot(filePath string) {
onFilePath <- filePath
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐛 Correctness Issue

Potential Deadlock in SetDirectFileRoot.

Writing to onFilePath channel without checking if it's full can cause a deadlock if the channel is not being actively read from.

Current Code (Diff):

-	onFilePath <- filePath
+	select {
+	case onFilePath <- filePath:
+	default:
+		// Channel is full, log the issue
+		log.Printf("Unable to set file path: channel full")
+	}
📝 Committable suggestion

‼️ IMPORTANT
Trust, but verify! 🕵️ Please review this suggestion with the care of a code archaeologist - check that it perfectly replaces the highlighted code, preserves all lines, maintains proper indentation, and won't break anything in production. Your future self will thank you! 🚀

Suggested change
onFilePath <- filePath
select {
case onFilePath <- filePath:
default:
// Channel is full, log the issue
log.Printf("Unable to set file path: channel full")
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants