diff --git a/android_legacy/src/main/AndroidManifest.xml b/android_legacy/src/main/AndroidManifest.xml index 8523e9d..120d404 100644 --- a/android_legacy/src/main/AndroidManifest.xml +++ b/android_legacy/src/main/AndroidManifest.xml @@ -6,6 +6,7 @@ + diff --git a/android_legacy/src/main/java/com/tailscale/ipn/App.java b/android_legacy/src/main/java/com/tailscale/ipn/App.java index cae86c9..e303227 100644 --- a/android_legacy/src/main/java/com/tailscale/ipn/App.java +++ b/android_legacy/src/main/java/com/tailscale/ipn/App.java @@ -284,6 +284,17 @@ public class App extends Application { return null; } + void requestNotificationPermission(Activity act) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { + // We can send notifications without explicit notifications permission. + return; + } + if (ContextCompat.checkSelfPermission(act, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) { + return; + } + act.requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, IPNActivity.NOTIFICATIONS_PERMISSION_RESULT); + } + void requestWriteStoragePermission(Activity act) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q || Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // We can write files without permission. diff --git a/android_legacy/src/main/java/com/tailscale/ipn/IPNActivity.java b/android_legacy/src/main/java/com/tailscale/ipn/IPNActivity.java index 4501d43..544558b 100644 --- a/android_legacy/src/main/java/com/tailscale/ipn/IPNActivity.java +++ b/android_legacy/src/main/java/com/tailscale/ipn/IPNActivity.java @@ -20,6 +20,7 @@ import org.gioui.GioView; public final class IPNActivity extends Activity { final static int WRITE_STORAGE_RESULT = 1000; + final static int NOTIFICATIONS_PERMISSION_RESULT = 1001; private GioView view; @@ -97,6 +98,13 @@ public final class IPNActivity extends Activity { if (grants.length > 0 && grants[0] == PackageManager.PERMISSION_GRANTED) { App.onWriteStorageGranted(); } + break; + case NOTIFICATIONS_PERMISSION_RESULT: + // Start the VPN regardless of the notifications permission being granted. + // It's not a blocker for running the VPN. + App app = ((App)getApplicationContext()); + app.startVPN(); + break; } } diff --git a/cmd/tailscale/main.go b/cmd/tailscale/main.go index 6ca8613..5973901 100644 --- a/cmd/tailscale/main.go +++ b/cmd/tailscale/main.go @@ -1148,10 +1148,17 @@ func (a *App) runUI() error { } case <-onVPNPrepared: if state.backend.State > ipn.Stopped { - if err := a.callVoidMethod(a.appCtx, "startVPN", "()V"); err != nil { - return err - } - if activity != 0 { + // If there isn't a foreground activity start the VPN right away. + if activity == 0 { + if err := a.callVoidMethod(a.appCtx, "startVPN", "()V"); err != nil { + return err + } + } else { + // Otherwise, check for notification permission and let the result + // of that start the VPN service. + if err := a.callVoidMethod(a.appCtx, "requestNotificationPermission", "(Landroid/app/Activity;)V", jni.Value(activity)); err != nil { + return err + } if err := a.callVoidMethod(a.appCtx, "requestWriteStoragePermission", "(Landroid/app/Activity;)V", jni.Value(activity)); err != nil { return err }