Compare commits

..

157 Commits

Author SHA1 Message Date
Jason A. Donenfeld 914917036e version: bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-27 15:52:23 +02:00
Jason A. Donenfeld 6e9ca435ca gradle: trim experimental list
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-27 13:51:25 +02:00
Jason A. Donenfeld 0797e99559 proguard: cleanup unused parts
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-27 13:12:02 +02:00
Jason A. Donenfeld 0ba064ff48 build: bump activity-ktx and fragment-ktx
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-25 14:53:41 +02:00
Jason A. Donenfeld b4fe9efadb strings: sync with crowdin
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-13 18:49:36 +02:00
Jason A. Donenfeld b1b08ce716 ui: display latest handshake time
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-24 18:07:03 +02:00
Jason A. Donenfeld 20480992c4 tunnel: export latest handshake stat
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-24 18:06:57 +02:00
Harsh Shandilya 9df90b259a gradle: update wrapper to 8.1.1
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-04-24 11:22:21 +05:30
Jason A. Donenfeld b94a412c09 tools: build elf-cleaner using system compiler
Rather than using the NDK compiler for the host arch and hoping it picks
up on system library locations, just use `cc` and rely on the
traditional symlink to resolve that to the default host compiler.

This should address build errors seen on macOS.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-21 15:43:25 +02:00
Jason A. Donenfeld 0b0a553123 ui: remove unused extra_margin constant
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-13 19:36:20 +02:00
Jason A. Donenfeld 6d4d2f4ebf tools: ndk-compat: NDK now is minimum API 19
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-13 19:24:50 +02:00
Jason A. Donenfeld e55358f685 build: bump AGP to 8.0.0
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-27 14:21:47 +02:00
Jason A. Donenfeld c2fdd0ca93 ui: layout: don't stretch text fields, so that alignment is correct
This makes things look uniform on RTL.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-13 17:47:36 +02:00
Jason A. Donenfeld 19044b0f53 ui: LogViewerActivity: cleanup ret val
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-13 16:06:54 +02:00
Jason A. Donenfeld d4e77e6d57 version: bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-12 15:48:04 +02:00
Jason A. Donenfeld 6ba3e4f001 tools: do not put vcs info into go binary
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-12 15:48:01 +02:00
Jason A. Donenfeld 8152191277 ui: further reduce log viewer memory usage
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-12 13:48:55 +02:00
Jason A. Donenfeld 8c6c909e98 tools: bump go
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-11 19:52:30 +02:00
Jason A. Donenfeld fdda87fd31 tunnel: defer enabling strict mode until later
Startup code can be synchronous.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-11 19:20:26 +02:00
Jason A. Donenfeld 3907f6a4c1 tunnel: do not keep service running when bringing tunnel down
Doing so causes the process to restart when the activity is cleared,
even when no tunnel is running. I fear this might also lead to the
current flurry of DeadSystemExceptions.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-11 19:17:55 +02:00
Jason A. Donenfeld 652971d746 tools: clean compile options a bit
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-11 16:23:37 +02:00
Jason A. Donenfeld 1bf8d7e4c4 build: shrink resources
Saves 300k.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-10 18:19:52 +02:00
Jason A. Donenfeld 3c834ac8e3 ui: always show visible localized error messages
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-07 15:22:00 +02:00
Jason A. Donenfeld 8669ae78e1 ui: set app list button text on main thread
Hopefully will fix, "Only the original thread that creted a view
hierarchy can touch its views."

Reported-by: Damir <2k1dmg@gmail.com>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-07 15:18:33 +02:00
Jason A. Donenfeld 53f8c8dbc4 build: remove bespoke signing config properties
Keys should be in HSMs anyway, and a setup like this does not easily
allow for that.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-06 17:16:29 +02:00
Jason A. Donenfeld 1328660b0b build: bump deps
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-05 22:19:37 +02:00
Jason A. Donenfeld 997edf4e4c version: bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-05 17:09:41 +02:00
Jason A. Donenfeld beb40ad09c ui: allow vetoing tunnel selection changes
These changes can happen at the wrong time, so we need to be able to
walk them back. Fixes:

Exception java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
  at androidx.fragment.app.FragmentManager.checkStateLoss (FragmentManager.java:1610)
  at androidx.fragment.app.FragmentManager.enqueueAction (FragmentManager.java:1650)
  at androidx.fragment.app.BackStackRecord.commitInternal (BackStackRecord.java:341)
  at androidx.fragment.app.BackStackRecord.commit (BackStackRecord.java:306)
  at androidx.fragment.app.FragmentManagerKt.commit$default (FragmentManager.kt:35)
  at com.wireguard.android.activity.MainActivity.onSelectedTunnelChanged (MainActivity.kt:113)
  at com.wireguard.android.activity.BaseActivity.setSelectedTunnel (BaseActivity.kt:25)
  at com.wireguard.android.activity.BaseActivity$onCreate$1.invokeSuspend (BaseActivity.kt:44)
  at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33)
  at kotlinx.coroutines.internal.DispatchedContinuation.resumeUndispatchedWith (DispatchedContinuation.java:256)
  at kotlinx.coroutines.DispatchedTaskKt.resume (DispatchedTask.kt:177)
  at kotlinx.coroutines.CancellableContinuationImpl.parentCancelled$kotlinx_coroutines_core (CancellableContinuationImpl.kt:190)
  at kotlinx.coroutines.DispatchedTaskKt.resumeUnconfined (DispatchedTask.kt:190)
  at kotlinx.coroutines.DispatchedTaskKt.dispatch (DispatchedTask.kt:161)
  at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume (CancellableContinuationImpl.kt:397)
  at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl (CancellableContinuationImpl.kt:431)
  at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default (CancellableContinuationImpl.kt:420)
  at kotlinx.coroutines.CancellableContinuationImpl.resumeWith (CancellableContinuationImpl.kt:328)
  at kotlinx.coroutines.ResumeAwaitOnCompletion.invoke (JobSupport.kt:1413)
  at kotlinx.coroutines.JobSupport.notifyHandlers (JobSupport.kt:368)
  at kotlinx.coroutines.JobSupport.notifyCompletion (JobSupport.kt:362)
  at kotlinx.coroutines.JobSupport.completeStateFinalization (JobSupport.kt:323)
  at kotlinx.coroutines.JobSupport.finalizeFinishingState (JobSupport.kt:240)
  at kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath (JobSupport.kt:906)
  at kotlinx.coroutines.JobSupport.tryMakeCompleting (JobSupport.kt:863)
  at kotlinx.coroutines.JobSupport.makeCompleting$kotlinx_coroutines_core (JobSupport.kt:806)
  at kotlinx.coroutines.CompletableDeferredImpl.complete (CompletableDeferredImpl.java:92)
  at com.wireguard.android.model.TunnelManager$onTunnelsLoaded$1.invokeSuspend (TunnelManager.kt:120)
  at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33)
  at kotlinx.coroutines.DispatchedTask.run (DispatchedTask.kt:106)
  at android.os.Handler.handleCallback (Handler.java:938)
  at android.os.Handler.dispatchMessage (Handler.java:99)
  at android.os.Looper.loop (Looper.java:237)
  at android.app.ActivityThread.main (ActivityThread.java:8163)
  at java.lang.reflect.Method.invoke
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:656)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:967)

Exception java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
  at androidx.fragment.app.FragmentManager.checkStateLoss (FragmentManager.java:1610)
  at androidx.fragment.app.FragmentManager.enqueueAction (FragmentManager.java:1650)
  at androidx.fragment.app.BackStackRecord.commitInternal (BackStackRecord.java:341)
  at androidx.fragment.app.BackStackRecord.commit (BackStackRecord.java:306)
  at androidx.fragment.app.FragmentManagerKt.commit$default (FragmentManager.kt:35)
  at com.wireguard.android.activity.MainActivity.onSelectedTunnelChanged (MainActivity.kt:113)
  at com.wireguard.android.activity.BaseActivity.setSelectedTunnel (BaseActivity.kt:25)
  at com.wireguard.android.activity.BaseActivity$onCreate$1.invokeSuspend (BaseActivity.kt:44)
  at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33)
  at kotlinx.coroutines.internal.DispatchedContinuation.resumeUndispatchedWith (DispatchedContinuation.java:256)
  at kotlinx.coroutines.DispatchedTaskKt.resume (DispatchedTask.kt:177)
  at kotlinx.coroutines.CancellableContinuationImpl.parentCancelled$kotlinx_coroutines_core (CancellableContinuationImpl.kt:190)
  at kotlinx.coroutines.DispatchedTaskKt.resumeUnconfined (DispatchedTask.kt:190)
  at kotlinx.coroutines.DispatchedTaskKt.dispatch (DispatchedTask.kt:161)
  at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume (CancellableContinuationImpl.kt:397)
  at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl (CancellableContinuationImpl.kt:431)
  at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default (CancellableContinuationImpl.kt:420)
  at kotlinx.coroutines.CancellableContinuationImpl.resumeWith (CancellableContinuationImpl.kt:328)
  at kotlinx.coroutines.ResumeAwaitOnCompletion.invoke (JobSupport.kt:1413)
  at kotlinx.coroutines.JobSupport.notifyHandlers (JobSupport.kt:368)
  at kotlinx.coroutines.JobSupport.notifyCompletion (JobSupport.kt:362)
  at kotlinx.coroutines.JobSupport.completeStateFinalization (JobSupport.kt:323)
  at kotlinx.coroutines.JobSupport.finalizeFinishingState (JobSupport.kt:240)
  at kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath (JobSupport.kt:906)
  at kotlinx.coroutines.JobSupport.tryMakeCompleting (JobSupport.kt:863)
  at kotlinx.coroutines.JobSupport.makeCompleting$kotlinx_coroutines_core (JobSupport.kt:806)
  at kotlinx.coroutines.CompletableDeferredImpl.complete (CompletableDeferredImpl.java:92)
  at com.wireguard.android.model.TunnelManager$onTunnelsLoaded$1.invokeSuspend (TunnelManager.kt:120)
  at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33)
  at kotlinx.coroutines.DispatchedTask.run (DispatchedTask.kt:106)
  at android.os.Handler.handleCallback (Handler.java:938)
  at android.os.Handler.dispatchMessage (Handler.java:99)
  at android.os.Looper.loopOnce (Looper.java:210)
  at android.os.Looper.loop (Looper.java:299)
  at android.app.ActivityThread.main (ActivityThread.java:8302)
  at java.lang.reflect.Method.invoke
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:556)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1037)

Exception java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
  at androidx.fragment.app.FragmentManager.checkStateLoss (FragmentManager.java:1610)
  at androidx.fragment.app.FragmentManager.ensureExecReady (FragmentManager.java:1711)
  at androidx.fragment.app.FragmentManager.execPendingActions (FragmentManager.java:1754)
  at androidx.fragment.app.FragmentManager.popBackStackImmediate (FragmentManager.java:854)
  at androidx.fragment.app.FragmentManager.popBackStackImmediate (FragmentManager.java:778)
  at com.wireguard.android.activity.MainActivity.onSelectedTunnelChanged (MainActivity.kt:110)
  at com.wireguard.android.activity.BaseActivity.setSelectedTunnel (BaseActivity.kt:25)
  at com.wireguard.android.activity.BaseActivity$onCreate$1.invokeSuspend (BaseActivity.kt:44)
  at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33)
  at kotlinx.coroutines.DispatchedTask.run (DispatchedTask.kt:106)
  at kotlinx.coroutines.EventLoop.processUnconfinedEvent (EventLoop.common.kt:69)
  at kotlinx.coroutines.DispatchedTaskKt.runUnconfinedEventLoop (DispatchedTask.kt:204)
  at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith (DispatchedContinuation.kt:282)
  at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable (Cancellable.kt:30)
  at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default (Cancellable.kt:25)
  at kotlinx.coroutines.CoroutineStart.invoke (CoroutineStart.java:110)
  at kotlinx.coroutines.AbstractCoroutine.start (AbstractCoroutine.kt:126)
  at kotlinx.coroutines.BuildersKt.launch$default (Builders.kt)
  at com.wireguard.android.model.TunnelManager.onTunnelsLoaded (TunnelManager.java:114)
  at com.wireguard.android.model.TunnelManager.access$onTunnelsLoaded (TunnelManager.java:40)
  at com.wireguard.android.model.TunnelManager$onCreate$1.invokeSuspend (TunnelManager.kt:104)
  at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33)
  at kotlinx.coroutines.DispatchedTask.run (DispatchedTask.kt:106)
  at android.os.Handler.handleCallback (Handler.java:942)
  at android.os.Handler.dispatchMessage (Handler.java:99)
  at android.os.Looper.loopOnce (Looper.java:226)
  at android.os.Looper.loop (Looper.java:313)
  at android.app.ActivityThread.main (ActivityThread.java:8757)
  at java.lang.reflect.Method.invoke
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:571)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1067)

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-05 17:08:47 +02:00
Jason A. Donenfeld 4bac284083 ui: don't use primary color on status bar < api 23
The primary color changes in night theme, and since there's no proper
mode for the status icons on those APIs, it looks bad.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-05 16:20:10 +02:00
Jason A. Donenfeld eae9216fe4 ui: no need to manually recreate activities after night change
Since version 1.1.0, setDefaultNightMode now takes care of that. Also,
set the initial mode in a blocking fashion to prevent flashing white.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-05 16:03:54 +02:00
Jason A. Donenfeld dc1860c74d ui: distinguish play store installs at runtime for reproducible builds
This change lets us use the same build for F-Droid, Play Store, self
builds, and elsewhere, which makes everything more easily publicly
verifiable, since the build system is reproducible. That means that all
APKs will have the same code and be completely interchangeable, no
matter where they come from.

It does this by removing the build-time branch for special Play Store
builds, and replacing it with a simple runtime check using the
PackageManager APIs that return the name of the installer. If the app is
installed by "com.android.vending", then it's a Play Store install.

It's possible to test this with:

    $ pm install -i com.android.vending path/to/package.apk

And it appears to work well.

One potential concern is that it's unclear whether the Play Store
reviewers install the app using utilities that set com.android.vending
like this. If not, that might be a problem. However, it looks like
various banking apps also use the installer package name check in the
same way, and refuse to start if it's not right. That suggests that it
would be impossible for Play Store reviewers to even review those
banking apps if they did not set com.android.vending properly.

Out of an abundance of caution, though, and in order to avoid a Play
Store suspension that's harder to appeal, I sent a support request
today (which just managed to fit exactly in the 1000 character limit):

    Hi,

    My app pays special attention to Google Play Store guidelines. For that
    reason, there is some code in the app that looks like this:

        if (BuildConfig.IS_GOOGLE_PLAY)
            ...
        else
            ...

    This means that I compile two versions of my app, one for Google Play,
    and another for other app stores. This has worked well for many years
    and it satisfies Google's policy requirements.

    However, compiling two versions of my app is a bit of a pain. Instead, I
    would like to do this check at runtime, with code like this:

        if (pm.getInstallSourceInfo(package).installingPackageName == "com.android.vending")
            ...
        else
            ...

    I have tested that this code works well, and I've installed my app with:

        $ pm install -i com.android.vending ui-release.apk

    This works and successfully satisfies the policy requirements.

    My question is how this works during the review process. Are reviewed
    apps installed with the necessary -i com.android.vending switch to make
    this work?

    Thanks.

They responded fairly quickly:

    Hi Jason,

    Thanks for contacting the Google Play team.

    Unfortunately I'm not able to comment on your planned implementation. If
    you think your app is in compliance, please submit your app for review.
    You may want to review the Developer Program Policies for additional
    policy guidance.

    We recommend reviewing the details listed in this blog post and update
    your app accordingly to comply with the changes.

    Thanks for your understanding and continued support.

    Regards,
    Mia
    Google Play Developer Support

So I'll interpret that as a, "if you think it's okay, submit it and see,
and then we'll let you know." So here we go. Hopefully if it is
rejected, the update will only be blocked, and I'll just revert this
commit and resubmit.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-05 13:54:23 +02:00
Jason A. Donenfeld 6209d3ee3d tools: clean unwanted ELF flags from binaries
Fixes warnings on old Android, such as:

linker  : /data/data/com.wireguard.android/code_cache/bin/wg-quick: unsupported flags DT_FLAGS_1=0x8000001
linker  : /data/data/com.wireguard.android/code_cache/bin/wg: unsupported flags DT_FLAGS_1=0x8000001

