You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
279 lines
9.8 KiB
279 lines
9.8 KiB
package foundation.pEp.jniadapter;
|
|
|
|
import android.content.Context;
|
|
import android.content.res.AssetManager;
|
|
import android.util.Log;
|
|
import org.apache.commons.io.FileUtils;
|
|
import org.apache.commons.io.IOUtils;
|
|
|
|
import java.io.*;
|
|
import java.lang.reflect.Method;
|
|
import java.util.Scanner;
|
|
|
|
import foundation.pEp.jniadapter.exceptions.pEpException;
|
|
|
|
public class AndroidHelper {
|
|
static {
|
|
System.loadLibrary("pEpJNIAndroidHelper");
|
|
}
|
|
|
|
public static final String TAG = "AndroidHelper";
|
|
|
|
private static native int setenv(String key, String value, boolean overwrite);
|
|
|
|
private static File homeDir;
|
|
public static File gnupgHomeDir;
|
|
private static File optDir;
|
|
private static File versionFile;
|
|
public static File binDir;
|
|
public static File libDir;
|
|
private static File tmpDir;
|
|
|
|
// TODO : Increment when needed.
|
|
// TODO : Check if this version tracking is really needed and Automatize it
|
|
// TODO : This could be automatically generated as the version is tied to git tag && other files in the JNI part.
|
|
public static String ENGINE_VERSION_CODE = "v3.2.0-RC3";
|
|
|
|
private static File shareDir;
|
|
|
|
private static final String dBFileName = "system.db";
|
|
private static File dBfile;
|
|
|
|
private static boolean already = false;
|
|
|
|
public static void envSetup(Context c) {
|
|
// "/opt" like dir to unpack GnuPG assets
|
|
optDir = c.getDir("opt", Context.MODE_PRIVATE);
|
|
|
|
// Add GnuPG's bin to PATH
|
|
binDir = new File(optDir, "bin");
|
|
setenv("PATH", System.getenv("PATH") + ":" +
|
|
binDir.getAbsolutePath(), true);
|
|
|
|
tmpDir = new File(c.getCacheDir(), "tmp");
|
|
setenv("TEMP", tmpDir.getAbsolutePath(), true);
|
|
|
|
// Tell dynamic loader where to find libs
|
|
// Add GnuPG's bin to PATH
|
|
String appLibDir = "";
|
|
try {
|
|
appLibDir = new File(c.getApplicationInfo().nativeLibraryDir).getCanonicalPath();
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
appLibDir = new File(c.getApplicationInfo().nativeLibraryDir).getAbsolutePath();
|
|
}
|
|
|
|
libDir = new File(optDir, "lib");
|
|
setenv("LD_LIBRARY_PATH", appLibDir + ":" +
|
|
libDir.getAbsolutePath() + ":" +
|
|
System.getenv("LD_LIBRARY_PATH"), true);
|
|
|
|
// Set HOME environment variable pointing to
|
|
// something like "/data/data/app.package.name/home"
|
|
// pEpEngine use it to find management DB
|
|
homeDir = c.getDir("home", Context.MODE_PRIVATE);
|
|
gnupgHomeDir = new File(homeDir, ".gnupg");
|
|
setenv("HOME", homeDir.getAbsolutePath(), true);
|
|
|
|
// pEpEngine need to find the safe words database
|
|
shareDir = c.getDir("trustwords", Context.MODE_PRIVATE);
|
|
dBfile = new File(shareDir, dBFileName);
|
|
|
|
// TRUSTWORDS is absolute path of dir containig system.db
|
|
setenv("TRUSTWORDS", shareDir.getAbsolutePath(), true);
|
|
|
|
// Check version file retains latest installed version
|
|
versionFile = new File(c.getFilesDir(), "VERSION");
|
|
}
|
|
|
|
public static void assetsSetup(Context c) {
|
|
envSetup(c);
|
|
boolean needUpgrade = needNewAssets();
|
|
Log.i(TAG, "assetsSetup: " + needUpgrade);
|
|
|
|
// If system.db still not here, then go get it in the assets.
|
|
if (dBfile.exists() && needUpgrade){
|
|
dBfile.delete();
|
|
}
|
|
if (!dBfile.exists()){
|
|
assetFileExtract(c, dBFileName, shareDir);
|
|
}
|
|
|
|
// TODO: Remove this when releasing JNI 2.2.X
|
|
// Delete opt dir as no longer needed
|
|
if (optDir.exists() && needUpgrade){
|
|
try {
|
|
FileUtils.deleteDirectory(optDir);
|
|
} catch (IOException e) {
|
|
Log.e(TAG, "Couldn't delete opt directory");
|
|
}
|
|
}
|
|
|
|
// Fill version file
|
|
setInstalledVersion(c);
|
|
|
|
// Clean and creat tempdir
|
|
if (tmpDir.exists()){
|
|
try {
|
|
FileUtils.deleteDirectory(tmpDir);
|
|
} catch (IOException e) {
|
|
Log.e(TAG, "Couldn't delete temp dir");
|
|
}
|
|
}
|
|
tmpDir.mkdirs();
|
|
}
|
|
|
|
public static void nativeSetup(Context c) {
|
|
// pre-load libs for pepengine, as
|
|
// android cannot solve lib dependencies on its own
|
|
System.loadLibrary("gmp");
|
|
System.loadLibrary("nettle");
|
|
System.loadLibrary("hogweed");
|
|
|
|
migrateFromGPGToSequoiaIfNeeded(c.getFilesDir());
|
|
|
|
}
|
|
|
|
private static void migrateFromGPGToSequoiaIfNeeded(File filesDir) {
|
|
if (gnupgHomeDir.exists()) {
|
|
try {
|
|
RandomAccessFile pubring = new RandomAccessFile(gnupgHomeDir.getAbsolutePath() + "/pubring.gpg", "r");
|
|
RandomAccessFile secring = new RandomAccessFile((gnupgHomeDir.getAbsolutePath() + "/secring.gpg"), "r");
|
|
|
|
byte[] pubringBytes = new byte[(int) pubring.length()];
|
|
pubring.readFully(pubringBytes);
|
|
Log.d("boss", "init engine objc at "+Thread.currentThread().getId());
|
|
try (Engine pEpEngine = new Engine()) {
|
|
pEpEngine.importKey(pubringBytes);
|
|
|
|
byte[] secringBytes = new byte[(int) secring.length()];
|
|
secring.readFully(secringBytes);
|
|
pEpEngine.importKey(secringBytes);
|
|
}
|
|
|
|
|
|
//TODO: MARK KEYRING AS IMPORTED
|
|
boolean renamed = gnupgHomeDir.renameTo(new File(homeDir, ".legacyKeyring"));
|
|
Log.d("pEp", String.format(".gnupg moved to .legacyKeyring %b", renamed));
|
|
new File(filesDir, "gpgme.log").delete();
|
|
} catch (FileNotFoundException ignore) {
|
|
//No keyring - nothing to do.
|
|
} catch (IOException exception) {
|
|
Log.e(TAG, "migrateFromGPGToSequoiaIfNeeded: ", exception);
|
|
} catch (pEpException exception) {
|
|
Log.w(TAG, "migrateFromGPGToSequoiaIfNeeded: NOTHING IMPORTED", exception);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void setup(Context c) {
|
|
if(!already){
|
|
already = true;
|
|
assetsSetup(c);
|
|
nativeSetup(c);
|
|
}
|
|
}
|
|
|
|
private static void assetPathExtract(Context c, String assetPath, File targetDir) {
|
|
AssetManager assetManager = c.getAssets();
|
|
|
|
try {
|
|
String items[] = assetManager.list(assetPath);
|
|
if (items.length > 0) {
|
|
File newDir = new File(targetDir, new File(assetPath).getName());
|
|
if (!newDir.exists())
|
|
newDir.mkdirs();
|
|
for (int i = 0; i < items.length; ++i) {
|
|
assetPathExtract(c, new File(assetPath, items[i]).getPath(), newDir);
|
|
}
|
|
} else {
|
|
assetFileExtract(c, assetPath, targetDir);
|
|
}
|
|
} catch (IOException ex) {
|
|
Log.e(TAG, assetPath + " : ", ex);
|
|
}
|
|
}
|
|
|
|
private static void assetFileExtract(Context c, String assetPath, File targetDir) {
|
|
AssetManager assetManager = c.getAssets();
|
|
|
|
try {
|
|
|
|
InputStream inputStream = assetManager.open(assetPath);
|
|
String targetFileName =
|
|
new File(targetDir,
|
|
new File(assetPath).getName()).getAbsolutePath();
|
|
OutputStream outputStream = new FileOutputStream(targetFileName);
|
|
IOUtils.copy(inputStream, outputStream);
|
|
outputStream.close();
|
|
inputStream.close();
|
|
Log.i(TAG, "asset " + assetPath + " extracted as " + targetFileName);
|
|
} catch (Exception e) {
|
|
Log.e(TAG, assetPath + ": " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
private static String getInstalledVersion() {
|
|
String versionCode = "";
|
|
if (versionFile.exists()){
|
|
try {
|
|
Scanner scan = new Scanner(versionFile);
|
|
versionCode = scan.next();
|
|
scan.close();
|
|
} catch (Exception e) {
|
|
Log.e(TAG, "getInstalledVersion: " + e.getMessage());
|
|
}
|
|
}
|
|
return versionCode;
|
|
}
|
|
|
|
private static void setInstalledVersion(Context context) {
|
|
try {
|
|
FileOutputStream fileOutputStream = new FileOutputStream(versionFile);
|
|
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);
|
|
outputStreamWriter.write(ENGINE_VERSION_CODE + "\n");
|
|
outputStreamWriter.close();
|
|
fileOutputStream.close();
|
|
} catch (Exception e) {
|
|
Log.e(TAG, "setInstalledVersion: " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
public static boolean needNewAssets() {
|
|
return !getInstalledVersion().equals(ENGINE_VERSION_CODE);
|
|
}
|
|
|
|
// TODO: replace with native impl, less prone to failure.
|
|
public static void chmod(String modestr, File path) {
|
|
int err = 1;
|
|
try {
|
|
Class<?> fileUtils = Class.forName("android.os.FileUtils");
|
|
Method setPermissions = fileUtils.getMethod("setPermissions",
|
|
String.class, int.class, int.class, int.class);
|
|
err = (Integer) setPermissions.invoke(
|
|
null, path.getAbsolutePath(),
|
|
Integer.parseInt(modestr, 8),
|
|
-1, -1);
|
|
if (err != 0) {
|
|
Log.i(TAG, "android.os.FileUtils.setPermissions() returned " + err
|
|
+ " for '" + path + "'");
|
|
}
|
|
} catch (Exception e) {
|
|
Log.i(TAG, "chmod:", e);
|
|
}
|
|
}
|
|
|
|
public static void chmod(String mode, File path, boolean recursive) {
|
|
chmod(mode, path);
|
|
if (recursive) {
|
|
File[] paths = path.listFiles();
|
|
for (File pth : paths) {
|
|
if (pth.isDirectory()) {
|
|
chmod(mode, pth, true);
|
|
} else {
|
|
chmod(mode, pth);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|