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
}