Reported-by: L.W.Reek <syphyr@gmail.com>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-05 13:50:57 +02:00
Jason A. Donenfeld 33aded09c6 tools: deduplicate cmake arguments
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-05 13:50:57 +02:00
Jason A. Donenfeld f360034f29 tools: remove cmake dependency hack
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-05 12:23:41 +02:00
L.W.Reek 01e72064d4 tools: add missing CLOCK_BOOTTIME case to mips64x
Signed-off-by: L.W.Reek <syphyr@gmail.com>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-05 02:49:26 +02:00
Jason A. Donenfeld 91a75b681d ui: always use MaterialAlertDialogBuilder
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-04 21:41:49 +02:00
Jason A. Donenfeld 0efdf0cdd7 strings: sync with crowdin
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-04 20:54:29 +02:00
Jason A. Donenfeld 75efd13556 ui: don't create fake theme color attr
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-04 20:53:42 +02:00
Jason A. Donenfeld cd812b03cd ui: force dark theme in TV mode
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-04 20:53:42 +02:00
Jason A. Donenfeld 794a2a1925 ui: use filled icon buttons in TV mode
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-04 20:53:42 +02:00
Jason A. Donenfeld 75899594c3 ui: use real M3 themeing on TV
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-04 20:53:42 +02:00
Jason A. Donenfeld 4944762d7c ui: redirect to play store if no file manager on tv
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-04 20:53:42 +02:00
Jason A. Donenfeld d6431db248 ui: detect tv framework stubs when getting content
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-04 20:53:42 +02:00
Jason A. Donenfeld b8f649d1cb ui: use ErrorMessages consistently
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-04 20:53:42 +02:00
Jason A. Donenfeld 9fa8ca8c77 ui: do not crash if tunnel preparation fails
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-04 15:31:52 +02:00
Jason A. Donenfeld 59da677c23 ui: do not OOM when leaving log window open for a while
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-04 15:31:52 +02:00
Jason A. Donenfeld 18a06b0a51 ui: show bottom sheet sync to prevent double add
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-04 15:31:52 +02:00
Jason A. Donenfeld 5e844bed5e ui: do not return to detail page when restoring state
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-04 15:31:52 +02:00
Jason A. Donenfeld 46bf98d7f6 ui: never access CREATOR directly
All of the parcelers have their own type prefix. So we have to actually
use the legit methods. This is a bit annoying, as there's no fully
compatible way across all API versions, so we have to branch.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-04 15:31:52 +02:00
Jason A. Donenfeld 3e9ddd8720 ui: never throw on link clicks
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-04 15:31:52 +02:00
Jason A. Donenfeld 9e33d9e56b version: bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-04 15:31:52 +02:00
Jason A. Donenfeld 9fae327315 tools: pass -Wl,--build-id=none and -buildid= to toolchain
Unfortunately, we need to fix this up in both the Makefile and in CMake,
because add_link_options doesn't immediately reflect on the CMake
variables in scope.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-04 15:14:28 +02:00
Jason A. Donenfeld a6f5eb0dd2 build: exclude extra kotlin cruft from release build
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-04-04 15:14:28 +02:00
Jason A. Donenfeld c3c4cd05b1 ui: don't play with status and navigation colors on old APIs
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-30 16:05:41 +02:00
Jason A. Donenfeld 58c19385a3 ui: do not allow double clicking fab to show menu twice
Exception java.lang.IllegalStateException:
  at androidx.fragment.app.FragmentStore.addFragment (FragmentStore.java:92)
  at androidx.fragment.app.FragmentManager.addFragment (FragmentManager.java:1481)
  at androidx.fragment.app.BackStackRecord.executeOps (BackStackRecord.java:387)
  at androidx.fragment.app.FragmentManager.executeOps (FragmentManager.java:1965)
  at androidx.fragment.app.FragmentManager.executeOpsTogether (FragmentManager.java:1873)
  at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute (FragmentManager.java:1823)
  at androidx.fragment.app.FragmentManager.execPendingActions (FragmentManager.java:1760)
  at androidx.fragment.app.FragmentManager$5.run (FragmentManager.java:547)
  at android.os.Handler.handleCallback (Handler.java:938)
  at android.os.Handler.dispatchMessage (Handler.java:99)
  at android.os.Looper.loop (Looper.java:268)
  at android.app.ActivityThread.main (ActivityThread.java:8101)
  at java.lang.reflect.Method.invoke
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:627)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:997)

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-30 15:15:39 +02:00
Jason A. Donenfeld 354e1c0ed9 ui: remove unneeded @JvmStatic annotations
These are only needed on methods called from databindings, since those
compile to java.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-30 15:07:45 +02:00
Jason A. Donenfeld 02b9677a9c ui: add list selection highlight color for tablet
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-30 14:53:28 +02:00
Jason A. Donenfeld 82d26e61da ui: the button is not always blue with Material You
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-30 14:23:10 +02:00
Jason A. Donenfeld a113770868 ui: use app resources instead of ctx for strings in callback
Exception java.lang.IllegalStateException:
  at androidx.fragment.app.Fragment.requireContext (Fragment.java:967)
  at com.wireguard.android.fragment.TunnelListFragment$tunnelFileImportResultLauncher$1$1.invokeSuspend (TunnelListFragment.kt:64)
  at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33)
  at kotlinx.coroutines.DispatchedTask.run (DispatchedTask.kt:104)
  at android.os.Handler.handleCallback (Handler.java:761)
  at android.os.Handler.dispatchMessage (Handler.java:98)
  at android.os.Looper.loop (Looper.java:156)
  at android.app.ActivityThread.main (ActivityThread.java:6617)
  at java.lang.reflect.Method.invoke
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:942)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:832)

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-30 13:54:06 +02:00
Jason A. Donenfeld b734f44200 ui: use M3 theme for TV to prevent crash
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-30 13:40:05 +02:00
Jason A. Donenfeld ee16d1329b version: bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-28 18:32:34 +02:00
Jason A. Donenfeld 900654e6b1 tools: use NDK's make binary rather than system's
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-28 18:31:38 +02:00
Jason A. Donenfeld a0cba19645 strings: sync crowdin
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-28 17:53:43 +02:00
Jason A. Donenfeld d7ccbd92e5 ui: add log items in buffered batches
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-28 17:47:36 +02:00
Jason A. Donenfeld 2da80e8dee ui: color verbose logs the same as debug logs
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-28 16:02:47 +02:00
Jason A. Donenfeld 450ef8ce1c ui: don't call getColor on already resolved attribute
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-28 16:02:47 +02:00
Jason A. Donenfeld 58e7b7a069 ui: bring back dark log viewer colors
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-28 16:02:47 +02:00
Harsh Shandilya d354831c6a ui: fix inconsistent style naming
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-03-28 17:42:35 +05:30
Harsh Shandilya fcb7caed23 gradle: opt into new publishing APIs
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-03-28 17:42:07 +05:30
Harsh Shandilya c7d1c4baba ui: adjust log viewer text sizes
10sp is below the minimum 11sp recommended for readability

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-03-28 00:53:18 +05:30
Harsh Shandilya 76423ce868 ui: use M3 styles everywhere
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-03-28 00:51:47 +05:30
Jason A. Donenfeld b37006ce2f ui: add possibility for themed icon
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-27 18:05:39 +02:00
Jason A. Donenfeld b072130e37 ui: add list selection backgrounds
Harsh - feel free to replace this commit with something better. I'm sure
it's the most terrible way to do it.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-27 18:00:00 +02:00
Jason A. Donenfeld 54dfa7bdd7 ui: cancel actionmode when pressing back button
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-27 17:59:00 +02:00
Jason A. Donenfeld e5a75307c2 ui: simplify actionmode cast
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-27 17:47:09 +02:00
Harsh Shandilya 4fcb8c4792 ui: introduce a fresh set of Material3 themes and migrate to them
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-03-27 16:10:24 +02:00
Harsh Shandilya fb05b358c0 ui: use AndroidX BundleCompat for extracting parcelable
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-03-27 16:11:30 +05:30
Jason A. Donenfeld f13624c58f ui: switch from SwitchMaterial to MaterialSwitch
This required some weird changes to prevent clipping on the top, because
apparently the new switch is a bit fatter.

I think this actually looks a bit uglier than before, but it seems like
that's what Material design wants. Maybe we can improve it?

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-25 23:58:06 +01:00
Jason A. Donenfeld 497a60cc90 ui: bring back donation button
For Google Play Store builds, we'll display an alert box. This was
inspired by the discussion around StreetComplete; hopefully we'll have a
similar okay outcome.

Link: https://github.com/streetcomplete/streetcomplete/issues/3768
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-24 14:16:08 +01:00
Jason A. Donenfeld 8139e60094 strings: update from crowdin
This is after doing a rebuild on the crowdin side.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-24 14:12:21 +01:00
Harsh Shandilya 9dab67f3f1 ui: fix inconsistent toolbar color
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-03-24 14:12:21 +01:00
Harsh Shandilya e342c9a28f ui: use Material switch component
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-03-24 14:12:21 +01:00
Jason A. Donenfeld cf943b7119 ui: migrate to OnBackPressedDispatcher
This is compatible with Android 13's prediction-based back gesture
animation.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-24 14:12:21 +01:00
Harsh Shandilya b7295cd56f build: raise target SDK to 33
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-03-24 14:12:21 +01:00
Jason A. Donenfeld 763d9a9f17 ui: set selected tunnel after creating fragments
I'm not sure why that comment (Samuel's) was there saying it was
necessary. Given it's been async for a long while, this wasn't
guaranteed anyway. So let's get rid of it and see what happens. Screen
rotation seems fine thus far.

Cc: Samuel Holland <samuel@sholland.org>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-24 14:12:21 +01:00
Jason A. Donenfeld f3107e349c ui: switch from using launchWhenCreated to using repeatOnLifecycle
Apparently the former is deprecated.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-24 14:12:21 +01:00
Jason A. Donenfeld caf2cded6e ui: simplify IME displaying in ConfigNamingDialogFragment
Also cleanup some event listener flow.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-24 14:12:21 +01:00
Harsh Shandilya b44380c81a ui: migrate to MenuProvider
AndroidX Activity 1.4.0 introduced the new lifecycle-dependent MenuProvider
APIs that supersede existing onCreateOptionsMenu-style menus.

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-03-22 21:35:34 +05:30
Harsh Shandilya 35c8c10b7e ui: address Parcelable API deprecations
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-03-22 21:35:34 +05:30
Harsh Shandilya 91227e445a ui: address PackageManager API deprecations
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-03-22 21:35:34 +05:30
Harsh Shandilya 94e6d99dc7 gradle: upgrade AndroidX libraries
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-03-22 21:35:34 +05:30
Harsh Shandilya 052ff060b1 gradle: use idiomatic task handling
- The hand-rolled clean task is not required

- Tasks should use configureEach to prevent eager evaluation

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-03-22 21:35:34 +05:30
Harsh Shandilya e843fccbc6 gradle: bump wrapper version
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-03-22 21:35:34 +05:30
Jason A. Donenfeld 3ab1f94514 tools: compile with latest NDK
The -mthumb -> -marm hack is unfortunate.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-22 15:02:54 +01:00
Jason A. Donenfeld 4aa4843b74 tools: use __ANDROID_MIN_SDK_VERSION__ instead of __ANDROID_API__
The latter is deprecated.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-22 15:02:54 +01:00
Jason A. Donenfeld 4d00c334b1 ui: set button text after loading app list in AppListDialogFragment
Otherwise, for lots of apps, the dialog shows before they're enumerated,
and the button text never gets set.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-22 15:02:54 +01:00
Jason A. Donenfeld 62986b5cad version: bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-21 15:53:39 +01:00
L.W.Reek 296295ab8a gradle: use same NDK for tunnel and UI
Fixes: 4f261560 ("gradle: force the use of an older NDK version")
Signed-off-by: L.W.Reek <syphyr@gmail.com>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-21 15:53:39 +01:00
L.W.Reek d1237e2718 build: Google Play requires target API level 31 or higher
Fix build failure from lint check

Signed-off-by: L.W.Reek <syphyr@gmail.com>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-21 15:53:39 +01:00
Jason A. Donenfeld a26b7c594a strings: sync with crowdin
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-21 15:47:26 +01:00
Jason A. Donenfeld 853e86e6d0 global: bump copyright date
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-21 15:41:11 +01:00
Jason A. Donenfeld a386e3a3d2 build: update to gradle 7.4.2 and restructure lint block
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-21 15:35:00 +01:00
Jason A. Donenfeld 560d9cf967 tools: bump deps
Notably, this isn't bumping to the latest wireguard-go master, which is
still in flux.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2023-03-21 15:35:00 +01:00
SlipkHunter 713947e432 gradle: adjust for and raise compileSdk to 33
Signed-off-by: SlipkHunter <abrito025@gmail.com>
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-01-20 19:54:23 +05:30
Harsh Shandilya f8690ca215 gradle: migrate to namespace attribute
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-01-20 19:37:41 +05:30
SlipkHunter 4f26156030 gradle: force the use of an older NDK version
NDK 25.x removed the bundled ld.gold/ld.bfd linkers, replacing
them with the ld.ldd linker from LLVM. This causes the toolchains
to pick up the host OS' `ld.gold` instead, which does not
know how to link ARM(64) code and fails the build.

Fixes: f269fb175d ("gradle: update AndroidX and Kotlin")
Signed-off-by: SlipkHunter <abrito025@gmail.com>
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-01-20 19:37:41 +05:30
Harsh Shandilya b353ed8ae9 gradle: update AGP and Kotlin
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2023-01-20 19:37:41 +05:30
Harsh Shandilya f269fb175d gradle: update AndroidX and Kotlin
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-12-01 00:15:39 +05:30
Harsh Shandilya 98fe4438d5 gradle: bump wrapper version
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-12-01 00:15:39 +05:30
Hwanseung Lee 0459d6d9f4 ui: un-export VpnService
There is no reason to expose this service to external apps.[1]

[1] https://developer.android.com/guide/topics/manifest/activity-element#exported

Signed-off-by: Hwanseung Lee <hwanseung@chromium.org>
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-12-01 00:15:39 +05:30
Harsh Shandilya 4ff564521f gradle: upgrade plugins and runtime dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-08-04 02:12:38 +05:30
Harsh Shandilya 9f139134c2 gradle: bump wrapper version
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-08-04 02:12:37 +05:30
SlipkHunter 284ad03ebf ui: add barrier to fix detail fragment overlap
When listen port and MTU are hidden, we need a barrier here.

Signed-off-by: SlipkHunter <abrito025@gmail.com>
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-07-07 15:35:10 +02:00
Harsh Shandilya c3693b3e66 gradle: remove crowdin kludge
This never ended up being used over the shell script

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-06-15 12:51:37 +05:30
Harsh Shandilya 960bc99f5e gradle: update AndroidX and Kotlin
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-06-15 12:51:37 +05:30
Lucas Lee Jing Yi 8d33b648cc gradle: remove duplicate useAndroidX entry
Signed-off-by: Lucas Lee Jing Yi <lucasleeeeeeeee@gmail.com>
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-06-15 12:51:31 +05:30
Harsh Shandilya 38cb184cf8 ui: reorder constraints to fix detail fragment overlap
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-05-26 06:50:00 +05:30
Harsh Shandilya 5d14caf3b2 ui: move default style to Material3 with Dynamic Colors
The TV theme has been kept as-is since Material You guidance around this
is a bit scarce at the moment.

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-05-26 06:49:22 +05:30
Harsh Shandilya 81c571f0c7 ui: normalise icon tints and remove unused drawable
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-05-26 06:01:03 +05:30
Harsh Shandilya 50b21e95c3 gradle: bump ConstraintLayout and MDC
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-05-26 06:01:03 +05:30
Harsh Shandilya 059bf88141 gradle: bump AGP and Kotlin
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-05-26 06:01:02 +05:30
Harsh Shandilya b3112ab0bd gradle: bump to 7.4.2
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-05-26 06:01:02 +05:30
Harsh Shandilya 1bb8a10633 ui: remove unnecessary boolean check
This check was added in 3c31c340d8 when the kernel module loader was
introduced into the app lifecycle, to avoid attempting to start a root shell
twice. When the module loader was removed in a03ad51622, this flag
was accidentally left in when it should have been deleted.

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-05-26 06:01:02 +05:30
Harsh Shandilya f3230fc8f9 idea: squelch XML codestyle warning
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-05-26 06:01:02 +05:30
Jason A. Donenfeld c762bc3120 version: bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2022-05-16 19:17:40 +02:00
Jason A. Donenfeld aeb8903ba2 tools: bump deps
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2022-05-16 19:15:07 +02:00
Shahzain Ali 2f244771a2 ui: move target SDK to 31
Signed-off-by: Shahzain Ali <shahzainali93@gmail.com>
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-04-26 01:27:41 +05:30
Harsh Shandilya 5ffb1d0c36 gradle: bump AGP and Kotlin
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-04-08 03:08:22 +05:30
Jason A. Donenfeld 62910caae1 tools: bump wireguard-go and go 1.18
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2022-03-20 22:31:57 -06:00
Vlad Loktionov 866fd35df5 ui: word wrap settings headers
Apparently some translations make this wrap, which is bad.

Signed-off-by: Vlad Loktionov <yobabay23@gmail.com>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2022-03-20 22:28:14 -06:00
Rin Patch 32a2ebb66f ui: exclude TunnelToggleActivity from recents
Fixes annoying behavior in quick settings widget, when you enable
the tunnel, try to switch to last used app, but instead it switches to
the toggle activity and turns the tunnel off.

Signed-off-by: Rin Patch <rin@patch.cx>
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-03-20 22:28:07 -06:00
Harsh Shandilya 70d9a87d0d ui: make validity check from QR code image scanner static
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-03-06 10:48:15 -07:00
Nikita Pustovoi 0bd39309c8 ui: allow importing tunnel from an QR image stored on the device
Add a new feature to import a tunnel from a saved QR image, this feature
integrates into 'import from file' flow, however adds a condition, if
file is an image, attempt to parse it as QR image file.

My use case for this feature, is to allow easier sharing of tunnels to
family.  Scanning QR code is ok when you have an external display to
show it, but if you sent QR code to someone, there is no way to import
it in the app.  If you share a config file, that becomes way harder for
a non-technical person to import as now they need to find a file with
that name in the file picker etc etc, Where the images are very visible
in the file picker, and user can easily recognize it for import.

Testing:
- Click "+" blue button, try to import a valid `.conf` file - the
  'original' file flow should not be affected
- Click "+" blue button, try to import a valid QR code image - if QR
  code was parsed, then a new tunnel will be added.
- Click "+" blue button, try to import an invalid QR code image - Error
  message will be shown

Signed-off-by: Nikita Pustovoi <deishelon@gmail.com>
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-03-06 10:48:15 -07:00
Harsh Shandilya 751ce54fa5 build: upgrade dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-03-03 11:53:18 +05:30
Michal Murin 72dc677819 tunnel: fix BadConfigExceptionTest
Fix the test by changing the DNS to a string with an invalid char in the
`invalid-value.conf` test configuration file.

Signed-off-by: Michal Murin <michal.murin@jamf.com>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2022-02-09 12:01:56 +01:00
Harsh Shandilya 7002367e4f gradle: update AndroidX dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-01-28 22:08:34 +05:30
Jason A. Donenfeld 5339e48251 ToolsInstaller: set id=wireguard magisk module param
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2022-01-21 17:10:28 +01:00
Marcel Joss d626e23210 ui: add margin on empty list text
Signed-off-by: Marcel Joss <marceljoss@hotmail.ch>
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-01-17 11:51:27 +05:30
Harsh Shandilya 342ed5e259 ui: make when statements exhaustive everywhere
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-01-08 15:30:12 +05:30
Harsh Shandilya 2bf09f8e2d gradle: bump AGP, Kotlin and AGP
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-01-08 15:30:11 +05:30
Harsh Shandilya 36d93a8e1b gradle: bump wrapper to 7.3.3
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2022-01-08 15:30:11 +05:30
Jason A. Donenfeld 1d7d306950 version: bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2021-10-29 17:21:43 +02:00
Jason A. Donenfeld 06306c669d README: re-add desugaring block
People are never gonna figure it out otherwise.

