diff --git a/app/src/main/java/com/wireguard/android/backend/GoBackend.java b/app/src/main/java/com/wireguard/android/backend/GoBackend.java index ffd05eae..4963f759 100644 --- a/app/src/main/java/com/wireguard/android/backend/GoBackend.java +++ b/app/src/main/java/com/wireguard/android/backend/GoBackend.java @@ -19,6 +19,7 @@ import com.wireguard.android.model.Tunnel; import com.wireguard.android.model.Tunnel.State; import com.wireguard.android.model.Tunnel.Statistics; import com.wireguard.android.util.ExceptionLoggers; +import com.wireguard.android.util.SharedLibraryLoader; import com.wireguard.config.Config; import com.wireguard.config.IPCidr; import com.wireguard.config.Interface; @@ -38,15 +39,12 @@ public final class GoBackend implements Backend { private static final String TAG = "WireGuard/" + GoBackend.class.getSimpleName(); private static CompletableFuture vpnService = new CompletableFuture<>(); - static { - System.loadLibrary("wg-go"); - } - private final Context context; private Tunnel currentTunnel; private int currentTunnelHandle = -1; public GoBackend(final Context context) { + SharedLibraryLoader.loadSharedLibrary(context, "wg-go"); this.context = context; } diff --git a/app/src/main/java/com/wireguard/android/util/SharedLibraryLoader.java b/app/src/main/java/com/wireguard/android/util/SharedLibraryLoader.java new file mode 100644 index 00000000..90ed8fcf --- /dev/null +++ b/app/src/main/java/com/wireguard/android/util/SharedLibraryLoader.java @@ -0,0 +1,74 @@ +/* + * Copyright © 2018 Jason A. Donenfeld . All Rights Reserved. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +package com.wireguard.android.util; + +import android.content.Context; +import android.os.Build; +import android.util.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.ZipFile; +import java.util.zip.ZipEntry; + +public final class SharedLibraryLoader { + private static final String TAG = "WireGuard/" + SharedLibraryLoader.class.getSimpleName(); + + private SharedLibraryLoader() { + } + + public static final void loadSharedLibrary(final Context context, final String libName) { + Throwable noAbiException; + try { + System.loadLibrary(libName); + return; + } catch (UnsatisfiedLinkError e) { + Log.d(TAG, "Failed to load library normally, so attempting to extract from apk", e); + noAbiException = e; + } + + final ZipFile zipFile; + try { + zipFile = new ZipFile(new File(context.getApplicationInfo().sourceDir), ZipFile.OPEN_READ); + } catch (IOException e) { + throw new RuntimeException(e); + } + + final String mappedLibName = System.mapLibraryName(libName); + byte buffer[] = new byte[1024 * 32]; + for (final String abi : Build.SUPPORTED_ABIS) { + final String libZipPath = "lib" + File.separatorChar + abi + File.separatorChar + mappedLibName; + final ZipEntry zipEntry = zipFile.getEntry(libZipPath); + if (zipEntry == null) + continue; + File f = null; + try { + f = File.createTempFile("lib", ".so", context.getCacheDir()); + Log.d(TAG, "Extracting apk:/" + libZipPath + " to " + f.getAbsolutePath() + " and loading"); + FileOutputStream out = new FileOutputStream(f); + InputStream in = zipFile.getInputStream(zipEntry); + int len; + while ((len = in.read(buffer)) != -1) { + out.write(buffer, 0, len); + } + out.close(); + System.load(f.getAbsolutePath()); + return; + } catch (Exception e) { + Log.d(TAG, "Failed to load library apk:/" + libZipPath, e); + noAbiException = e; + } finally { + if (f != null) + f.delete(); + } + } + if (noAbiException instanceof RuntimeException) + throw (RuntimeException) noAbiException; + throw new RuntimeException(noAbiException); + } +}