This reverts commit 1701e4fde4.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2021-10-29 17:19:00 +02:00
Jason A. Donenfeld d19e126e2e tools: bump libwg-go
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2021-10-29 16:58:28 +02:00
Jason A. Donenfeld bf84e26a86 global: bump copyright year
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2021-10-29 16:47:12 +02:00
Jason A. Donenfeld a03ad51622 tunnel: remove kernel module downloader
Nathan Chance dropped the ball repeatedly and never maintained this in a
consistent way that anybody could use. With Android 12 out now, just
drop it all together. A bummer, but I don't see much of a choice.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2021-10-29 16:42:36 +02:00
Harsh Shandilya 447b06ec2c ui: fix and silence lint errors
- The `copied_to_clipboard` translation for Farsi does not include
the placeholder, so it has been removed.

- A couple lints that are errors but we cannot particularly do anything about
were downgraded to warnings.

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-10-28 03:19:56 +05:30
Harsh Shandilya 6da1d5ed01 tunnel: make JSR305 annotations a compileOnly dependency
These have no business being part of the runtime classpath of this library

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-10-28 03:19:55 +05:30
Harsh Shandilya 836c367499 gradle: upgrade AndroidX dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-10-28 03:19:55 +05:30
Harsh Shandilya 15cb99a41f strings: sync translations
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-10-27 18:56:52 +05:30
Harsh Shandilya fae74dd3ca ui: upgrade zxing-android-embedded
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-10-25 22:42:39 +05:30
Harsh Shandilya e462ae270d gradle: bump wrapper to 7.2
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-10-25 22:38:00 +05:30
Harsh Shandilya 24d5a28a4a gradle: bump AGP
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-10-25 22:27:34 +05:30
Harsh Shandilya 3df42898ae gradle: upgrade AndroidX dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-10-10 18:23:51 +05:30
Harsh Shandilya 1701e4fde4 README: link to Android desugaring docs
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-09-27 08:45:46 +05:30
Jason A. Donenfeld f6d175b774 strings: sync with crowdin
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2021-09-26 20:05:35 -06:00
Jason A. Donenfeld 878c450563 README: bump desugar version
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2021-09-26 18:40:30 -06:00
195 changed files with 3656 additions and 4034 deletions
+1 -1
View File
@@ -14,5 +14,5 @@ build/
*.dex
*.iml
*.jks
keystore.properties
gradlew.bat
maint/
+3
View File
@@ -1,3 +1,6 @@
[submodule "tunnel/tools/wireguard-tools"]
path = tunnel/tools/wireguard-tools
url = https://git.zx2c4.com/wireguard-tools
[submodule "tunnel/tools/elf-cleaner"]
path = tunnel/tools/elf-cleaner
url = https://github.com/termux/termux-elf-cleaner
-3
View File
@@ -66,9 +66,6 @@
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="10" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="JAVA">
<option name="METHOD_ANNOTATION_WRAP" value="0" />
<option name="FIELD_ANNOTATION_WRAP" value="0" />
+1 -1
View File
@@ -1,6 +1,6 @@
<component name="CopyrightManager">
<copyright>
<option name="notice" value="Copyright © &amp;#36;today.year WireGuard LLC. All Rights Reserved.&#10;SPDX-License-Identifier: Apache-2.0" />
<option name="notice" value="Copyright © 2017-&amp;#36;today.year WireGuard LLC. All Rights Reserved.&#10;SPDX-License-Identifier: Apache-2.0" />
<option name="myName" value="Default" />
</copyright>
</component>
+2 -2
View File
@@ -22,7 +22,7 @@ The tunnel library is [on Maven Central](https://search.maven.org/artifact/com.w
implementation 'com.wireguard.android:tunnel:$wireguardTunnelVersion'
```
The library makes use of Java 8 features, so be sure to support those in your gradle configuration with desugaring:
The library makes use of Java 8 features, so be sure to support those in your gradle configuration with [desugaring](https://developer.android.com/studio/write/java8-support#library-desugaring):
```
compileOptions {
@@ -31,7 +31,7 @@ compileOptions {
coreLibraryDesugaringEnabled = true
}
dependencies {
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.0.10"
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5"
}
```
+15 -52
View File
@@ -1,69 +1,32 @@
buildscript {
ext {
activityVersion = '1.3.1'
annotationsVersion = '1.2.0'
appcompatVersion = '1.3.1'
activityVersion = '1.7.1'
annotationsVersion = '1.6.0'
appcompatVersion = '1.6.1'
biometricVersion = '1.1.0'
collectionVersion = '1.1.0'
constraintLayoutVersion = '2.1.0'
coordinatorLayoutVersion = '1.1.0'
coreKtxVersion = '1.6.0'
coroutinesVersion = '1.5.2'
collectionVersion = '1.2.0'
constraintLayoutVersion = '2.1.4'
coordinatorLayoutVersion = '1.2.0'
coreKtxVersion = '1.10.0'
coroutinesVersion = '1.6.4'
datastoreVersion = '1.0.0'
desugarVersion = '1.1.5'
fragmentVersion = '1.3.6'
fragmentVersion = '1.5.7'
jsr305Version = '3.0.2'
junitVersion = '4.13.2'
lifecycleRuntimeKtxVersion = '2.3.1'
materialComponentsVersion = '1.4.0'
preferenceVersion = '1.1.1'
zxingEmbeddedVersion = '4.2.0'
lifecycleRuntimeKtxVersion = '2.6.1'
materialComponentsVersion = '1.8.0'
preferenceVersion = '1.2.0'
zxingEmbeddedVersion = '4.3.0'
groupName = 'com.wireguard.android'
}
}
plugins {
id "de.undercouch.download" version "4.1.2"
}
task downloadCrowdin(type: Download) {
src 'https://crowdin.com/backend/download/project/wireguard.zip'
dest file('build/translations.zip')
overwrite true
}
task cleanCrowdin(type: Delete) {
delete 'ui/src/main/res/values-*/strings.xml'
}
task extractCrowdin(type: Copy, dependsOn: ['downloadCrowdin', 'cleanCrowdin']) {
mustRunAfter 'downloadCrowdin'
from zipTree(file('build/translations.zip'))
into file('build/translations')
doFirst {
delete 'build/translations'
}
}
task crowdin(type: Copy, dependsOn: ['extractCrowdin']) {
mustRunAfter 'extractCrowdin'
from 'build/translations/wireguard-android/ui/src/main/res'
into 'ui/src/main/res/'
doLast {
delete 'build/translations'
delete 'build/translations.zip'
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
tasks {
wrapper {
gradleVersion = "7.1.1"
distributionSha256Sum = "bf8b869948901d422e9bb7d1fa61da6a6e19411baa7ad6ee929073df85d6365d"
gradleVersion = "8.1.1"
distributionSha256Sum = "e111cb9948407e26351227dabce49822fb88c37ee72f1d1582a69c68af2e702f"
}
}
+2 -5
View File
@@ -18,9 +18,6 @@ org.gradle.jvmargs=-Xmx1536m
# Turn off AP discovery in compile path to enable compile avoidance
kapt.include.compile.classpath=false
# Enable AndroidX
android.useAndroidX=true
# Enable non-transitive R class namespacing where each library only contains
# references to the resources it declares instead of declarations plus all
# transitive dependency references.
@@ -62,8 +59,8 @@ android.library.defaults.buildfeatures.androidresources=false
# Suppress warnings for some features that aren't yet stabilized
android.suppressUnsupportedOptionWarnings=android.keepWorkerActionServicesBetweenBuilds,\
android.experimental.enableNewResourceShrinker,android.experimental.enableNewResourceShrinker.preciseShrinking,\
android.enableAppCompileTimeRClass,android.experimental.enableSourceSetPathsMap,android.experimental.cacheCompileLibResources,\
android.experimental.enableNewResourceShrinker.preciseShrinking,\
android.enableAppCompileTimeRClass,\
android.suppressUnsupportedOptionWarnings
# OSSRH sometimes struggles with slow deployments, so this makes Gradle
Binary file not shown.
+3 -2
View File
@@ -1,6 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=bf8b869948901d422e9bb7d1fa61da6a6e19411baa7ad6ee929073df85d6365d
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
distributionSha256Sum=e111cb9948407e26351227dabce49822fb88c37ee72f1d1582a69c68af2e702f
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+174 -114
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright 2015 the original author or authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,67 +17,98 @@
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MSYS* | MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -87,9 +118,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,7 +129,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -106,80 +137,109 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
+2 -2
View File
@@ -1,6 +1,6 @@
pluginManagement {
def agpVersion = "7.0.2"
def kotlinVersion = "1.5.31"
def agpVersion = '8.0.0'
def kotlinVersion = "1.8.0"
repositories {
gradlePluginPortal()
google()
+21 -8
View File
@@ -6,14 +6,15 @@ version wireguardVersionName
group groupName
android {
compileSdkVersion 30
compileSdk 33
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
namespace 'com.wireguard.android.tunnel'
defaultConfig {
minSdkVersion 21
targetSdkVersion 30
targetSdkVersion 33
versionCode wireguardVersionCode
versionName wireguardVersionName
}
@@ -28,31 +29,43 @@ android {
}
}
buildTypes {
all {
externalNativeBuild {
cmake {
targets 'libwg-go.so', 'libwg.so', 'libwg-quick.so'
arguments "-DGRADLE_USER_HOME=${project.gradle.gradleUserHomeDir}"
}
}
}
release {
externalNativeBuild {
cmake {
arguments "-DANDROID_PACKAGE_NAME=${groupName}", "-DGRADLE_USER_HOME=${project.gradle.gradleUserHomeDir}"
arguments "-DANDROID_PACKAGE_NAME=${groupName}"
}
}
}
debug {
externalNativeBuild {
cmake {
arguments "-DANDROID_PACKAGE_NAME=${groupName}.debug", "-DGRADLE_USER_HOME=${project.gradle.gradleUserHomeDir}"
arguments "-DANDROID_PACKAGE_NAME=${groupName}.debug"
}
}
}
}
lintOptions {
disable('LongLogTag')
disable('NewApi') // Desugaring!
lint {
disable 'LongLogTag', 'NewApi'
}
publishing {
multipleVariants("release") {
allVariants()
}
}
}
dependencies {
implementation "androidx.annotation:annotation:$annotationsVersion"
implementation "androidx.collection:collection:$collectionVersion"
implementation "com.google.code.findbugs:jsr305:$jsr305Version"
compileOnly "com.google.code.findbugs:jsr305:$jsr305Version"
testImplementation "junit:junit:$junitVersion"
}
+4 -4
View File
@@ -1,15 +1,15 @@
<!--
~ Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
~ Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
~ SPDX-License-Identifier: Apache-2.0
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.wireguard.android.tunnel">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<service
android:name="com.wireguard.android.backend.GoBackend$VpnService"
android:permission="android.permission.BIND_VPN_SERVICE">
android:permission="android.permission.BIND_VPN_SERVICE"
android:exported="false">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -24,6 +24,7 @@ import com.wireguard.crypto.KeyFormatException;
import com.wireguard.util.NonNullForAll;
import java.net.InetAddress;
import java.time.Instant;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -125,12 +126,14 @@ public final class GoBackend implements Backend {
Key key = null;
long rx = 0;
long tx = 0;
long latestHandshakeMSec = 0;
for (final String line : config.split("\\n")) {
if (line.startsWith("public_key=")) {
if (key != null)
stats.add(key, rx, tx);
stats.add(key, rx, tx, latestHandshakeMSec);
rx = 0;
tx = 0;
latestHandshakeMSec = 0;
try {
key = Key.fromHex(line.substring(11));
} catch (final KeyFormatException ignored) {
@@ -152,10 +155,26 @@ public final class GoBackend implements Backend {
} catch (final NumberFormatException ignored) {
tx = 0;
}
} else if (line.startsWith("last_handshake_time_sec=")) {
if (key == null)
continue;
try {
latestHandshakeMSec += Long.parseLong(line.substring(24)) * 1000;
} catch (final NumberFormatException ignored) {
latestHandshakeMSec = 0;
}
} else if (line.startsWith("last_handshake_time_nsec=")) {
if (key == null)
continue;
try {
latestHandshakeMSec += Long.parseLong(line.substring(25)) / 1000000;
} catch (final NumberFormatException ignored) {
latestHandshakeMSec = 0;
}
}
}
if (key != null)
stats.add(key, rx, tx);
stats.add(key, rx, tx, latestHandshakeMSec);
return stats;
}
@@ -324,6 +343,9 @@ public final class GoBackend implements Backend {
currentTunnelHandle = -1;
currentConfig = null;
wgTurnOff(handleToClose);
try {
vpnService.get(0, TimeUnit.NANOSECONDS).stopSelf();
} catch (final TimeoutException ignored) { }
}
tunnel.onStateChange(state);
@@ -1,41 +1,70 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.backend;
import android.os.SystemClock;
import android.util.Pair;
import com.wireguard.crypto.Key;
import com.wireguard.util.NonNullForAll;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import androidx.annotation.Nullable;
/**
* Class representing transfer statistics for a {@link Tunnel} instance.
*/
@NonNullForAll
public class Statistics {
private final Map<Key, Pair<Long, Long>> peerBytes = new HashMap<>();
// TODO: switch to Java Record class once R8 supports desugaring those.
public final class PeerStats {
public final long rxBytes, txBytes, latestHandshakeEpochMillis;
PeerStats(final long rxBytes, final long txBytes, final long latestHandshakeEpochMillis) {
this.rxBytes = rxBytes;
this.txBytes = txBytes;
this.latestHandshakeEpochMillis = latestHandshakeEpochMillis;
}
@Override public boolean equals(final Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
final PeerStats stats = (PeerStats) o;
return rxBytes == stats.rxBytes && txBytes == stats.txBytes && latestHandshakeEpochMillis == stats.latestHandshakeEpochMillis;
}
@Override public int hashCode() {
return Objects.hash(rxBytes, txBytes, latestHandshakeEpochMillis);
}
}
private final Map<Key, PeerStats> stats = new HashMap<>();
private long lastTouched = SystemClock.elapsedRealtime();
Statistics() {
}
/**
* Add a peer and its current data usage to the internal map.
* Add a peer and its current stats to the internal map.
*
* @param key A WireGuard public key bound to a particular peer
* @param rx The received traffic for the {@link com.wireguard.config.Peer} referenced by
* the provided {@link Key}. This value is in bytes
* @param tx The transmitted traffic for the {@link com.wireguard.config.Peer} referenced by
* the provided {@link Key}. This value is in bytes.
* @param key A WireGuard public key bound to a particular peer
* @param rxBytes The received traffic for the {@link com.wireguard.config.Peer} referenced by
* the provided {@link Key}. This value is in bytes
* @param txBytes The transmitted traffic for the {@link com.wireguard.config.Peer} referenced by
* the provided {@link Key}. This value is in bytes.
* @param latestHandshake The timestamp of the latest handshake for the {@link com.wireguard.config.Peer}
* referenced by the provided {@link Key}. The value is in epoch milliseconds.
*/
void add(final Key key, final long rx, final long tx) {
peerBytes.put(key, Pair.create(rx, tx));
void add(final Key key, final long rxBytes, final long txBytes, final long latestHandshake) {
stats.put(key, new PeerStats(rxBytes, txBytes, latestHandshake));
lastTouched = SystemClock.elapsedRealtime();
}
@@ -49,31 +78,14 @@ public class Statistics {
}
/**
* Get the received traffic (in bytes) for the {@link com.wireguard.config.Peer} referenced by
* the provided {@link Key}
* Get the statistics for the {@link com.wireguard.config.Peer} referenced by the provided {@link Key}
*
* @param peer A {@link Key} representing a {@link com.wireguard.config.Peer}.
* @return a long representing the number of bytes received by this peer.
* @return a {@link PeerStats} representing various statistics about this peer.
*/
public long peerRx(final Key peer) {
final Pair<Long, Long> rxTx = peerBytes.get(peer);
if (rxTx == null)
return 0;
return rxTx.first;
}
/**
* Get the transmitted traffic (in bytes) for the {@link com.wireguard.config.Peer} referenced by
* the provided {@link Key}
*
* @param peer A {@link Key} representing a {@link com.wireguard.config.Peer}.
* @return a long representing the number of bytes transmitted by this peer.
*/
public long peerTx(final Key peer) {
final Pair<Long, Long> rxTx = peerBytes.get(peer);
if (rxTx == null)
return 0;
return rxTx.second;
@Nullable
public PeerStats peer(final Key peer) {
return this.stats.get(peer);
}
/**
@@ -83,7 +95,7 @@ public class Statistics {
* {@link com.wireguard.config.Peer}s
*/
public Key[] peers() {
return peerBytes.keySet().toArray(new Key[0]);
return stats.keySet().toArray(new Key[0]);
}
/**
@@ -93,8 +105,8 @@ public class Statistics {
*/
public long totalRx() {
long rx = 0;
for (final Pair<Long, Long> val : peerBytes.values()) {
rx += val.first;
for (final PeerStats val : stats.values()) {
rx += val.rxBytes;
}
return rx;
}
@@ -106,8 +118,8 @@ public class Statistics {
*/
public long totalTx() {
long tx = 0;
for (final Pair<Long, Long> val : peerBytes.values()) {
tx += val.second;
for (final PeerStats val : stats.values()) {
tx += val.txBytes;
}
return tx;
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -20,6 +20,7 @@ import com.wireguard.util.NonNullForAll;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -53,6 +54,10 @@ public final class WgQuickBackend implements Backend {
this.toolsInstaller = toolsInstaller;
}
public static boolean hasKernelSupport() {
return new File("/sys/module/wireguard").exists();
}
@Override
public Set<String> getRunningTunnelNames() {
final List<String> output = new ArrayList<>();
@@ -79,17 +84,17 @@ public final class WgQuickBackend implements Backend {
final Statistics stats = new Statistics();
final Collection<String> output = new ArrayList<>();
try {
if (rootShell.run(output, String.format("wg show '%s' transfer", tunnel.getName())) != 0)
if (rootShell.run(output, String.format("wg show '%s' dump", tunnel.getName())) != 0)
return stats;
} catch (final Exception ignored) {
return stats;
}
for (final String line : output) {
final String[] parts = line.split("\\t");
if (parts.length != 3)
if (parts.length != 8)
continue;
try {
stats.add(Key.fromBase64(parts[0]), Long.parseLong(parts[1]), Long.parseLong(parts[2]));
stats.add(Key.fromBase64(parts[0]), Long.parseLong(parts[5]), Long.parseLong(parts[6]), Long.parseLong(parts[4]) * 1000);
} catch (final Exception ignored) {
}
}
@@ -1,215 +0,0 @@
/*
* Copyright © 2019-2020 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.util;
import android.content.Context;
import android.system.OsConstants;
import android.util.Base64;
import com.wireguard.android.util.RootShell.RootShellException;
import com.wireguard.crypto.Ed25519;
import com.wireguard.util.NonNullForAll;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.InvalidParameterException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import androidx.annotation.Nullable;
/**
* Class that implements the logic for downloading and loading signed, prebuilt modules for
* WireGuard into the running kernel.
*/
@NonNullForAll
@SuppressWarnings("MagicNumber")
public class ModuleLoader {
private static final String MODULE_LIST_URL = "https://download.wireguard.com/android-module/modules.txt.sig";
private static final String MODULE_NAME = "wireguard-%s.ko";
private static final String MODULE_PUBLIC_KEY_BASE64 = "RWRmHuT9PSqtwfsLtEx+QS06BJtLgFYteL9WCNjH7yuyu5Y1DieSN7If";
private static final String MODULE_URL = "https://download.wireguard.com/android-module/%s";
private final File moduleDir;
private final RootShell rootShell;
private final File tmpDir;
private final String userAgent;
/**
* Public constructor for ModuleLoader
*
* @param context A {@link Context} instance.
* @param rootShell A {@link RootShell} instance used to run elevated commands required for module
* loading.
* @param userAgent A {@link String} that represents the User-Agent string used for connections
* to the upstream server.
*/
public ModuleLoader(final Context context, final RootShell rootShell, final String userAgent) {
moduleDir = new File(context.getCacheDir(), "kmod");
tmpDir = new File(context.getCacheDir(), "tmp");
this.rootShell = rootShell;
this.userAgent = userAgent;
}
/**
* Check whether a WireGuard module is already loaded into the kernel.
*
* @return boolean indicating if WireGuard is already enabled in the kernel.
*/
public static boolean isModuleLoaded() {
return new File("/sys/module/wireguard").exists();
}
/**
* Download the correct WireGuard module for the device
*
* @return {@link OsConstants}.EXIT_SUCCESS if everything succeeds, ENOENT otherwise.
* @throws IOException if the remote hash list was not found or empty.
* @throws RootShellException if {@link RootShell} has a failure executing elevated commands.
* @throws NoSuchAlgorithmException if SHA256 algorithm is not available in device JDK.
*/
public Integer download() throws IOException, RootShellException, NoSuchAlgorithmException {
final List<String> output = new ArrayList<>();
rootShell.run(output, "sha256sum /proc/version|cut -d ' ' -f 1");
if (output.size() != 1 || output.get(0).length() != 64)
throw new InvalidParameterException("Invalid sha256 of /proc/version");
final String moduleName = String.format(MODULE_NAME, output.get(0));
HttpURLConnection connection = (HttpURLConnection) new URL(MODULE_LIST_URL).openConnection();
connection.setRequestProperty("User-Agent", userAgent);
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
throw new IOException("Hash list could not be found");
final byte[] input = new byte[1024 * 1024 * 3 /* 3MiB */];
int len;
try (final InputStream inputStream = connection.getInputStream()) {
int offset = 0;
while (input.length - offset > 0 && (len = inputStream.read(input, offset, input.length - offset)) > 0) {
offset += len;
}
len = offset;
}
if (len <= 0)
throw new IOException("Hash list was empty");
final Map<String, Sha256Digest> modules = verifySignedHashes(new String(input, 0, len, StandardCharsets.UTF_8));
if (modules == null)
throw new InvalidParameterException("The signature did not verify or invalid hash list format");
if (!modules.containsKey(moduleName))
return OsConstants.ENOENT;
connection = (HttpURLConnection) new URL(String.format(MODULE_URL, moduleName)).openConnection();
connection.setRequestProperty("User-Agent", userAgent);
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
throw new IOException("Module file could not be found, despite being on hash list");
tmpDir.mkdirs();
moduleDir.mkdir();
File tempFile = null;
try {
tempFile = File.createTempFile("UNVERIFIED-", null, tmpDir);
final MessageDigest digest = MessageDigest.getInstance("SHA-256");
try (final InputStream inputStream = connection.getInputStream();
final FileOutputStream outputStream = new FileOutputStream(tempFile)) {
int total = 0;
while ((len = inputStream.read(input)) > 0) {
total += len;
if (total > 1024 * 1024 * 15 /* 15 MiB */)
throw new IOException("File too big");
outputStream.write(input, 0, len);
digest.update(input, 0, len);
}
outputStream.getFD().sync();
}
if (!Arrays.equals(digest.digest(), modules.get(moduleName).bytes))
throw new IOException("Incorrect file hash");
if (!tempFile.renameTo(new File(moduleDir, moduleName)))
throw new IOException("Unable to rename to final destination");
} finally {
if (tempFile != null)
tempFile.delete();
}
return OsConstants.EXIT_SUCCESS;
}
/**
* Load the downloaded module. ModuleLoader#download must be called before this.
*
* @throws IOException if {@link RootShell} has a failure executing elevated commands.
* @throws RootShellException if {@link RootShell} has a failure executing elevated commands.
*/
public void loadModule() throws IOException, RootShellException {
rootShell.run(null, String.format("insmod \"%s/wireguard-$(sha256sum /proc/version|cut -d ' ' -f 1).ko\"", moduleDir.getAbsolutePath()));
}
/**
* Check if the module might already exist in the app's data.
*
* @return boolean indicating whether downloadable module might exist already.
*/
public boolean moduleMightExist() {
return moduleDir.exists() && moduleDir.isDirectory();
}
@Nullable
private Map<String, Sha256Digest> verifySignedHashes(final String signifyDigest) {
byte[] publicKeyBytes = Base64.decode(MODULE_PUBLIC_KEY_BASE64, Base64.DEFAULT);
if (publicKeyBytes == null || publicKeyBytes.length != 32 + 10 || publicKeyBytes[0] != 'E' || publicKeyBytes[1] != 'd')
return null;
final String[] lines = signifyDigest.split("\n", 3);
if (lines.length != 3)
return null;
if (!lines[0].startsWith("untrusted comment: "))
return null;
byte[] signatureBytes = Base64.decode(lines[1], Base64.DEFAULT);
if (signatureBytes == null || signatureBytes.length != 64 + 10)
return null;
for (int i = 0; i < 10; ++i) {
if (signatureBytes[i] != publicKeyBytes[i])
return null;
}
publicKeyBytes = Arrays.copyOfRange(publicKeyBytes, 10, 10 + 32);
signatureBytes = Arrays.copyOfRange(signatureBytes, 10, 10 + 64);
if (!Ed25519.verify(lines[2].getBytes(StandardCharsets.UTF_8), signatureBytes, publicKeyBytes))
return null;
final Map<String, Sha256Digest> hashes = new HashMap<>();
for (final String line : lines[2].split("\n")) {
final String[] components = line.split(" {2}", 2);
if (components.length != 2)
return null;
try {
hashes.put(components[1], new Sha256Digest(components[0]));
} catch (final Exception ignored) {
return null;
}
}
return hashes;
}
private static final class Sha256Digest {
private final byte[] bytes;
private Sha256Digest(final String hex) {
if (hex.length() != 64)
throw new InvalidParameterException("SHA256 hashes must be 32 bytes long");
bytes = new byte[32];
for (int i = 0; i < 32; ++i)
bytes[i] = (byte) Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -145,7 +145,7 @@ public final class ToolsInstaller {
script.append("trap 'rm -rf /data/adb/modules/wireguard' INT TERM EXIT; ");
script.append(String.format("rm -rf /data/adb/modules/wireguard/; mkdir -p /data/adb/modules/wireguard%s; ", INSTALL_DIR));
script.append("printf 'name=WireGuard Command Line Tools\nversion=1.0\nversionCode=1\nauthor=zx2c4\ndescription=Command line tools for WireGuard\nminMagisk=1500\n' > /data/adb/modules/wireguard/module.prop; ");
script.append("printf 'id=wireguard\nname=WireGuard Command Line Tools\nversion=1.0\nversionCode=1\nauthor=zx2c4\ndescription=Command line tools for WireGuard\nminMagisk=1500\n' > /data/adb/modules/wireguard/module.prop; ");
script.append("touch /data/adb/modules/wireguard/auto_mount; ");
for (final String name : EXECUTABLES) {
final File destination = new File("/data/adb/modules/wireguard" + INSTALL_DIR, name);
@@ -1,5 +1,5 @@
/*
* Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,6 +1,6 @@
/*
* Copyright © 2016 Southern Storm Software, Pty Ltd.
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
File diff suppressed because it is too large Load Diff
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
+1 -1
View File
@@ -1,6 +1,6 @@
[Interface]
Address = 192.0.2.2/32,2001:db8:ffff:ffff:ffff:ffff:ffff:ffff/128
DNS = 192.0.2.0,yes
DNS = 192.0.2.0,invalid_value
PrivateKey = TFlmmEUC7V7VtiDYLKsbP5rySTKLIZq1yn8lMqK83wo=
[Peer]
AllowedIPs = 0.0.0.0/0, ::0/0
+24 -15
View File
@@ -1,35 +1,44 @@
# SPDX-License-Identifier: Apache-2.0
#
# Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
# Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
cmake_minimum_required(VERSION 3.4.1)
project("WireGuard")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
# Work around https://github.com/android-ndk/ndk/issues/602
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=gold")
add_link_options(LINKER:--build-id=none)
add_compile_options(-Wall -Werror)
add_executable(libwg-quick.so wireguard-tools/src/wg-quick/android.c ndk-compat/compat.c)
target_compile_options(libwg-quick.so PUBLIC -O3 -std=gnu11 -Wall -include ${CMAKE_CURRENT_SOURCE_DIR}/ndk-compat/compat.h -DWG_PACKAGE_NAME=\"${ANDROID_PACKAGE_NAME}\")
target_compile_options(libwg-quick.so PUBLIC -std=gnu11 -include ${CMAKE_CURRENT_SOURCE_DIR}/ndk-compat/compat.h -DWG_PACKAGE_NAME=\"${ANDROID_PACKAGE_NAME}\")
target_link_libraries(libwg-quick.so -ldl)
file(GLOB WG_SOURCES wireguard-tools/src/*.c ndk-compat/compat.c)
add_executable(libwg.so ${WG_SOURCES})
target_include_directories(libwg.so PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/wireguard-tools/src/uapi/linux/" "${CMAKE_CURRENT_SOURCE_DIR}/wireguard-tools/src/")
target_compile_options(libwg.so PUBLIC -O3 -std=gnu11 -D_GNU_SOURCE -include ${CMAKE_CURRENT_SOURCE_DIR}/ndk-compat/compat.h -DHAVE_VISIBILITY_HIDDEN -DRUNSTATEDIR=\"/data/data/${ANDROID_PACKAGE_NAME}/cache\")
target_compile_options(libwg.so PUBLIC -std=gnu11 -include ${CMAKE_CURRENT_SOURCE_DIR}/ndk-compat/compat.h -DRUNSTATEDIR=\"/data/data/${ANDROID_PACKAGE_NAME}/cache\")
add_custom_target(libwg-go.so WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libwg-go" COMMENT "Building wireguard-go" VERBATIM COMMAND make
add_custom_target(libwg-go.so WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libwg-go" COMMENT "Building wireguard-go" VERBATIM COMMAND "${ANDROID_HOST_PREBUILTS}/bin/make"
ANDROID_ARCH_NAME=${ANDROID_ARCH_NAME}
ANDROID_C_COMPILER=${ANDROID_C_COMPILER}
ANDROID_TOOLCHAIN_ROOT=${ANDROID_TOOLCHAIN_ROOT}
ANDROID_LLVM_TRIPLE=${ANDROID_LLVM_TRIPLE}
ANDROID_SYSROOT=${ANDROID_SYSROOT}
ANDROID_PACKAGE_NAME=${ANDROID_PACKAGE_NAME}
GRADLE_USER_HOME=${GRADLE_USER_HOME}
CFLAGS=${CMAKE_C_FLAGS}\ -Wno-unused-command-line-argument
LDFLAGS=${CMAKE_SHARED_LINKER_FLAGS}\ -fuse-ld=gold
CC=${CMAKE_C_COMPILER}
CFLAGS=${CMAKE_C_FLAGS}
LDFLAGS=${CMAKE_SHARED_LINKER_FLAGS}
SYSROOT=${CMAKE_SYSROOT}
TARGET=${CMAKE_C_COMPILER_TARGET}
DESTDIR=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
BUILDDIR=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/../generated-src
)
# Hack to make it actually build as part of the default target
add_dependencies(libwg.so libwg-go.so)
# Strip unwanted ELF sections to prevent DT_FLAGS_1 warnings on old Android versions
file(GLOB ELF_CLEANER_SOURCES elf-cleaner/*.c elf-cleaner/*.cpp)
add_custom_target(elf-cleaner COMMENT "Building elf-cleaner" VERBATIM COMMAND cc
-O2 -DPACKAGE_NAME="elf-cleaner" -DPACKAGE_VERSION="" -DCOPYRIGHT=""
-o "${CMAKE_CURRENT_BINARY_DIR}/elf-cleaner" ${ELF_CLEANER_SOURCES}
)
add_custom_command(TARGET libwg.so POST_BUILD VERBATIM COMMAND "${CMAKE_CURRENT_BINARY_DIR}/elf-cleaner"
--api-level "${ANDROID_NATIVE_API_LEVEL}" "$<TARGET_FILE:libwg.so>")
add_dependencies(libwg.so elf-cleaner)
add_custom_command(TARGET libwg-quick.so POST_BUILD VERBATIM COMMAND "${CMAKE_CURRENT_BINARY_DIR}/elf-cleaner"
--api-level "${ANDROID_NATIVE_API_LEVEL}" "$<TARGET_FILE:libwg-quick.so>")
add_dependencies(libwg-quick.so elf-cleaner)
+10 -10
View File
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
#
# Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
# Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
BUILDDIR ?= $(CURDIR)/build
DESTDIR ?= $(CURDIR)/out
@@ -12,20 +12,20 @@ NDK_GO_ARCH_MAP_arm64 := arm64
NDK_GO_ARCH_MAP_mips := mipsx
NDK_GO_ARCH_MAP_mips64 := mips64x
CLANG_FLAGS := --target=$(ANDROID_LLVM_TRIPLE) --gcc-toolchain=$(ANDROID_TOOLCHAIN_ROOT) --sysroot=$(ANDROID_SYSROOT)
export CGO_CFLAGS := $(CLANG_FLAGS) $(CFLAGS)
export CGO_LDFLAGS := $(CLANG_FLAGS) $(LDFLAGS) -Wl,-soname=libwg-go.so
export CC := $(ANDROID_C_COMPILER)
comma := ,
CLANG_FLAGS := --target=$(TARGET) --sysroot=$(SYSROOT)
export CGO_CFLAGS := $(CLANG_FLAGS) $(subst -mthumb,-marm,$(CFLAGS))
export CGO_LDFLAGS := $(CLANG_FLAGS) $(patsubst -Wl$(comma)--build-id=%,-Wl$(comma)--build-id=none,$(LDFLAGS)) -Wl,-soname=libwg-go.so
export GOARCH := $(NDK_GO_ARCH_MAP_$(ANDROID_ARCH_NAME))
export GOOS := android
export CGO_ENABLED := 1
GO_VERSION := 1.17.1
GO_VERSION := 1.20.3
GO_PLATFORM := $(shell uname -s | tr '[:upper:]' '[:lower:]')-$(NDK_GO_ARCH_MAP_$(shell uname -m))
GO_TARBALL := go$(GO_VERSION).$(GO_PLATFORM).tar.gz
GO_HASH_darwin-amd64 := 3c452046b1dfa27b70d3217c9fe6de266f9fd74d83aad81382fead70efcdffca
GO_HASH_darwin-arm64 := 48f48a3cfe49b7bb448510ec9bf1682439e4e95fa6888580914a3115fe853d8c
GO_HASH_linux-amd64 := dab7d9c34361dc21ec237d584590d72500652e7c909bf082758fb63064fca0ef
GO_HASH_darwin-amd64 := c1e1161d6d859deb576e6cfabeb40e3d042ceb1c6f444f617c3c9d76269c3565
GO_HASH_darwin-arm64 := 86b0ed0f2b2df50fa8036eea875d1cf2d76cefdacf247c44639a1464b7e36b95
GO_HASH_linux-amd64 := 979694c2c25c735755bf26f4f45e19e64e4811d661dd07b8c010f7a8e18adfca
default: $(DESTDIR)/libwg-go.so
@@ -47,6 +47,6 @@ $(BUILDDIR)/go-$(GO_VERSION)/.prepared: $(GRADLE_USER_HOME)/caches/golang/$(GO_T
$(DESTDIR)/libwg-go.so: export PATH := $(BUILDDIR)/go-$(GO_VERSION)/bin/:$(PATH)
$(DESTDIR)/libwg-go.so: $(BUILDDIR)/go-$(GO_VERSION)/.prepared go.mod
go build -tags linux -ldflags="-X golang.zx2c4.com/wireguard/ipc.socketDirectory=/data/data/$(ANDROID_PACKAGE_NAME)/cache/wireguard" -v -trimpath -o "$@" -buildmode c-shared
go build -tags linux -ldflags="-X golang.zx2c4.com/wireguard/ipc.socketDirectory=/data/data/$(ANDROID_PACKAGE_NAME)/cache/wireguard -buildid=" -v -trimpath -buildvcs=false -o "$@" -buildmode c-shared
.DELETE_ON_ERROR:
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: Apache-2.0
*
* Copyright (C) 2017-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright © 2017-2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
package main
+9 -5
View File
@@ -1,10 +1,14 @@
module golang.zx2c4.com/wireguard/android
go 1.16
go 1.20
require (
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
golang.org/x/net v0.0.0-20210924151903-3ad01bbaa167 // indirect
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665
golang.zx2c4.com/wireguard v0.0.0-20210926231638-fcc601dbf0f6
golang.org/x/sys v0.6.0
golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675
)
require (
golang.org/x/crypto v0.7.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
)
+10 -24
View File
@@ -1,24 +1,10 @@
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210924151903-3ad01bbaa167 h1:eDd+TJqbgfXruGQ5sJRU7tEtp/58OAx4+Ayjxg4SM+4=
golang.org/x/net v0.0.0-20210924151903-3ad01bbaa167/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665 h1:QOQNt6vCjMpXE7JSK5VvAzJC1byuN3FgTNSBwf+CJgI=
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.zx2c4.com/wireguard v0.0.0-20210926231638-fcc601dbf0f6 h1:/A0kGH6GG7qv3wJtWNCMDPFUqU5iUTYZw0Mj1DhzVXQ=
golang.zx2c4.com/wireguard v0.0.0-20210926231638-fcc601dbf0f6/go.mod h1:laHzsbfMhGSobUmruXWAyMKKHSqvIcrqZJMyHD+/3O8=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675 h1:/J/RVnr7ng4fWPRH3xa4WtBJ1Jp+Auu4YNLmGiPv5QU=
golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675/go.mod h1:whfbyDBt09xhCYQWtO2+3UVjlaq6/9hDZrjg2ZE6SyA=
@@ -1,7 +1,8 @@
From b83553d9f260ba20c6faaa52e6fe6f74309eb41a Mon Sep 17 00:00:00 2001
From 61f3ae8298d1c503cbc31539e0f3a73446c7db9d Mon Sep 17 00:00:00 2001
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
Date: Mon, 22 Feb 2021 02:36:03 +0100
Subject: [PATCH] runtime: use CLOCK_BOOTTIME in nanotime on Linux
Date: Tue, 21 Mar 2023 15:33:56 +0100
Subject: [PATCH] [release-branch.go1.20] runtime: use CLOCK_BOOTTIME in
nanotime on Linux
This makes timers account for having expired while a computer was
asleep, which is quite common on mobile devices. Note that BOOTTIME is
@@ -21,17 +22,17 @@ Change-Id: I7b2a6ca0c5bc5fce57ec0eeafe7b68270b429321
src/runtime/sys_linux_amd64.s | 2 +-
src/runtime/sys_linux_arm.s | 4 ++--
src/runtime/sys_linux_arm64.s | 4 ++--
src/runtime/sys_linux_mips64x.s | 2 +-
src/runtime/sys_linux_mips64x.s | 4 ++--
src/runtime/sys_linux_mipsx.s | 2 +-
src/runtime/sys_linux_ppc64x.s | 2 +-
src/runtime/sys_linux_s390x.s | 2 +-
8 files changed, 11 insertions(+), 11 deletions(-)
8 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/runtime/sys_linux_386.s b/src/runtime/sys_linux_386.s
index 1e3a834812..78b6021fc7 100644
index 12a294153d..17e3524b40 100644
--- a/src/runtime/sys_linux_386.s
+++ b/src/runtime/sys_linux_386.s
@@ -337,13 +337,13 @@ noswitch:
@@ -352,13 +352,13 @@ noswitch:
LEAL 8(SP), BX // &ts (struct timespec)
MOVL BX, 4(SP)
@@ -48,10 +49,10 @@ index 1e3a834812..78b6021fc7 100644
INVOKE_SYSCALL
diff --git a/src/runtime/sys_linux_amd64.s b/src/runtime/sys_linux_amd64.s
index 37cb8dad03..e8b730bcaa 100644
index c7a89ba536..01f0a6a26e 100644
--- a/src/runtime/sys_linux_amd64.s
+++ b/src/runtime/sys_linux_amd64.s
@@ -302,7 +302,7 @@ noswitch:
@@ -255,7 +255,7 @@ noswitch:
SUBQ $16, SP // Space for results
ANDQ $~15, SP // Align for C code
@@ -61,7 +62,7 @@ index 37cb8dad03..e8b730bcaa 100644
MOVQ runtime·vdsoClockgettimeSym(SB), AX
CMPQ AX, $0
diff --git a/src/runtime/sys_linux_arm.s b/src/runtime/sys_linux_arm.s
index 475f52344c..bb567abcf4 100644
index 7b8c4f0e04..9798a1334e 100644
--- a/src/runtime/sys_linux_arm.s
+++ b/src/runtime/sys_linux_arm.s
@@ -11,7 +11,7 @@
@@ -73,20 +74,20 @@ index 475f52344c..bb567abcf4 100644
// for EABI, as we don't support OABI
#define SYS_BASE 0x0
@@ -366,7 +366,7 @@ noswitch:
SUB $24, R13 // Space for results
BIC $0x7, R13 // Align for C code
@@ -374,7 +374,7 @@ finish:
// func nanotime1() int64
TEXT runtime·nanotime1(SB),NOSPLIT,$12-8
- MOVW $CLOCK_MONOTONIC, R0
+ MOVW $CLOCK_BOOTTIME, R0
MOVW $8(R13), R1 // timespec
MOVW runtime·vdsoClockgettimeSym(SB), R2
CMP $0, R2
MOVW $spec-12(SP), R1 // timespec
MOVW runtime·vdsoClockgettimeSym(SB), R4
diff --git a/src/runtime/sys_linux_arm64.s b/src/runtime/sys_linux_arm64.s
index 198a5bacef..9715387f36 100644
index 38ff6ac330..6b819c5441 100644
--- a/src/runtime/sys_linux_arm64.s
+++ b/src/runtime/sys_linux_arm64.s
@@ -13,7 +13,7 @@
@@ -14,7 +14,7 @@
#define AT_FDCWD -100
#define CLOCK_REALTIME 0
@@ -95,7 +96,7 @@ index 198a5bacef..9715387f36 100644
#define SYS_exit 93
#define SYS_read 63
@@ -319,7 +319,7 @@ noswitch:
@@ -338,7 +338,7 @@ noswitch:
BIC $15, R1
MOVD R1, RSP
@@ -105,10 +106,10 @@ index 198a5bacef..9715387f36 100644
CBZ R2, fallback
diff --git a/src/runtime/sys_linux_mips64x.s b/src/runtime/sys_linux_mips64x.s
index c3e9f37694..e3879acd38 100644
index 47f2da524d..a8b387f193 100644
--- a/src/runtime/sys_linux_mips64x.s
+++ b/src/runtime/sys_linux_mips64x.s
@@ -312,7 +312,7 @@ noswitch:
@@ -326,7 +326,7 @@ noswitch:
AND $~15, R1 // Align for C code
MOVV R1, R29
@@ -117,11 +118,20 @@ index c3e9f37694..e3879acd38 100644
MOVV $0(R29), R5
MOVV runtime·vdsoClockgettimeSym(SB), R25
@@ -336,7 +336,7 @@ noswitch:
// see walltime for detail
BEQ R2, R0, finish
MOVV R0, runtime·vdsoClockgettimeSym(SB)
- MOVW $1, R4 // CLOCK_MONOTONIC
+ MOVW $7, R4 // CLOCK_BOOTTIME
MOVV $0(R29), R5
JMP fallback
diff --git a/src/runtime/sys_linux_mipsx.s b/src/runtime/sys_linux_mipsx.s
index fab2ab3892..f9af103594 100644
index 5e6b6c1504..7f5fd2a80e 100644
--- a/src/runtime/sys_linux_mipsx.s
+++ b/src/runtime/sys_linux_mipsx.s
@@ -238,7 +238,7 @@ TEXT runtime·walltime1(SB),NOSPLIT,$8-12
@@ -243,7 +243,7 @@ TEXT runtime·walltime(SB),NOSPLIT,$8-12
RET
TEXT runtime·nanotime1(SB),NOSPLIT,$8-8
@@ -131,11 +141,11 @@ index fab2ab3892..f9af103594 100644
MOVW $SYS_clock_gettime, R2
SYSCALL
diff --git a/src/runtime/sys_linux_ppc64x.s b/src/runtime/sys_linux_ppc64x.s
index fd69ee70a5..ff6bc8355b 100644
index d0427a4807..05ee9fede9 100644
--- a/src/runtime/sys_linux_ppc64x.s
+++ b/src/runtime/sys_linux_ppc64x.s
@@ -249,7 +249,7 @@ fallback:
JMP finish
@@ -298,7 +298,7 @@ fallback:
JMP return
TEXT runtime·nanotime1(SB),NOSPLIT,$16-8
- MOVD $1, R3 // CLOCK_MONOTONIC
@@ -144,18 +154,18 @@ index fd69ee70a5..ff6bc8355b 100644
MOVD R1, R15 // R15 is unchanged by C code
MOVD g_m(g), R21 // R21 = m
diff --git a/src/runtime/sys_linux_s390x.s b/src/runtime/sys_linux_s390x.s
index c15a1d5364..f52c4d5098 100644
index 1448670b91..7d2ee3231c 100644
--- a/src/runtime/sys_linux_s390x.s
+++ b/src/runtime/sys_linux_s390x.s
@@ -207,7 +207,7 @@ TEXT runtime·walltime1(SB),NOSPLIT,$16
@@ -296,7 +296,7 @@ fallback:
RET
TEXT runtime·nanotime1(SB),NOSPLIT,$16
- MOVW $1, R2 // CLOCK_MONOTONIC
+ MOVW $7, R2 // CLOCK_BOOTTIME
MOVD $tp-16(SP), R3
MOVW $SYS_clock_gettime, R1
SYSCALL
TEXT runtime·nanotime1(SB),NOSPLIT,$32-8
- MOVW $1, R2 // CLOCK_MONOTONIC
+ MOVW $7, R2 // CLOCK_BOOTTIME
MOVD R15, R7 // Backup stack pointer
--
2.30.1
2.17.1
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: Apache-2.0
*
* Copyright © 2017-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright © 2017-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
#include <jni.h>
+2 -54
View File
@@ -1,64 +1,12 @@
/* SPDX-License-Identifier: BSD
*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
*
*/
#define FILE_IS_EMPTY
#if defined(__ANDROID_API__) && __ANDROID_API__ < 18
#undef FILE_IS_EMPTY
#include <stdio.h>
#include <stdlib.h>
ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
{
char *ptr, *eptr;
if (*buf == NULL || *bufsiz == 0) {
*bufsiz = BUFSIZ;
if ((*buf = malloc(*bufsiz)) == NULL)
return -1;
}
for (ptr = *buf, eptr = *buf + *bufsiz;;) {
int c = fgetc(fp);
if (c == -1) {
if (feof(fp)) {
ssize_t diff = (ssize_t)(ptr - *buf);
if (diff != 0) {
*ptr = '\0';
return diff;
}
}
return -1;
}
*ptr++ = c;
if (c == delimiter) {
*ptr = '\0';
return ptr - *buf;
}
if (ptr + 2 >= eptr) {
char *nbuf;
size_t nbufsiz = *bufsiz * 2;
ssize_t d = ptr - *buf;
if ((nbuf = realloc(*buf, nbufsiz)) == NULL)
return -1;
*buf = nbuf;
*bufsiz = nbufsiz;
eptr = nbuf + nbufsiz;
ptr = nbuf + d;
}
}
}
ssize_t getline(char **buf, size_t *bufsiz, FILE *fp)
{
return getdelim(buf, bufsiz, '\n', fp);
}
#endif
#if defined(__ANDROID_API__) && __ANDROID_API__ < 24
#if defined(__ANDROID_MIN_SDK_VERSION__) && __ANDROID_MIN_SDK_VERSION__ < 24
#undef FILE_IS_EMPTY
#include <string.h>
+2 -8
View File
@@ -1,16 +1,10 @@
/* SPDX-License-Identifier: BSD
*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
*
*/
#if defined(__ANDROID_API__) && __ANDROID_API__ < 18
#include <stdio.h>
ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp);
ssize_t getline(char **buf, size_t *bufsiz, FILE *fp);
#endif
#if defined(__ANDROID_API__) && __ANDROID_API__ < 24
#if defined(__ANDROID_MIN_SDK_VERSION__) && __ANDROID_MIN_SDK_VERSION__ < 24
char *strchrnul(const char *s, int c);
#endif
+17 -29
View File
@@ -1,3 +1,5 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
@@ -7,21 +9,18 @@ plugins {
version wireguardVersionName
group groupName
// Create a variable called keystorePropertiesFile, and initialize it to your
// keystore.properties file, in the rootProject folder.
final def keystorePropertiesFile = rootProject.file("keystore.properties")
android {
compileSdkVersion 30
compileSdk 33
buildFeatures {
buildConfig = true
dataBinding = true
viewBinding = true
}
namespace = 'com.wireguard.android'
defaultConfig {
applicationId 'com.wireguard.android'
minSdkVersion 21
targetSdkVersion 30
targetSdkVersion 33
versionCode wireguardVersionCode
versionName wireguardVersionName
buildConfigField 'int', 'MIN_SDK_VERSION', "$minSdkVersion.apiLevel"
@@ -31,32 +30,24 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
coreLibraryDesugaringEnabled = true
}
if (keystorePropertiesFile.exists()) {
final def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile rootProject.file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
}
buildTypes {
release {
if (keystorePropertiesFile.exists()) signingConfig signingConfigs.release
minifyEnabled true
proguardFiles "proguard-android-optimize.txt", "proguard-rules.pro"
shrinkResources true
proguardFiles "proguard-android-optimize.txt"
packagingOptions {
exclude "DebugProbesKt.bin"
exclude "kotlin-tooling-metadata.json"
}
}
debug {
applicationIdSuffix ".debug"
versionNameSuffix "-debug"
}
}
lintOptions {
disable('LongLogTag')
lint {
disable 'LongLogTag'
warning 'MissingTranslation', 'ImpliedQuantity'
}
}
@@ -80,14 +71,11 @@ dependencies {
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:$desugarVersion"
}
tasks.withType(JavaCompile) {
tasks.withType(JavaCompile).configureEach {
options.compilerArgs << '-Xlint:unchecked'
options.deprecation = true
}
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
useIR = true
}
tasks.withType(KotlinCompile).configureEach {
kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8
}
+1 -2
View File
@@ -1,7 +1,6 @@
-allowaccessmodification
-dontpreverify
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontobfuscate
-verbose
-keepattributes *Annotation*
-14
View File
@@ -1,14 +0,0 @@
# Squelch all warnings, they're harmless but ProGuard
# escalates them as errors.
-dontwarn sun.misc.Unsafe
# Fragment 1.2.4 allows Fragment classes to be obfuscated but
# databinding references in XML seem to not be rewritten to
# match, so we preserve the names as 1.2.3 did.
-if public class ** extends androidx.fragment.app.Fragment
-keep public class <1> {
public <init>();
}
# Don't obfuscate
-dontobfuscate
+16 -11
View File
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.wireguard.android"
android:installLocation="internalOnly">
<uses-permission android:name="android.permission.CAMERA" />
@@ -21,6 +20,9 @@
<uses-feature
android:name="android.hardware.camera.any"
android:required="false" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<permission
android:name="${applicationId}.permission.CONTROL_TUNNELS"
@@ -29,13 +31,11 @@
android:label="@string/permission_label"
android:protectionLevel="dangerous" />
<!-- This is safe to override, the APIs we use work perfectly fine on Android 6 -->
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
<application
android:name=".Application"
android:allowBackup="false"
android:banner="@mipmap/banner"
android:enableOnBackInvokedCallback="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
@@ -44,9 +44,10 @@
<activity
android:name=".activity.TunnelToggleActivity"
android:theme="@style/NoBackgroundTheme" />
android:theme="@style/NoBackgroundTheme"
android:excludeFromRecents="true"/>
<activity android:name=".activity.MainActivity">
<activity android:name=".activity.MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -60,7 +61,8 @@
<activity
android:name=".activity.TvMainActivity"
android:theme="@style/TvTheme">
android:theme="@style/TvTheme"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
@@ -84,7 +86,8 @@
<activity
android:name=".activity.LogViewerActivity"
android:label="@string/log_viewer_title">
android:label="@string/log_viewer_title"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
@@ -96,7 +99,7 @@
android:exported="false"
android:grantUriPermissions="true" />
<receiver android:name=".BootShutdownReceiver">
<receiver android:name=".BootShutdownReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.ACTION_SHUTDOWN" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
@@ -105,7 +108,8 @@
<receiver
android:name=".model.TunnelManager$IntentReceiver"
android:permission="${applicationId}.permission.CONTROL_TUNNELS">
android:permission="${applicationId}.permission.CONTROL_TUNNELS"
android:exported="true">
<intent-filter>
<action android:name="com.wireguard.android.action.REFRESH_TUNNEL_STATES" />
<action android:name="com.wireguard.android.action.SET_TUNNEL_UP" />
@@ -116,7 +120,8 @@
<service
android:name=".QuickTileService"
android:icon="@drawable/ic_tile"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
android:exported="true">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android
@@ -16,12 +16,12 @@ import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStoreFile
import com.google.android.material.color.DynamicColors
import com.wireguard.android.backend.Backend
import com.wireguard.android.backend.GoBackend
import com.wireguard.android.backend.WgQuickBackend
import com.wireguard.android.configStore.FileConfigStore
import com.wireguard.android.model.TunnelManager
import com.wireguard.android.util.ModuleLoader
import com.wireguard.android.util.RootShell
import com.wireguard.android.util.ToolsInstaller
import com.wireguard.android.util.UserKnobs
@@ -35,6 +35,7 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.lang.ref.WeakReference
import java.util.Locale
@@ -42,7 +43,6 @@ class Application : android.app.Application() {
private val futureBackend = CompletableDeferred<Backend>()
private val coroutineScope = CoroutineScope(Job() + Dispatchers.Main.immediate)
private var backend: Backend? = null
private lateinit var moduleLoader: ModuleLoader
private lateinit var rootShell: RootShell
private lateinit var preferencesDataStore: DataStore<Preferences>
private lateinit var toolsInstaller: ToolsInstaller
@@ -58,27 +58,13 @@ class Application : android.app.Application() {
startActivity(intent)
System.exit(0)
}
if (BuildConfig.DEBUG) {
StrictMode.setVmPolicy(VmPolicy.Builder().detectAll().penaltyLog().build())
StrictMode.setThreadPolicy(ThreadPolicy.Builder().detectAll().penaltyLog().build())
}
}
private suspend fun determineBackend(): Backend {
var backend: Backend? = null
var didStartRootShell = false
if (!ModuleLoader.isModuleLoaded() && moduleLoader.moduleMightExist()) {
if (UserKnobs.enableKernelModule.first() && WgQuickBackend.hasKernelSupport()) {
try {
rootShell.start()
didStartRootShell = true
moduleLoader.loadModule()
} catch (ignored: Exception) {
}
}
if (!UserKnobs.disableKernelModule.first() && ModuleLoader.isModuleLoaded()) {
try {
if (!didStartRootShell)
rootShell.start()
val wgQuickBackend = WgQuickBackend(applicationContext, rootShell, toolsInstaller)
wgQuickBackend.setMultipleTunnels(UserKnobs.multipleTunnels.first())
backend = wgQuickBackend
@@ -98,15 +84,24 @@ class Application : android.app.Application() {
override fun onCreate() {
Log.i(TAG, USER_AGENT)
super.onCreate()
DynamicColors.applyToActivitiesIfAvailable(this)
rootShell = RootShell(applicationContext)
toolsInstaller = ToolsInstaller(applicationContext, rootShell)
moduleLoader = ModuleLoader(applicationContext, rootShell, USER_AGENT)
preferencesDataStore = PreferenceDataStoreFactory.create { applicationContext.preferencesDataStoreFile("settings") }
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
coroutineScope.launch {
AppCompatDelegate.setDefaultNightMode(
if (UserKnobs.darkTheme.first()) AppCompatDelegate.MODE_NIGHT_YES else AppCompatDelegate.MODE_NIGHT_NO)
runBlocking {
AppCompatDelegate.setDefaultNightMode(if (UserKnobs.darkTheme.first()) AppCompatDelegate.MODE_NIGHT_YES else AppCompatDelegate.MODE_NIGHT_NO)
}
UserKnobs.darkTheme.onEach {
val newMode = if (it) {
AppCompatDelegate.MODE_NIGHT_YES
} else {
AppCompatDelegate.MODE_NIGHT_NO
}
if (AppCompatDelegate.getDefaultNightMode() != newMode) {
AppCompatDelegate.setDefaultNightMode(newMode)
}
}.launchIn(coroutineScope)
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
}
@@ -120,6 +115,11 @@ class Application : android.app.Application() {
Log.e(TAG, Log.getStackTraceString(e))
}
}
if (BuildConfig.DEBUG) {
StrictMode.setVmPolicy(VmPolicy.Builder().detectAll().penaltyLog().build())
StrictMode.setThreadPolicy(ThreadPolicy.Builder().detectAll().penaltyLog().build())
}
}
override fun onTerminate() {
@@ -132,30 +132,20 @@ class Application : android.app.Application() {
private const val TAG = "WireGuard/Application"
private lateinit var weakSelf: WeakReference<Application>
@JvmStatic
fun get(): Application {
return weakSelf.get()!!
}
@JvmStatic
suspend fun getBackend() = get().futureBackend.await()
@JvmStatic
fun getModuleLoader() = get().moduleLoader
@JvmStatic
fun getRootShell() = get().rootShell
@JvmStatic
fun getPreferencesDataStore() = get().preferencesDataStore
@JvmStatic
fun getToolsInstaller() = get().toolsInstaller
@JvmStatic
fun getTunnelManager() = get().tunnelManager
@JvmStatic
fun getCoroutineScope() = get().coroutineScope
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android
@@ -1,28 +1,36 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.activity
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.CallbackRegistry
import androidx.databinding.CallbackRegistry.NotifierCallback
import androidx.lifecycle.lifecycleScope
import com.wireguard.android.Application
import com.wireguard.android.model.ObservableTunnel
import kotlinx.coroutines.launch
/**
* Base class for activities that need to remember the currently-selected tunnel.
*/
abstract class BaseActivity : ThemeChangeAwareActivity() {
abstract class BaseActivity : AppCompatActivity() {
private val selectionChangeRegistry = SelectionChangeRegistry()
private var created = false
var selectedTunnel: ObservableTunnel? = null
set(value) {
val oldTunnel = field
if (oldTunnel == value) return
field = value
onSelectedTunnelChanged(oldTunnel, value)
selectionChangeRegistry.notifyCallbacks(oldTunnel, 0, value)
if (created) {
if (!onSelectedTunnelChanged(oldTunnel, value)) {
field = oldTunnel
} else {
selectionChangeRegistry.notifyCallbacks(oldTunnel, 0, value)
}
}
}
fun addOnSelectedTunnelChangedListener(listener: OnSelectedTunnelChangedListener) {
@@ -30,17 +38,25 @@ abstract class BaseActivity : ThemeChangeAwareActivity() {
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Restore the saved tunnel if there is one; otherwise grab it from the arguments.
val savedTunnelName = when {
savedInstanceState != null -> savedInstanceState.getString(KEY_SELECTED_TUNNEL)
intent != null -> intent.getStringExtra(KEY_SELECTED_TUNNEL)
else -> null
}
if (savedTunnelName != null)
lifecycleScope.launchWhenCreated { selectedTunnel = Application.getTunnelManager().getTunnels()[savedTunnelName] }
// The selected tunnel must be set before the superclass method recreates fragments.
super.onCreate(savedInstanceState)
if (savedTunnelName != null) {
lifecycleScope.launch {
val tunnel = Application.getTunnelManager().getTunnels()[savedTunnelName]
if (tunnel == null)
created = true
selectedTunnel = tunnel
created = true
}
} else {
created = true
}
}
override fun onSaveInstanceState(outState: Bundle) {
@@ -48,7 +64,7 @@ abstract class BaseActivity : ThemeChangeAwareActivity() {
super.onSaveInstanceState(outState)
}
protected abstract fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?, newTunnel: ObservableTunnel?)
protected abstract fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?, newTunnel: ObservableTunnel?): Boolean
fun removeOnSelectedTunnelChangedListener(
listener: OnSelectedTunnelChangedListener) {
@@ -1,5 +1,5 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -27,6 +27,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.collection.CircularArray
import androidx.core.app.ShareCompat
import androidx.core.content.res.ResourcesCompat
import androidx.lifecycle.lifecycleScope
@@ -40,6 +41,7 @@ import com.wireguard.android.R
import com.wireguard.android.databinding.LogViewerActivityBinding
import com.wireguard.android.util.DownloadsFileSaver
import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.util.resolveAttribute
import com.wireguard.crypto.KeyPair
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -61,8 +63,8 @@ import java.util.regex.Pattern
class LogViewerActivity : AppCompatActivity() {
private lateinit var binding: LogViewerActivityBinding
private lateinit var logAdapter: LogEntryAdapter
private var logLines = arrayListOf<LogLine>()
private var rawLogLines = StringBuffer()
private var logLines = CircularArray<LogLine>()
private var rawLogLines = CircularArray<String>()
private var recyclerView: RecyclerView? = null
private var saveButton: MenuItem? = null
private val year by lazy {
@@ -70,7 +72,7 @@ class LogViewerActivity : AppCompatActivity() {
yearFormatter.format(Date())
}
private val defaultColor by lazy { ResourcesCompat.getColor(resources, R.color.primary_text_color, theme) }
private val defaultColor by lazy { resolveAttribute(com.google.android.material.R.attr.colorOnSurface) }
private val debugColor by lazy { ResourcesCompat.getColor(resources, R.color.debug_tag_color, theme) }
@@ -112,7 +114,7 @@ class LogViewerActivity : AppCompatActivity() {
binding.shareFab.setOnClickListener {
revokeLastUri()
val key = KeyPair().privateKey.toHex()
LOGS[key] = rawLogLines.toString().toByteArray(Charsets.UTF_8)
LOGS[key] = rawLogBytes()
lastUri = Uri.parse("content://${BuildConfig.APPLICATION_ID}.exported-log/$key")
val shareIntent = ShareCompat.IntentBuilder(this)
.setType("text/plain")
@@ -126,9 +128,9 @@ class LogViewerActivity : AppCompatActivity() {
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.log_viewer, menu)
saveButton = menu?.findItem(R.id.save_log)
saveButton = menu.findItem(R.id.save_log)
return true
}
@@ -149,13 +151,22 @@ class LogViewerActivity : AppCompatActivity() {
private val downloadsFileSaver = DownloadsFileSaver(this)
private fun rawLogBytes() : ByteArray {
val builder = StringBuilder()
for (i in 0 until rawLogLines.size()) {
builder.append(rawLogLines[i])
builder.append('\n')
}
return builder.toString().toByteArray(Charsets.UTF_8)
}
private suspend fun saveLog() {
var exception: Throwable? = null
var outputFile: DownloadsFileSaver.DownloadsFile? = null
withContext(Dispatchers.IO) {
try {
outputFile = downloadsFileSaver.save("wireguard-log.txt", "text/plain", true)
outputFile?.outputStream?.write(rawLogLines.toString().toByteArray(Charsets.UTF_8))
outputFile?.outputStream?.write(rawLogBytes())
} catch (e: Throwable) {
outputFile?.delete()
exception = e
@@ -184,36 +195,60 @@ class LogViewerActivity : AppCompatActivity() {
return@withContext
}
val stdout = BufferedReader(InputStreamReader(process!!.inputStream, StandardCharsets.UTF_8))
var haveScrolled = false
val start = System.nanoTime()
var startPeriod = start
var posStart = 0
var timeLastNotify = System.nanoTime()
var priorModified = false
val bufferedLogLines = arrayListOf<LogLine>()
var timeout = 1000000000L / 2 // The timeout is initially small so that the view gets populated immediately.
val MAX_LINES = (1 shl 16) - 1
val MAX_BUFFERED_LINES = (1 shl 14) - 1
while (true) {
val line = stdout.readLine() ?: break
rawLogLines.append(line)
rawLogLines.append('\n')
if (rawLogLines.size() >= MAX_LINES)
rawLogLines.popFirst()
rawLogLines.addLast(line)
val logLine = parseLine(line)
withContext(Dispatchers.Main.immediate) {
if (logLine != null) {
recyclerView?.let {
val shouldScroll = haveScrolled && !it.canScrollVertically(1)
logLines.add(logLine)
if (haveScrolled) logAdapter.notifyDataSetChanged()
if (shouldScroll)
it.scrollToPosition(logLines.size - 1)
}
} else {
logLines.lastOrNull()?.msg += "\n$line"
if (haveScrolled) logAdapter.notifyDataSetChanged()
if (logLine != null) {
bufferedLogLines.add(logLine)
} else {
if (bufferedLogLines.isNotEmpty()) {
bufferedLogLines.last().msg += "\n$line"
} else if (!logLines.isEmpty) {
logLines[logLines.size() - 1].msg += "\n$line"
priorModified = true
}
if (!haveScrolled) {
val end = System.nanoTime()
val scroll = (end - start) > 1000000000L * 2.5 || !stdout.ready()
if (logLines.isNotEmpty() && (scroll || (end - startPeriod) > 1000000000L / 4)) {
logAdapter.notifyDataSetChanged()
recyclerView?.scrollToPosition(logLines.size - 1)
startPeriod = end
}
if (scroll) haveScrolled = true
}
val timeNow = System.nanoTime()
if (bufferedLogLines.size < MAX_BUFFERED_LINES && (timeNow - timeLastNotify) < timeout && stdout.ready())
continue
timeout = 1000000000L * 5 / 2 // Increase the timeout after the initial view has something in it.
timeLastNotify = timeNow
withContext(Dispatchers.Main.immediate) {
val isScrolledToBottomAlready = recyclerView?.canScrollVertically(1) == false
if (priorModified) {
logAdapter.notifyItemChanged(posStart - 1)
priorModified = false
}
val fullLen = logLines.size() + bufferedLogLines.size
if (fullLen >= MAX_LINES) {
val numToRemove = fullLen - MAX_LINES + 1
logLines.removeFromStart(numToRemove)
logAdapter.notifyItemRangeRemoved(0, numToRemove)
posStart -= numToRemove
}
for (bufferedLine in bufferedLogLines) {
logLines.addLast(bufferedLine)
}
bufferedLogLines.clear()
logAdapter.notifyItemRangeInserted(posStart, logLines.size() - posStart)
posStart = logLines.size()
if (isScrolledToBottomAlready) {
recyclerView?.scrollToPosition(logLines.size() - 1)
}
}
}
@@ -259,7 +294,7 @@ class LogViewerActivity : AppCompatActivity() {
private fun levelToColor(level: String): Int {
return when (level) {
"D" -> debugColor
"V", "D" -> debugColor
"E" -> errorColor
"I" -> infoColor
"W" -> warningColor
@@ -267,7 +302,7 @@ class LogViewerActivity : AppCompatActivity() {
}
}
override fun getItemCount() = logLines.size
override fun getItemCount() = logLines.size()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.activity
@@ -9,6 +9,8 @@ import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.activity.addCallback
import androidx.appcompat.app.ActionBar
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
@@ -26,27 +28,29 @@ import com.wireguard.android.model.ObservableTunnel
class MainActivity : BaseActivity(), FragmentManager.OnBackStackChangedListener {
private var actionBar: ActionBar? = null
private var isTwoPaneLayout = false
private var backPressedCallback: OnBackPressedCallback? = null
override fun onBackPressed() {
private fun handleBackPressed() {
val backStackEntries = supportFragmentManager.backStackEntryCount
// If the two-pane layout does not have an editor open, going back should exit the app.
if (isTwoPaneLayout && backStackEntries <= 1) {
finish()
return
}
// Deselect the current tunnel on navigating back from the detail pane to the one-pane list.
if (!isTwoPaneLayout && backStackEntries == 1) {
if (backStackEntries >= 1)
supportFragmentManager.popBackStack()
// Deselect the current tunnel on navigating back from the detail pane to the one-pane list.
if (backStackEntries == 1)
selectedTunnel = null
return
}
super.onBackPressed()
}
override fun onBackStackChanged() {
val backStackEntries = supportFragmentManager.backStackEntryCount
backPressedCallback?.isEnabled = backStackEntries >= 1
if (actionBar == null) return
// Do not show the home menu when the two-pane layout is at the detail view (see above).
val backStackEntries = supportFragmentManager.backStackEntryCount
val minBackStackEntries = if (isTwoPaneLayout) 2 else 1
actionBar!!.setDisplayHomeAsUpEnabled(backStackEntries >= minBackStackEntries)
}
@@ -57,6 +61,7 @@ class MainActivity : BaseActivity(), FragmentManager.OnBackStackChangedListener
actionBar = supportActionBar
isTwoPaneLayout = findViewById<View?>(R.id.master_detail_wrapper) != null
supportFragmentManager.addOnBackStackChangedListener(this)
backPressedCallback = onBackPressedDispatcher.addCallback(this) { handleBackPressed() }
onBackStackChanged()
}
@@ -69,7 +74,7 @@ class MainActivity : BaseActivity(), FragmentManager.OnBackStackChangedListener
return when (item.itemId) {
android.R.id.home -> {
// The back arrow in the action bar should act the same as the back button.
onBackPressed()
onBackPressedDispatcher.onBackPressed()
true
}
R.id.menu_action_edit -> {
@@ -91,13 +96,17 @@ class MainActivity : BaseActivity(), FragmentManager.OnBackStackChangedListener
}
override fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?,
newTunnel: ObservableTunnel?) {
newTunnel: ObservableTunnel?): Boolean {
val fragmentManager = supportFragmentManager
if (fragmentManager.isStateSaved) {
return false
}
val backStackEntries = fragmentManager.backStackEntryCount
if (newTunnel == null) {
// Clear everything off the back stack (all editors and detail fragments).
fragmentManager.popBackStackImmediate(0, FragmentManager.POP_BACK_STACK_INCLUSIVE)
return
return true
}
if (backStackEntries == 2) {
// Pop the editor off the back stack to reveal the detail fragment. Use the immediate
@@ -111,5 +120,6 @@ class MainActivity : BaseActivity(), FragmentManager.OnBackStackChangedListener
addToBackStack(null)
}
}
return true
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.activity
@@ -8,6 +8,7 @@ import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.commit
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
@@ -17,7 +18,6 @@ import com.wireguard.android.R
import com.wireguard.android.backend.WgQuickBackend
import com.wireguard.android.preference.PreferencesPreferenceDataStore
import com.wireguard.android.util.AdminKnobs
import com.wireguard.android.util.ModuleLoader
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -25,7 +25,7 @@ import kotlinx.coroutines.withContext
/**
* Interface for changing application-global persistent settings.
*/
class SettingsActivity : ThemeChangeAwareActivity() {
class SettingsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (supportFragmentManager.findFragmentById(android.R.id.content) == null) {
@@ -79,30 +79,19 @@ class SettingsActivity : ThemeChangeAwareActivity() {
startActivity(Intent(requireContext(), LogViewerActivity::class.java))
true
}
val moduleInstaller = preferenceManager.findPreference<Preference>("module_downloader")
val kernelModuleDisabler = preferenceManager.findPreference<Preference>("kernel_module_disabler")
moduleInstaller?.isVisible = false
if (ModuleLoader.isModuleLoaded()) {
moduleInstaller?.parent?.removePreference(moduleInstaller)
val kernelModuleEnabler = preferenceManager.findPreference<Preference>("kernel_module_enabler")
if (WgQuickBackend.hasKernelSupport()) {
lifecycleScope.launch {
if (Application.getBackend() !is WgQuickBackend) {
try {
withContext(Dispatchers.IO) { Application.getRootShell().start() }
} catch (_: Throwable) {
kernelModuleDisabler?.parent?.removePreference(kernelModuleDisabler)
kernelModuleEnabler?.parent?.removePreference(kernelModuleEnabler)
}
}
}
} else {
kernelModuleDisabler?.parent?.removePreference(kernelModuleDisabler)
lifecycleScope.launch {
try {
withContext(Dispatchers.IO) { Application.getRootShell().start() }
moduleInstaller?.isVisible = true
} catch (_: Throwable) {
moduleInstaller?.parent?.removePreference(moduleInstaller)
}
}
kernelModuleEnabler?.parent?.removePreference(kernelModuleEnabler)
}
}
}
@@ -1,33 +0,0 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.activity
import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.lifecycle.lifecycleScope
import com.wireguard.android.util.UserKnobs
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
abstract class ThemeChangeAwareActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
UserKnobs.darkTheme.onEach {
val newMode = if (it) {
AppCompatDelegate.MODE_NIGHT_YES
} else {
AppCompatDelegate.MODE_NIGHT_NO
}
if (AppCompatDelegate.getDefaultNightMode() != newMode) {
AppCompatDelegate.setDefaultNightMode(newMode)
recreate()
}
}.launchIn(lifecycleScope)
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.activity
@@ -22,7 +22,8 @@ class TunnelCreatorActivity : BaseActivity() {
}
}
override fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?, newTunnel: ObservableTunnel?) {
override fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?, newTunnel: ObservableTunnel?): Boolean {
finish()
return true
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.activity
@@ -1,11 +1,14 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.activity
import android.Manifest
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
@@ -16,17 +19,21 @@ import android.os.storage.StorageVolume
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.activity.addCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import androidx.core.view.forEach
import androidx.databinding.DataBindingUtil
import androidx.databinding.Observable
import androidx.databinding.ObservableBoolean
import androidx.databinding.ObservableField
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.wireguard.android.Application
import com.wireguard.android.R
import com.wireguard.android.backend.GoBackend
@@ -41,6 +48,8 @@ import com.wireguard.android.model.ObservableTunnel
import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.util.QuantityFormatter
import com.wireguard.android.util.TunnelImporter
import com.wireguard.android.util.UserKnobs
import com.wireguard.android.util.applicationScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -48,7 +57,27 @@ import kotlinx.coroutines.withContext
import java.io.File
class TvMainActivity : AppCompatActivity() {
private val tunnelFileImportResultLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { data ->
private val tunnelFileImportResultLauncher = registerForActivityResult(object : ActivityResultContracts.GetContent() {
override fun createIntent(context: Context, input: String): Intent {
val intent = super.createIntent(context, input)
/* AndroidTV now comes with stubs that do nothing but display a Toast less helpful than
* what we can do, so detect this and throw an exception that we can catch later. */
val activitiesToResolveIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.packageManager.queryIntentActivities(intent, PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong()))
} else {
@Suppress("DEPRECATION")
context.packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
}
if (activitiesToResolveIntent.all {
val name = it.activityInfo.packageName
name.startsWith("com.google.android.tv.frameworkpackagestubs") || name.startsWith("com.android.tv.frameworkpackagestubs")
}) {
throw ActivityNotFoundException()
}
return intent
}
}) { data ->
if (data == null) return@registerForActivityResult
lifecycleScope.launch {
TunnelImporter.importTunnel(contentResolver, data) {
@@ -84,6 +113,14 @@ class TvMainActivity : AppCompatActivity() {
private val filesRoot = ObservableField("")
override fun onCreate(savedInstanceState: Bundle?) {
if (AppCompatDelegate.getDefaultNightMode() != AppCompatDelegate.MODE_NIGHT_YES) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
applicationScope.launch {
UserKnobs.setDarkTheme(true)
}
}
}
super.onCreate(savedInstanceState)
binding = TvActivityBinding.inflate(layoutInflater)
lifecycleScope.launch {
@@ -174,7 +211,12 @@ class TvMainActivity : AppCompatActivity() {
try {
tunnelFileImportResultLauncher.launch("*/*")
} catch (_: Throwable) {
Toast.makeText(this@TvMainActivity, getString(R.string.tv_no_file_picker), Toast.LENGTH_LONG).show()
MaterialAlertDialogBuilder(binding.root.context).setMessage(R.string.tv_no_file_picker).setCancelable(false).setPositiveButton(android.R.string.ok) { _, _ ->
try {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://webstoreredirect")))
} catch (_: Throwable) {
}
}.show()
}
}
}
@@ -185,6 +227,17 @@ class TvMainActivity : AppCompatActivity() {
binding.tunnelList.requestFocus()
}
}
val backPressedCallback = onBackPressedDispatcher.addCallback(this) { handleBackPressed() }
val updateBackPressedCallback = object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
backPressedCallback.isEnabled = isDeleting.get() || filesRoot.get()?.isNotEmpty() == true
}
}
isDeleting.addOnPropertyChangedCallback(updateBackPressedCallback)
filesRoot.addOnPropertyChangedCallback(updateBackPressedCallback)
backPressedCallback.isEnabled = false
binding.executePendingBindings()
setContentView(binding.root)
@@ -298,7 +351,7 @@ class TvMainActivity : AppCompatActivity() {
}
}
override fun onBackPressed() {
private fun handleBackPressed() {
when {
isDeleting.get() -> {
isDeleting.set(false)
@@ -313,7 +366,6 @@ class TvMainActivity : AppCompatActivity() {
binding.tunnelList.requestFocus()
}
}
else -> super.onBackPressed()
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.configStore
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.configStore
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.databinding
@@ -23,6 +23,7 @@ import com.wireguard.android.R
import com.wireguard.android.databinding.ObservableKeyedRecyclerViewAdapter.RowConfigurationHandler
import com.wireguard.android.widget.ToggleSwitch
import com.wireguard.android.widget.ToggleSwitch.OnBeforeCheckedChangeListener
import com.wireguard.android.widget.TvCardView
import com.wireguard.config.Attribute
import com.wireguard.config.InetNetwork
import java.net.InetAddress
@@ -168,4 +169,16 @@ object BindingAdapters {
0
}
}
@JvmStatic
@BindingAdapter("isUp")
fun setIsUp(card: TvCardView, up: Boolean) {
card.isUp = up
}
@JvmStatic
@BindingAdapter("isDeleting")
fun setIsDeleting(card: TvCardView, deleting: Boolean) {
card.isDeleting = deleting
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.databinding
@@ -1,5 +1,5 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.databinding
@@ -1,5 +1,5 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.databinding
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.databinding
@@ -1,5 +1,5 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.databinding
@@ -1,5 +1,5 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.fragment
@@ -34,10 +34,6 @@ class AddTunnelsSheet : BottomSheetDialogFragment() {
}
}
override fun getTheme(): Int {
return R.style.BottomSheetDialogTheme
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
if (savedInstanceState != null) dismiss()
val view = inflater.inflate(R.layout.add_tunnels_bottom_sheet, container, false)
@@ -76,7 +72,7 @@ class AddTunnelsSheet : BottomSheetDialogFragment() {
}
})
val gradientDrawable = GradientDrawable().apply {
setColor(requireContext().resolveAttribute(R.attr.colorBackground))
setColor(requireContext().resolveAttribute(com.google.android.material.R.attr.colorSurface))
}
view.background = gradientDrawable
}
@@ -1,11 +1,15 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.fragment
import android.Manifest
import android.app.Dialog
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.PackageInfoFlags
import android.os.Build
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
@@ -15,6 +19,7 @@ import androidx.databinding.Observable
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.setFragmentResult
import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.tabs.TabLayout
import com.wireguard.android.BR
import com.wireguard.android.R
@@ -40,7 +45,7 @@ class AppListDialogFragment : DialogFragment() {
try {
val applicationData: MutableList<ApplicationData> = ArrayList()
withContext(Dispatchers.IO) {
val packageInfos = pm.getPackagesHoldingPermissions(arrayOf(Manifest.permission.INTERNET), 0)
val packageInfos = getPackagesHoldingPermissions(pm, arrayOf(Manifest.permission.INTERNET))
packageInfos.forEach {
val packageName = it.packageName
val appInfo = it.applicationInfo
@@ -58,6 +63,7 @@ class AppListDialogFragment : DialogFragment() {
withContext(Dispatchers.Main.immediate) {
appData.clear()
appData.addAll(applicationData)
setButtonText()
}
} catch (e: Throwable) {
withContext(Dispatchers.Main.immediate) {
@@ -76,6 +82,15 @@ class AppListDialogFragment : DialogFragment() {
initiallyExcluded = arguments?.getBoolean(KEY_IS_EXCLUDED) ?: true
}
private fun getPackagesHoldingPermissions(pm: PackageManager, permissions: Array<String>): List<PackageInfo> {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
pm.getPackagesHoldingPermissions(permissions, PackageInfoFlags.of(0L))
} else {
@Suppress("DEPRECATION")
pm.getPackagesHoldingPermissions(permissions, 0)
}
}
private fun setButtonText() {
val numSelected = appData.count { it.isSelected }
button?.text = if (numSelected == 0)
@@ -88,7 +103,7 @@ class AppListDialogFragment : DialogFragment() {
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val alertDialogBuilder = AlertDialog.Builder(requireActivity())
val alertDialogBuilder = MaterialAlertDialogBuilder(requireActivity())
val binding = AppListDialogFragmentBinding.inflate(requireActivity().layoutInflater, null, false)
binding.executePendingBindings()
alertDialogBuilder.setView(binding.root)
@@ -139,6 +154,7 @@ class AppListDialogFragment : DialogFragment() {
const val KEY_SELECTED_APPS = "selected_apps"
const val KEY_IS_EXCLUDED = "is_excluded"
const val REQUEST_SELECTION = "request_selection"
fun newInstance(selectedApps: ArrayList<String?>?, isExcluded: Boolean): AppListDialogFragment {
val extras = Bundle()
extras.putStringArrayList(KEY_SELECTED_APPS, selectedApps)
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.fragment
@@ -67,12 +67,20 @@ abstract class BaseFragment : Fragment(), OnSelectedTunnelChangedListener {
val activity = activity ?: return
activity.lifecycleScope.launch {
if (Application.getBackend() is GoBackend) {
val intent = GoBackend.VpnService.prepare(activity)
if (intent != null) {
pendingTunnel = tunnel
pendingTunnelUp = checked
permissionActivityResultLauncher.launch(intent)
return@launch
try {
val intent = GoBackend.VpnService.prepare(activity)
if (intent != null) {
pendingTunnel = tunnel
pendingTunnelUp = checked
permissionActivityResultLauncher.launch(intent)
return@launch
}
} catch (e: Throwable) {
val message = activity.getString(R.string.error_prepare, ErrorMessages[e])
Snackbar.make(view, message, Snackbar.LENGTH_LONG)
.setAnchorView(view.findViewById(R.id.create_fab))
.show()
Log.e(TAG, message, e)
}
}
setTunnelStateWithPermissionsResult(tunnel, checked)
@@ -1,18 +1,15 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.fragment
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import android.view.inputmethod.InputMethodManager
import androidx.appcompat.app.AlertDialog
import androidx.core.content.getSystemService
import android.view.WindowManager
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.wireguard.android.Application
import com.wireguard.android.R
import com.wireguard.android.databinding.ConfigNamingDialogFragmentBinding
@@ -26,7 +23,6 @@ import java.nio.charset.StandardCharsets
class ConfigNamingDialogFragment : DialogFragment() {
private var binding: ConfigNamingDialogFragmentBinding? = null
private var config: Config? = null
private var imm: InputMethodManager? = null
private fun createTunnelAndDismiss() {
val binding = binding ?: return
@@ -41,12 +37,6 @@ class ConfigNamingDialogFragment : DialogFragment() {
}
}
}
override fun dismiss() {
setKeyboardVisible(false)
super.dismiss()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val configText = requireArguments().getString(KEY_CONFIG_TEXT)
@@ -63,51 +53,23 @@ class ConfigNamingDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val activity = requireActivity()
imm = activity.getSystemService()
val alertDialogBuilder = AlertDialog.Builder(activity)
val alertDialogBuilder = MaterialAlertDialogBuilder(activity)
alertDialogBuilder.setTitle(R.string.import_from_qr_code)
binding = ConfigNamingDialogFragmentBinding.inflate(activity.layoutInflater, null, false)
binding?.apply {
executePendingBindings()
alertDialogBuilder.setView(root)
}
alertDialogBuilder.setPositiveButton(R.string.create_tunnel, null)
alertDialogBuilder.setPositiveButton(R.string.create_tunnel) { _, _ -> createTunnelAndDismiss() }
alertDialogBuilder.setNegativeButton(R.string.cancel) { _, _ -> dismiss() }
return alertDialogBuilder.create().apply {
setOnShowListener {
findViewById<TextInputEditText>(R.id.tunnel_name_text)?.apply {
setOnFocusChangeListener { v, _ ->
v.post {
imm?.showSoftInput(v, InputMethodManager.SHOW_IMPLICIT)
}
}
requestFocus()
}
}
}
}
override fun onResume() {
super.onResume()
val dialog = dialog as AlertDialog?
if (dialog != null) {
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener { createTunnelAndDismiss() }
setKeyboardVisible(true)
}
}
private fun setKeyboardVisible(visible: Boolean) {
if (visible) {
imm!!.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0)
} else if (binding != null) {
imm!!.hideSoftInputFromWindow(binding!!.tunnelNameText.windowToken, 0)
}
val dialog = alertDialogBuilder.create()
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
return dialog
}
companion object {
private const val KEY_CONFIG_TEXT = "config_text"
@JvmStatic
fun newInstance(configText: String?): ConfigNamingDialogFragment {
val extras = Bundle()
extras.putString(KEY_CONFIG_TEXT, configText)
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.fragment
@@ -8,9 +8,12 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.core.view.MenuProvider
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import com.wireguard.android.R
import com.wireguard.android.backend.Tunnel
@@ -24,18 +27,17 @@ import kotlinx.coroutines.launch
/**
* Fragment that shows details about a specific tunnel.
*/
class TunnelDetailFragment : BaseFragment() {
class TunnelDetailFragment : BaseFragment(), MenuProvider {
private var binding: TunnelDetailFragmentBinding? = null
private var lastState = Tunnel.State.TOGGLE
private var timerActive = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return false
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.tunnel_detail, menu)
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.tunnel_detail, menu)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
@@ -46,6 +48,11 @@ class TunnelDetailFragment : BaseFragment() {
return binding?.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
}
override fun onDestroyView() {
binding = null
super.onDestroyView()
@@ -105,16 +112,25 @@ class TunnelDetailFragment : BaseFragment() {
val peer: TunnelDetailPeerBinding = DataBindingUtil.getBinding(binding.peersLayout.getChildAt(i))
?: continue
val publicKey = peer.item!!.publicKey
val rx = statistics.peerRx(publicKey)
val tx = statistics.peerTx(publicKey)
if (rx == 0L && tx == 0L) {
val peerStats = statistics.peer(publicKey)
if (peerStats == null || (peerStats.rxBytes == 0L && peerStats.txBytes == 0L)) {
peer.transferLabel.visibility = View.GONE
peer.transferText.visibility = View.GONE
continue
} else {
peer.transferText.text = getString(R.string.transfer_rx_tx,
QuantityFormatter.formatBytes(peerStats.rxBytes),
QuantityFormatter.formatBytes(peerStats.txBytes))
peer.transferLabel.visibility = View.VISIBLE
peer.transferText.visibility = View.VISIBLE
}
if (peerStats == null || peerStats.latestHandshakeEpochMillis == 0L) {
peer.latestHandshakeLabel.visibility = View.GONE
peer.latestHandshakeText.visibility = View.GONE
} else {
peer.latestHandshakeText.text = QuantityFormatter.formatEpochAgo(peerStats.latestHandshakeEpochMillis)
peer.latestHandshakeLabel.visibility = View.VISIBLE
peer.latestHandshakeText.visibility = View.VISIBLE
}
peer.transferText.text = getString(R.string.transfer_rx_tx, QuantityFormatter.formatBytes(rx), QuantityFormatter.formatBytes(tx))
peer.transferLabel.visibility = View.VISIBLE
peer.transferText.visibility = View.VISIBLE
}
} catch (e: Throwable) {
for (i in 0 until binding.peersLayout.childCount) {
@@ -122,6 +138,8 @@ class TunnelDetailFragment : BaseFragment() {
?: continue
peer.transferLabel.visibility = View.GONE
peer.transferText.visibility = View.GONE
peer.latestHandshakeLabel.visibility = View.GONE
peer.latestHandshakeText.visibility = View.GONE
}
}
}
@@ -1,10 +1,11 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.fragment
import android.content.Context
import android.os.Build
import android.os.Bundle
import android.text.InputType
import android.util.Log
@@ -18,6 +19,9 @@ import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.Toast
import androidx.core.os.BundleCompat
import androidx.core.view.MenuProvider
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import com.google.android.material.snackbar.Snackbar
import com.wireguard.android.Application
@@ -35,7 +39,7 @@ import kotlinx.coroutines.launch
/**
* Fragment for editing a WireGuard configuration.
*/
class TunnelEditorFragment : BaseFragment() {
class TunnelEditorFragment : BaseFragment(), MenuProvider {
private var haveShownKeys = false
private var binding: TunnelEditorFragmentBinding? = null
private var tunnel: ObservableTunnel? = null
@@ -65,11 +69,10 @@ class TunnelEditorFragment : BaseFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.config_editor, menu)
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.config_editor, menu)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
@@ -83,6 +86,11 @@ class TunnelEditorFragment : BaseFragment() {
return binding?.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
}
override fun onDestroyView() {
activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
binding = null
@@ -105,8 +113,8 @@ class TunnelEditorFragment : BaseFragment() {
selectedTunnel = tunnel
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.menu_action_save) {
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
if (menuItem.itemId == R.id.menu_action_save) {
binding ?: return false
val newConfig = try {
binding!!.config!!.resolve()
@@ -152,7 +160,7 @@ class TunnelEditorFragment : BaseFragment() {
}
return true
}
return super.onOptionsItemSelected(item)
return false
}
@Suppress("UNUSED_PARAMETER")
@@ -265,7 +273,7 @@ class TunnelEditorFragment : BaseFragment() {
onSelectedTunnelChanged(null, selectedTunnel)
} else {
tunnel = selectedTunnel
val config: ConfigProxy = savedInstanceState.getParcelable(KEY_LOCAL_CONFIG)!!
val config = BundleCompat.getParcelable(savedInstanceState, KEY_LOCAL_CONFIG, ConfigProxy::class.java)!!
val originalName = savedInstanceState.getString(KEY_ORIGINAL_NAME)
if (tunnel != null && tunnel!!.name != originalName) onSelectedTunnelChanged(null, tunnel) else binding!!.config = config
}
@@ -297,6 +305,7 @@ class TunnelEditorFragment : BaseFragment() {
Snackbar.LENGTH_SHORT
).show()
}
is BiometricAuthenticator.Result.Cancelled -> {}
}
}
} else {
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.fragment
@@ -16,12 +16,16 @@ import android.view.ViewGroup
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.activity.addCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.lifecycle.lifecycleScope
import com.google.android.material.snackbar.Snackbar
import com.google.zxing.integration.android.IntentIntegrator
import com.google.zxing.qrcode.QRCodeReader
import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanOptions
import com.wireguard.android.Application
import com.wireguard.android.R
import com.wireguard.android.activity.TunnelCreatorActivity
@@ -30,14 +34,13 @@ import com.wireguard.android.databinding.TunnelListFragmentBinding
import com.wireguard.android.databinding.TunnelListItemBinding
import com.wireguard.android.model.ObservableTunnel
import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.util.QrCodeFromFileScanner
import com.wireguard.android.util.TunnelImporter
import com.wireguard.android.widget.MultiselectableRelativeLayout
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch
import java.util.ArrayList
import java.util.HashSet
/**
* Fragment containing a list of known WireGuard tunnels. It allows creating and deleting tunnels.
@@ -45,22 +48,36 @@ import java.util.HashSet
class TunnelListFragment : BaseFragment() {
private val actionModeListener = ActionModeListener()
private var actionMode: ActionMode? = null
private var backPressedCallback: OnBackPressedCallback? = null
private var binding: TunnelListFragmentBinding? = null
private val tunnelFileImportResultLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { data ->
if (data == null) return@registerForActivityResult
val activity = activity ?: return@registerForActivityResult
val contentResolver = activity.contentResolver ?: return@registerForActivityResult
activity.lifecycleScope.launch {
TunnelImporter.importTunnel(contentResolver, data) { showSnackbar(it) }
if (QrCodeFromFileScanner.validContentType(contentResolver, data)) {
try {
val qrCodeFromFileScanner = QrCodeFromFileScanner(contentResolver, QRCodeReader())
val result = qrCodeFromFileScanner.scan(data)
TunnelImporter.importTunnel(parentFragmentManager, result.text) { showSnackbar(it) }
} catch (e: Exception) {
val error = ErrorMessages[e]
val message = Application.get().resources.getString(R.string.import_error, error)
Log.e(TAG, message, e)
showSnackbar(message)
}
} else {
TunnelImporter.importTunnel(contentResolver, data) { showSnackbar(it) }
}
}
}
private val qrImportResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val qrCode = IntentIntegrator.parseActivityResult(result.resultCode, result.data)?.contents
?: return@registerForActivityResult
val activity = activity ?: return@registerForActivityResult
val fragManager = parentFragmentManager
activity.lifecycleScope.launch { TunnelImporter.importTunnel(fragManager, qrCode) { showSnackbar(it) } }
private val qrImportResultLauncher = registerForActivityResult(ScanContract()) { result ->
val qrCode = result.contents
val activity = activity
if (qrCode != null && activity != null) {
activity.lifecycleScope.launch { TunnelImporter.importTunnel(parentFragmentManager, qrCode) { showSnackbar(it) } }
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -80,6 +97,8 @@ class TunnelListFragment : BaseFragment() {
val bottomSheet = AddTunnelsSheet()
binding?.apply {
createFab.setOnClickListener {
if (childFragmentManager.findFragmentByTag("BOTTOM_SHEET") != null)
return@setOnClickListener
childFragmentManager.setFragmentResultListener(AddTunnelsSheet.REQUEST_KEY_NEW_TUNNEL, viewLifecycleOwner) { _, bundle ->
when (bundle.getString(AddTunnelsSheet.REQUEST_METHOD)) {
AddTunnelsSheet.REQUEST_CREATE -> {
@@ -89,18 +108,20 @@ class TunnelListFragment : BaseFragment() {
tunnelFileImportResultLauncher.launch("*/*")
}
AddTunnelsSheet.REQUEST_SCAN -> {
qrImportResultLauncher.launch(IntentIntegrator(requireActivity())
qrImportResultLauncher.launch(ScanOptions()
.setOrientationLocked(false)
.setBeepEnabled(false)
.setPrompt(getString(R.string.qr_code_hint))
.createScanIntent())
.setPrompt(getString(R.string.qr_code_hint)))
}
}
}
bottomSheet.show(childFragmentManager, "BOTTOM_SHEET")
bottomSheet.showNow(childFragmentManager, "BOTTOM_SHEET")
}
executePendingBindings()
}
backPressedCallback = requireActivity().onBackPressedDispatcher.addCallback(this) { actionMode?.finish() }
backPressedCallback?.isEnabled = false
return binding?.root
}
@@ -225,6 +246,7 @@ class TunnelListFragment : BaseFragment() {
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
actionMode = mode
backPressedCallback?.isEnabled = true
if (activity != null) {
resources = activity!!.resources
}
@@ -236,6 +258,7 @@ class TunnelListFragment : BaseFragment() {
override fun onDestroyActionMode(mode: ActionMode) {
actionMode = null
backPressedCallback?.isEnabled = false
resources = null
animateFab(binding?.createFab, true)
checkedItems.clear()
@@ -255,7 +278,7 @@ class TunnelListFragment : BaseFragment() {
}
val adapter = if (binding == null) null else binding!!.tunnelList.adapter
if (actionMode == null && !checkedItems.isEmpty() && activity != null) {
(activity as AppCompatActivity?)!!.startSupportActionMode(this)
(activity as AppCompatActivity).startSupportActionMode(this)
} else if (actionMode != null && checkedItems.isEmpty()) {
actionMode!!.finish()
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.model
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.model
@@ -1,5 +1,5 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.model
@@ -0,0 +1,55 @@
/*
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.preference
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.util.AttributeSet
import android.widget.Toast
import androidx.preference.Preference
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.wireguard.android.R
import com.wireguard.android.util.ErrorMessages
class DonatePreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs) {
override fun getSummary() = context.getString(R.string.donate_summary)
override fun getTitle() = context.getString(R.string.donate_title)
override fun onClick() {
val installer = try {
val packageName = context.packageName
val pm = context.packageManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
pm.getInstallSourceInfo(packageName).installingPackageName ?: ""
} else {
@Suppress("DEPRECATION")
pm.getInstallerPackageName(packageName) ?: ""
}
} catch (_: Throwable) {
""
}
/* Google Play Store forbids links to our donation page. */
if (installer == "com.android.vending") {
MaterialAlertDialogBuilder(context)
.setTitle(R.string.donate_title)
.setMessage(R.string.donate_google_play_disappointment)
.show()
return
}
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://www.wireguard.com/donations/")
try {
context.startActivity(intent)
} catch (e: Throwable) {
Toast.makeText(context, ErrorMessages[e], Toast.LENGTH_SHORT).show()
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.preference
@@ -26,7 +26,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.system.exitProcess
class KernelModuleDisablerPreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs) {
class KernelModuleEnablerPreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs) {
private var state = State.UNKNOWN
init {
@@ -44,10 +44,10 @@ class KernelModuleDisablerPreference(context: Context, attrs: AttributeSet?) : P
activity.lifecycleScope.launch {
if (state == State.DISABLED) {
setState(State.ENABLING)
UserKnobs.setDisableKernelModule(false)
UserKnobs.setEnableKernelModule(true)
} else if (state == State.ENABLED) {
setState(State.DISABLING)
UserKnobs.setDisableKernelModule(true)
UserKnobs.setEnableKernelModule(false)
}
val observableTunnels = Application.getTunnelManager().getTunnels()
val downings = observableTunnels.map { async(SupervisorJob()) { it.setStateAsync(Tunnel.State.DOWN) } }
@@ -76,13 +76,13 @@ class KernelModuleDisablerPreference(context: Context, attrs: AttributeSet?) : P
private enum class State(val titleResourceId: Int, val summaryResourceId: Int, val shouldEnableView: Boolean, val visible: Boolean) {
UNKNOWN(0, 0, false, false),
ENABLED(R.string.module_disabler_enabled_title, R.string.module_disabler_enabled_summary, true, true),
DISABLED(R.string.module_disabler_disabled_title, R.string.module_disabler_disabled_summary, true, true),
ENABLING(R.string.module_disabler_disabled_title, R.string.success_application_will_restart, false, true),
DISABLING(R.string.module_disabler_enabled_title, R.string.success_application_will_restart, false, true);
ENABLED(R.string.module_enabler_enabled_title, R.string.module_enabler_enabled_summary, true, true),
DISABLED(R.string.module_enabler_disabled_title, R.string.module_enabler_disabled_summary, true, true),
ENABLING(R.string.module_enabler_disabled_title, R.string.success_application_will_restart, false, true),
DISABLING(R.string.module_enabler_enabled_title, R.string.success_application_will_restart, false, true);
}
companion object {
private const val TAG = "WireGuard/KernelModuleDisablerPreference"
private const val TAG = "WireGuard/KernelModuleEnablerPreference"
}
}
@@ -1,70 +0,0 @@
/*
* Copyright © 2019 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.preference
import android.content.Context
import android.content.Intent
import android.system.OsConstants
import android.util.AttributeSet
import android.widget.Toast
import androidx.preference.Preference
import com.wireguard.android.Application
import com.wireguard.android.R
import com.wireguard.android.activity.SettingsActivity
import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.util.UserKnobs
import com.wireguard.android.util.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.system.exitProcess
class ModuleDownloaderPreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs) {
private var state = State.INITIAL
override fun getSummary() = context.getString(state.messageResourceId)
override fun getTitle() = context.getString(R.string.module_installer_title)
override fun onClick() {
setState(State.WORKING)
lifecycleScope.launch {
try {
when (withContext(Dispatchers.IO) { Application.getModuleLoader().download() }) {
OsConstants.ENOENT -> setState(State.NOTFOUND)
OsConstants.EXIT_SUCCESS -> {
setState(State.SUCCESS)
UserKnobs.setDisableKernelModule(null)
withContext(Dispatchers.IO) {
val restartIntent = Intent(context, SettingsActivity::class.java)
restartIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
restartIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
Application.get().startActivity(restartIntent)
exitProcess(0)
}
}
else -> setState(State.FAILURE)
}
} catch (e: Throwable) {
setState(State.FAILURE)
Toast.makeText(context, ErrorMessages[e], Toast.LENGTH_LONG).show()
}
}
}
private fun setState(state: State) {
if (this.state == state) return
this.state = state
if (isEnabled != state.shouldEnableView) isEnabled = state.shouldEnableView
notifyChanged()
}
private enum class State(val messageResourceId: Int, val shouldEnableView: Boolean) {
INITIAL(R.string.module_installer_initial, true),
FAILURE(R.string.module_installer_error, true),
WORKING(R.string.module_installer_working, false),
SUCCESS(R.string.success_application_will_restart, false),
NOTFOUND(R.string.module_installer_not_found, false);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.preference
@@ -1,14 +1,14 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.preference
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.util.AttributeSet
import android.widget.Toast
import androidx.preference.Preference
import com.wireguard.android.Application
import com.wireguard.android.BuildConfig
@@ -16,6 +16,7 @@ import com.wireguard.android.R
import com.wireguard.android.backend.Backend
import com.wireguard.android.backend.GoBackend
import com.wireguard.android.backend.WgQuickBackend
import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.util.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -33,7 +34,8 @@ class VersionPreference(context: Context, attrs: AttributeSet?) : Preference(con
intent.data = Uri.parse("https://www.wireguard.com/")
try {
context.startActivity(intent)
} catch (_: ActivityNotFoundException) {
} catch (e: Throwable) {
Toast.makeText(context, ErrorMessages[e], Toast.LENGTH_SHORT).show()
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.preference
@@ -98,6 +98,7 @@ class ZipExporterPreference(context: Context, attrs: AttributeSet?) : Preference
Snackbar.LENGTH_SHORT
).show()
}
is BiometricAuthenticator.Result.Cancelled -> {}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -1,5 +1,5 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.util
@@ -1,5 +1,5 @@
/*
* Copyright © 2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.util
@@ -1,11 +1,13 @@
/*
* Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.util
import android.content.res.Resources
import android.os.RemoteException
import com.google.zxing.ChecksumException
import com.google.zxing.NotFoundException
import com.wireguard.android.Application
import com.wireguard.android.R
import com.wireguard.android.backend.BackendException
@@ -84,8 +86,14 @@ object ErrorMessages {
rootCause is RootShellException -> {
resources.getString(RSE_REASON_MAP.getValue(rootCause.reason), *rootCause.format)
}
rootCause.message != null -> {
rootCause.message!!
rootCause is NotFoundException -> {
resources.getString(R.string.error_no_qr_found)
}
rootCause is ChecksumException -> {
resources.getString(R.string.error_qr_checksum)
}
rootCause.localizedMessage != null -> {
rootCause.localizedMessage!!
}
else -> {
val errorType = rootCause.javaClass.simpleName
@@ -101,7 +109,7 @@ object ErrorMessages {
if (kfe!!.type == KeyFormatException.Type.LENGTH) return resources.getString(KFE_FORMAT_MAP.getValue(kfe.format))
} else if (bce.cause is ParseException) {
val pe = bce.cause as ParseException?
if (pe!!.message != null) return ": ${pe.message}"
if (pe!!.localizedMessage != null) return ": ${pe.localizedMessage}"
} else if (bce.location == BadConfigException.Location.LISTEN_PORT) {
return resources.getString(R.string.bad_config_explanation_udp_port)
} else if (bce.location == BadConfigException.Location.MTU) {
@@ -1,5 +1,5 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
@@ -0,0 +1,110 @@
/*
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.util
import android.content.ContentResolver
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.util.Log
import com.google.zxing.BinaryBitmap
import com.google.zxing.DecodeHintType
import com.google.zxing.NotFoundException
import com.google.zxing.RGBLuminanceSource
import com.google.zxing.Reader
import com.google.zxing.Result
import com.google.zxing.common.HybridBinarizer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
/**
* Encapsulates the logic of scanning a barcode from a file,
* @property contentResolver - Resolver to read the incoming data
* @property reader - An instance of zxing's [Reader] class to parse the image
*/
class QrCodeFromFileScanner(
private val contentResolver: ContentResolver,
private val reader: Reader,
) {
private fun scanBitmapForResult(source: Bitmap): Result {
val width = source.width
val height = source.height
val pixels = IntArray(width * height)
source.getPixels(pixels, 0, width, 0, 0, width, height)
val bBitmap = BinaryBitmap(HybridBinarizer(RGBLuminanceSource(width, height, pixels)))
return reader.decode(bBitmap, mapOf(DecodeHintType.TRY_HARDER to true))
}
private fun downscaleBitmap(source: Bitmap, scaledSize: Int): Bitmap {
val originalWidth = source.width
val originalHeight = source.height
var newWidth = -1
var newHeight = -1
val multFactor: Float
when {
originalHeight > originalWidth -> {
newHeight = scaledSize
multFactor = originalWidth.toFloat() / originalHeight.toFloat()
newWidth = (newHeight * multFactor).toInt()
}
originalWidth > originalHeight -> {
newWidth = scaledSize
multFactor = originalHeight.toFloat() / originalWidth.toFloat()
newHeight = (newWidth * multFactor).toInt()
}
originalHeight == originalWidth -> {
newHeight = scaledSize
newWidth = scaledSize
}
}
return Bitmap.createScaledBitmap(source, newWidth, newHeight, false)
}
private fun doScan(data: Uri): Result {
Log.d(TAG, "Starting to scan an image: $data")
contentResolver.openInputStream(data).use { inputStream ->
val originalBitmap = BitmapFactory.decodeStream(inputStream)
?: throw IllegalArgumentException("Can't decode stream to Bitmap")
return try {
scanBitmapForResult(originalBitmap).also {
Log.d(TAG, "Found result in original image")
}
} catch (e: Exception) {
Log.e(TAG, "Original image scan finished with error: $e, will try downscaled image")
val scaleBitmap = downscaleBitmap(originalBitmap, 500)
scanBitmapForResult(originalBitmap).also { scaleBitmap.recycle() }
} finally {
originalBitmap.recycle()
}
}
}
/**
* Attempts to parse incoming data
* @return result of the decoding operation
* @throws NotFoundException when parser didn't find QR code in the image
*/
suspend fun scan(data: Uri) = withContext(Dispatchers.Default) { doScan(data) }
companion object {
private const val TAG = "QrCodeFromFileScanner"
/**
* Given a reference to a file, check if this file could be parsed by this class
* @return true if the file can be parsed, false if not
*/
fun validContentType(contentResolver: ContentResolver, data: Uri): Boolean {
return contentResolver.getType(data)?.startsWith("image/") == true
}
}
}

Some files were not shown because too many files have changed in this diff Show More