diff --git a/.gitmodules b/.gitmodules
index 1d764850a..e69de29bb 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +0,0 @@
-[submodule "tor"]
- path = tor
- url = https://github.com/thaliproject/Tor_Onion_Proxy_Library
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 29243356c..189167980 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -154,15 +154,17 @@ dependencies {
implementation "com.github.mmin18:realtimeblurview:1.2.1"
- // Tor
- // implementation "com.msopentech.thali:universal:0.0.3"
- // implementation "org.torproject:tor-android-binary:0.4.2.7a"
- // implementation "com.msopentech.thali.toronionproxy.android:android:0.0.3@aar"
- // implementation "org.slf4j:slf4j-android:1.7.30"
- // implementation "net.freehaven.tor.control:jtorctl:0.2" // controlling tor instance via its control port
-
implementation "com.jakewharton:process-phoenix:2.0.0"
implementation 'org.brotli:dec:0.1.2'
+
+ implementation "com.jakewharton:process-phoenix:2.0.0"
+
+ implementation 'info.guardianproject:tor-android:0.4.7.8'
+ implementation 'info.guardianproject:jtorctl:0.4.5.7'
+
+ implementation('dev.doubledot.doki:library:0.0.1@aar') {
+ transitive = true
+ }
}
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro
index 3ff1cebad..6457feed7 100644
--- a/android/app/proguard-rules.pro
+++ b/android/app/proguard-rules.pro
@@ -32,3 +32,5 @@
-keep class lnrpc.** { *; }
-keep class com.facebook.react.turbomodule.** { *; }
+
+-keep class org.torproject.jni.** { *; }
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 45ab123e9..eeca26e73 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -40,6 +40,7 @@
+
diff --git a/android/app/src/main/java/com/blixtwallet/BlixtTor.java b/android/app/src/main/java/com/blixtwallet/BlixtTor.java
new file mode 100644
index 000000000..d8a644501
--- /dev/null
+++ b/android/app/src/main/java/com/blixtwallet/BlixtTor.java
@@ -0,0 +1,165 @@
+package com.blixtwallet;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.Build;
+import android.os.IBinder;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.util.Stack;
+
+import androidx.core.app.NotificationManagerCompat;
+
+import com.facebook.react.bridge.Promise;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
+import com.facebook.react.bridge.ReactMethod;
+import com.reactnativecommunity.asyncstorage.AsyncLocalStorageUtil;
+import com.reactnativecommunity.asyncstorage.ReactDatabaseSupplier;
+
+import org.torproject.jni.TorService;
+
+
+public class BlixtTor extends ReactContextBaseJavaModule {
+ static private final String TAG = "BlixtTor";
+ static TorService torService;
+ static String currentTorStatus = TorService.STATUS_OFF;
+ static Stack calleeResolvers = new Stack<>();
+ static NotificationManagerCompat notificationManager;
+
+ static private boolean getPersistentServicesEnabled(Context context) {
+ ReactDatabaseSupplier dbSupplier = ReactDatabaseSupplier.getInstance(context);
+ SQLiteDatabase db = dbSupplier.get();
+ String persistentServicesEnabled = AsyncLocalStorageUtil.getItemImpl(db, "persistentServicesEnabled");
+ if (persistentServicesEnabled != null) {
+ return persistentServicesEnabled.equals("true");
+ }
+ Log.w(TAG, "Could not find persistentServicesEnabled in asyncStorage");
+ return false;
+ }
+
+ static private final ServiceConnection torServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ // We've bound to LocalService, cast the IBinder and get LocalService instance
+ TorService.LocalBinder binder = (TorService.LocalBinder) service;
+ torService = binder.getService();
+ Log.i(TAG, "torService.getService()");
+ boolean persistentServicesEnabled = getPersistentServicesEnabled(torService);
+ if (persistentServicesEnabled) {
+ torService.startForeground(0xc0feefee, getNotification(torService));
+ }
+ Log.i(TAG, "onServiceConnected");
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName arg0) {
+ Log.i(TAG, "onServiceDisconnected");
+ }
+
+ public Notification getNotification(Context context) {
+ Intent notificationIntent = new Intent (context, MainActivity.class);
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ NotificationChannel channel = new NotificationChannel(BuildConfig.APPLICATION_ID, "blixt", NotificationManager.IMPORTANCE_NONE);
+ channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
+ notificationManager = NotificationManagerCompat.from(context);
+ notificationManager.createNotificationChannel(channel);
+ notificationManager.createNotificationChannel(channel);
+ }
+ return new Notification.Builder(context, BuildConfig.APPLICATION_ID)
+ .setContentTitle("Tor")
+ .setContentText("Tor is running in the background")
+ .setSmallIcon(R.drawable.ic_stat_ic_notification)
+ .setContentIntent(pendingIntent)
+ .setTicker("Blixt Wallet")
+ .setOngoing(true)
+ .build();
+ }
+ };
+
+ private final BroadcastReceiver torBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+// Toast.makeText(context, intent.getStringExtra(TorService.EXTRA_STATUS), Toast.LENGTH_SHORT).show();
+ String status = intent.getStringExtra(TorService.EXTRA_STATUS);
+ if (intent != null && intent.getAction() != null && intent.getAction().equals("org.torproject.android.intent.action.STOP")) {
+ torService.stopSelf();
+ }
+ currentTorStatus = status;
+ Log.i(TAG, "onReceive " + status);
+ if (status.equals(TorService.STATUS_ON)) {
+ while (calleeResolvers.size() > 0) {
+ calleeResolvers.pop().resolve(TorService.socksPort);
+ }
+ } else if (status.equals(TorService.STATUS_OFF)) {
+ getReactApplicationContext().unregisterReceiver(torBroadcastReceiver);
+ }
+ }
+ };
+
+ public BlixtTor(ReactApplicationContext reactContext) {
+ super(reactContext);
+ }
+
+ public String getName() {
+ return "BlixtTor";
+ }
+
+ @ReactMethod
+ public void startTor(Promise promise) {
+ Log.i(TAG, "startTor()");
+// if (calleeResolver != null) {
+// Log.i(TAG, "calleeResolver != null");
+// promise.reject(TAG, "Tor already in progress starting");
+// return;
+// }
+ if (currentTorStatus.equals(TorService.STATUS_ON)) {
+ Log.i(TAG, "socksPort = " + TorService.socksPort + ", currentTorStatus.equals(TorService.STATUS_ON) " + currentTorStatus.equals(TorService.STATUS_ON));
+ promise.resolve(TorService.socksPort);
+ return;
+ }
+ calleeResolvers.add(promise);
+
+ boolean persistentServicesEnabled = getPersistentServicesEnabled(getReactApplicationContext());
+ getReactApplicationContext().registerReceiver(torBroadcastReceiver, new IntentFilter(TorService.ACTION_STATUS));
+ Intent intent = new Intent(getReactApplicationContext(), TorService.class);
+
+ if (persistentServicesEnabled) {
+ getReactApplicationContext().startForegroundService(intent);
+ }
+ getReactApplicationContext().bindService(intent, torServiceConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ @ReactMethod
+ public void stopTor(Promise promise) {
+ if (notificationManager != null) {
+ notificationManager.cancelAll();
+ }
+ Log.i(TAG,"Unbinding TorService");
+ try {
+ getReactApplicationContext().unbindService(torServiceConnection);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Tried to unbindService on unbound service");
+ }
+ promise.resolve(true);
+ };
+
+ @ReactMethod
+ public void showMsg(Promise promise) {
+ MainActivity.getActivity().showMsg();
+ promise.resolve(true);
+ }
+}
diff --git a/android/app/src/main/java/com/blixtwallet/BlixtTorPackage.java b/android/app/src/main/java/com/blixtwallet/BlixtTorPackage.java
new file mode 100644
index 000000000..3c766d7c2
--- /dev/null
+++ b/android/app/src/main/java/com/blixtwallet/BlixtTorPackage.java
@@ -0,0 +1,22 @@
+package com.blixtwallet;
+
+import com.facebook.react.ReactPackage;
+import com.facebook.react.bridge.NativeModule;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.uimanager.ViewManager;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class BlixtTorPackage implements ReactPackage {
+ @Override
+ public List createViewManagers(ReactApplicationContext reactContext) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List createNativeModules(ReactApplicationContext reactContext) {
+ return Arrays.asList(new BlixtTor(reactContext));
+ }
+}
diff --git a/android/app/src/main/java/com/blixtwallet/BlixtTorUtils.java b/android/app/src/main/java/com/blixtwallet/BlixtTorUtils.java
new file mode 100644
index 000000000..177658ddc
--- /dev/null
+++ b/android/app/src/main/java/com/blixtwallet/BlixtTorUtils.java
@@ -0,0 +1,16 @@
+package com.blixtwallet;
+
+import com.blixtwallet.BuildConfig;
+
+public class BlixtTorUtils {
+ public static int getListenPort() {
+ int listenPort = 9760;
+ if (BuildConfig.CHAIN.equals("testnet")) {
+ listenPort += 10;
+ }
+ if (BuildConfig.DEBUG) {
+ listenPort += 100;
+ }
+ return listenPort;
+ }
+}
diff --git a/android/app/src/main/java/com/blixtwallet/LndMobile.java b/android/app/src/main/java/com/blixtwallet/LndMobile.java
index 4b069d94a..148e58012 100644
--- a/android/app/src/main/java/com/blixtwallet/LndMobile.java
+++ b/android/app/src/main/java/com/blixtwallet/LndMobile.java
@@ -1,7 +1,5 @@
package com.blixtwallet;
-// import com.blixtwallet.tor.BlixtTorUtils;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
@@ -63,14 +61,13 @@
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableType;
import com.facebook.react.modules.core.DeviceEventManagerModule;
-import com.facebook.react.modules.permissions.PermissionsModule;
-
import com.reactnativecommunity.asyncstorage.AsyncLocalStorageUtil;
-import com.jakewharton.processphoenix.ProcessPhoenix;
-import com.oblador.keychain.KeychainModule;
+import com.reactnativecommunity.asyncstorage.ReactDatabaseSupplier;
import com.hypertrack.hyperlog.HyperLog;
+import org.torproject.jni.TorService;
+
// TODO break this class up
class LndMobile extends ReactContextBaseJavaModule {
private final String TAG = "LndMobile";
@@ -242,6 +239,17 @@ public void onServiceDisconnected(ComponentName className) {
private LndMobileServiceConnection lndMobileServiceConnection;
+ private boolean getPersistentServicesEnabled(Context context) {
+ ReactDatabaseSupplier dbSupplier = ReactDatabaseSupplier.getInstance(context);
+ SQLiteDatabase db = dbSupplier.get();
+ String persistentServicesEnabled = AsyncLocalStorageUtil.getItemImpl(db, "persistentServicesEnabled");
+ if (persistentServicesEnabled != null) {
+ return persistentServicesEnabled.equals("true");
+ }
+ HyperLog.w(TAG, "Could not find persistentServicesEnabled in asyncStorage");
+ return false;
+ }
+
public LndMobile(ReactApplicationContext reactContext) {
super(reactContext);
}
@@ -284,9 +292,13 @@ public void initialize(Promise promise) {
lndMobileServiceConnection = new LndMobileServiceConnection(req);
messenger = new Messenger(new IncomingHandler()); // me
-
+ Intent intent = new Intent(getReactApplicationContext(), LndMobileService.class);
+ if (getPersistentServicesEnabled(getReactApplicationContext())) {
+ getReactApplicationContext().startForegroundService(intent);
+ }
+ // else rely on bindService to start LND
getReactApplicationContext().bindService(
- new Intent(getReactApplicationContext(), LndMobileService.class),
+ intent,
lndMobileServiceConnection,
Context.BIND_AUTO_CREATE
);
@@ -353,13 +365,11 @@ public void startLnd(boolean torEnabled, String args, Promise promise) {
String params = "--lnddir=" + getReactApplicationContext().getFilesDir().getPath();
if (torEnabled) {
- // int listenPort = BlixtTorUtils.getListenPort();
- // int socksPort = BlixtTorUtils.getSocksPort();
- // int controlPort = BlixtTorUtils.getControlPort();
- // params += " --tor.active --tor.socks=127.0.0.1:" + socksPort + " --tor.control=127.0.0.1:" + controlPort;
- // params += " --tor.v3 --listen=localhost:" + listenPort;
- }
- else {
+ int listenPort = BlixtTorUtils.getListenPort();
+ String controlSocket = "unix://" + getReactApplicationContext().getDir(TorService.class.getSimpleName(), Context.MODE_PRIVATE).getAbsolutePath() + "/data/ControlSocket";
+ params += " --tor.active --tor.control=" + controlSocket;
+ params += " --tor.v3 --listen=localhost:" + listenPort;
+ } else {
// If Tor isn't active, make sure we aren't
// listening at all
params += " --nolisten";
diff --git a/android/app/src/main/java/com/blixtwallet/LndMobileScheduledSync.java b/android/app/src/main/java/com/blixtwallet/LndMobileScheduledSync.java
index f9989a0e9..75479ed75 100644
--- a/android/app/src/main/java/com/blixtwallet/LndMobileScheduledSync.java
+++ b/android/app/src/main/java/com/blixtwallet/LndMobileScheduledSync.java
@@ -1,34 +1,19 @@
package com.blixtwallet;
-import android.util.Log;
-import android.content.ComponentName;
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteStatement;
-
import androidx.work.Constraints;
import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.PeriodicWorkRequest;
import androidx.work.NetworkType;
import androidx.work.WorkInfo;
import androidx.work.WorkManager;
-import androidx.lifecycle.Observer;
-import androidx.annotation.Nullable;
-import com.facebook.react.bridge.Arguments;
-import com.facebook.react.bridge.WritableMap;
-import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
-import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;
-import com.reactnativecommunity.asyncstorage.ReactDatabaseSupplier;
-import com.reactnativecommunity.asyncstorage.AsyncLocalStorageUtil;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.List;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import com.hypertrack.hyperlog.HyperLog;
diff --git a/android/app/src/main/java/com/blixtwallet/LndMobileScheduledSyncWorker.java b/android/app/src/main/java/com/blixtwallet/LndMobileScheduledSyncWorker.java
index f220063da..d2047a9ba 100644
--- a/android/app/src/main/java/com/blixtwallet/LndMobileScheduledSyncWorker.java
+++ b/android/app/src/main/java/com/blixtwallet/LndMobileScheduledSyncWorker.java
@@ -1,8 +1,5 @@
package com.blixtwallet;
-// import com.blixtwallet.tor.BlixtTor;
-// import com.blixtwallet.tor.BlixtTorUtils;
-
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.content.ComponentName;
@@ -18,19 +15,16 @@
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteStatement;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.concurrent.futures.ResolvableFuture;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+import com.google.common.util.concurrent.ListenableFuture;
import androidx.work.ListenableWorker;
import androidx.work.WorkerParameters;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.protobuf.ByteString;
-
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteStatement;
-
import com.facebook.react.bridge.ReactApplicationContext;
import com.reactnativecommunity.asyncstorage.ReactDatabaseSupplier;
import com.reactnativecommunity.asyncstorage.AsyncLocalStorageUtil;
@@ -39,13 +33,14 @@
import com.facebook.react.bridge.ReadableMap;
import com.oblador.keychain.KeychainModule;
-
+import com.google.protobuf.ByteString;
import com.hypertrack.hyperlog.HyperLog;
+import org.torproject.jni.TorService;
+
public class LndMobileScheduledSyncWorker extends ListenableWorker {
private final String TAG = "LndScheduledSyncWorker";
private final String HANDLERTHREAD_NAME = "blixt_lndmobile_sync";
- private ResolvableFuture future = ResolvableFuture.create();
private Handler incomingHandler;
private boolean lndMobileServiceBound = false;
private Messenger messengerService; // The service
@@ -53,13 +48,15 @@ public class LndMobileScheduledSyncWorker extends ListenableWorker {
private ReactDatabaseSupplier dbSupplier;
private boolean lndStarted = false;
private boolean torEnabled = false;
+ private int torSocksPort = -1;
private boolean torStarted = false;
+ private boolean persistentServicesEnabled = false;
// Keeps track of how many times we've tried to get info
// If this keeps going without `syncedToChain` flipping to `true`
// we'll close down lnd and the worker
private int numGetInfoCalls = 0;
- // BlixtTor blixtTor;
+ BlixtTor blixtTor;
// private enum WorkState {
// NOT_STARTED, BOUND, WALLET_UNLOCKED, WAITING_FOR_SYNC, DONE;
@@ -75,70 +72,99 @@ public class LndMobileScheduledSyncWorker extends ListenableWorker {
public LndMobileScheduledSyncWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
dbSupplier = ReactDatabaseSupplier.getInstance(getApplicationContext());
- // blixtTor = new BlixtTor(new ReactApplicationContext(getApplicationContext()));
+ blixtTor = new BlixtTor(new ReactApplicationContext(getApplicationContext()));
}
@Override
public ListenableFuture startWork() {
- HyperLog.i(TAG, "------------------------------------");
- HyperLog.i(TAG, "Starting scheduled sync work");
- HyperLog.i(TAG, "I am " + getApplicationContext().getPackageName());
- writeLastScheduledSyncAttemptToDb();
-
- HyperLog.i(TAG, "MainActivity.started = " + MainActivity.started);
- if (MainActivity.started) {
- HyperLog.i(TAG, "MainActivity is started, quitting job");
- future.set(Result.success());
- return future;
- }
-
torEnabled = getTorEnabled();
+ persistentServicesEnabled = getPersistentServicesEnabled();
+
+ return CallbackToFutureAdapter.getFuture(completer -> {
+ HyperLog.i(TAG, "------------------------------------");
+ HyperLog.i(TAG, "Starting scheduled sync work");
+ HyperLog.i(TAG, "I am " + getApplicationContext().getPackageName());
+ writeLastScheduledSyncAttemptToDb();
+
+ HyperLog.i(TAG, "MainActivity.started = " + MainActivity.started);
+ if (persistentServicesEnabled || MainActivity.started) {
+ HyperLog.i(TAG, "MainActivity is started or persistentServicesEnabled = " + persistentServicesEnabled + ", quitting job");
+ completer.set(Result.success());
+ return null;
+ }
- KeychainModule keychain = new KeychainModule(new ReactApplicationContext(getApplicationContext()));
-
- WritableMap keychainOptions = Arguments.createMap();
- WritableMap keychainOptionsAuthenticationPrompt = Arguments.createMap();
- keychainOptionsAuthenticationPrompt.putString("title", "Authenticate to retrieve secret");
- keychainOptionsAuthenticationPrompt.putString("cancel", "Cancel");
- keychainOptions.putMap("authenticationPrompt", keychainOptionsAuthenticationPrompt);
+ KeychainModule keychain = new KeychainModule(new ReactApplicationContext(getApplicationContext()));
+
+ WritableMap keychainOptions = Arguments.createMap();
+ WritableMap keychainOptionsAuthenticationPrompt = Arguments.createMap();
+ keychainOptionsAuthenticationPrompt.putString("title", "Authenticate to retrieve secret");
+ keychainOptionsAuthenticationPrompt.putString("cancel", "Cancel");
+ keychainOptions.putMap("authenticationPrompt", keychainOptionsAuthenticationPrompt);
+
+ keychain.getInternetCredentialsForServer("password", keychainOptions, new PromiseWrapper() {
+ @Override
+ public void onSuccess(@Nullable Object value) {
+ HyperLog.d(TAG, "onSuccess");
+
+ if (value == null) {
+ HyperLog.e(TAG, "Failed to get wallet password, got null from keychain provider");
+ completer.set(Result.failure());
+ return;
+ } else {
+ HyperLog.d(TAG, "Password data retrieved from keychain");
+ final String password = ((ReadableMap) value).getString("password");
+ HyperLog.d(TAG, "Password retrieved");
+
+ if (torEnabled) {
+ // boolean startTorResult = startTor();
+ // if (!startTorResult) {
+ // Log.e(TAG, "Could not start Tor");
+ // future.set(Result.failure());
+ // return;
+ // }
+ blixtTor.startTor(new PromiseWrapper() {
+ @Override
+ void onSuccess(@Nullable Object value) {
+ HyperLog.i(TAG, "Tor started");
+ HyperLog.i(TAG, "torSocksPort: " + (int) value);
+ torStarted = true;
+ torSocksPort = (int) value;
+
+ startLndWorkThread(completer, password);
+ }
- keychain.getInternetCredentialsForServer("password", keychainOptions, new PromiseWrapper() {
- @Override
- public void onSuccess(@Nullable Object value) {
- HyperLog.d(TAG, "onSuccess");
+ @Override
+ void onFail(Throwable throwable) {
+ HyperLog.e(TAG, "Failed to start Tor", throwable);
+ blixtTor.stopTor(new PromiseWrapper() {
+ @Override
+ void onSuccess(@Nullable Object value) {
+ }
- if (value == null) {
- HyperLog.e(TAG, "Failed to get wallet password, got null from keychain provider");
- future.set(Result.failure());
- return;
- }
- else {
- HyperLog.d(TAG, "Password data retrieved from keychain");
- final String password = ((ReadableMap) value).getString("password");
- HyperLog.d(TAG, "Password retrieved");
-
- if (torEnabled) {
- boolean startTorResult = startTor();
- if (!startTorResult) {
- future.set(Result.failure());
- return;
+ @Override
+ void onFail(Throwable throwable) {
+ }
+ });
+ completer.set(Result.failure());
+ }
+ });
+ } else {
+ startLndWorkThread(completer, password);
}
}
- startLndWorkThread(future, password);
}
- }
- @Override
- public void onFail(Throwable throwable) {
- HyperLog.d(TAG, "Failed to get wallet password " + throwable.getMessage(), throwable);
- future.set(Result.failure());
- }
+ @Override
+ public void onFail(Throwable throwable) {
+ HyperLog.d(TAG, "Failed to get wallet password " + throwable.getMessage(), throwable);
+ completer.set(Result.failure());
+ }
+ });
+ return null;
});
-
- return future;
}
- private void startLndWorkThread(ResolvableFuture future, String password) {
+ private void startLndWorkThread(CallbackToFutureAdapter.Completer completer, String password) {
// Make sure we don't attempt to start lnd twice.
// A better fix in the future would be to actually
// use MSG_CHECKSTATUS in the future
@@ -164,7 +190,7 @@ public void handleMessage(Message msg) {
// Just exit if we reach this scenario
HyperLog.w(TAG, "WARNING, Got MSG_REGISTER_CLIENT_ACK when lnd should already be started, quitting work.");
unbindLndMobileService();
- future.set(Result.success());
+ completer.set(Result.success());
return;
}
} catch (Throwable t) {
@@ -198,7 +224,10 @@ public void handleMessage(Message msg) {
HyperLog.i(TAG, "Got WalletState.RPC_ACTIVE");
HyperLog.i(TAG, "LndMobileService reports RPC server ready. Sending GetInfo request");
getInfoRequest();
- } else {
+ } else if (currentState == lnrpc.Stateservice.WalletState.SERVER_ACTIVE) {
+ HyperLog.i(TAG, "Got WalletState.SERVER_ACTIVE");
+ HyperLog.i(TAG, "We do not care about that.");
+ } else {
HyperLog.w(TAG, "SubscribeState got unknown state " + currentState);
}
} catch (Throwable t) {
@@ -232,14 +261,14 @@ public void handleMessage(Message msg) {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
- stopWorker(true);
+ stopWorker(true, completer);
}
}, 10000);
}
else {
if (++numGetInfoCalls == 20) {
HyperLog.e(TAG, "GetInfo was called " + numGetInfoCalls + " times and still no syncedToChain = true. shutting down worker.");
- stopWorker(false);
+ stopWorker(false, completer);
} else{
HyperLog.i(TAG, "Sleeping 10s then checking again");
Handler handler = new Handler();
@@ -250,7 +279,7 @@ public void run() {
}
catch (Throwable t) {
HyperLog.e(TAG, "Job handler got an exception, shutting down worker.", t);
- stopWorker(false);
+ stopWorker(false, completer);
}
}
}, 10000);
@@ -271,7 +300,7 @@ public void run() {
}
} catch (Throwable t) {
HyperLog.e(TAG, "Job handler got an exception, shutting down worker.", t);
- stopWorker(false);
+ stopWorker(false, completer);
}
}
};
@@ -288,70 +317,76 @@ public void run() {
thread.run();
}
- private void stopWorker(boolean success) {
+ private void stopWorker(boolean success, CallbackToFutureAdapter.Completer completer) {
HyperLog.i(TAG, "Job is done. Quitting");
unbindLndMobileService();
if (torStarted) {
- if (!MainActivity.started) {
+// if (!MainActivity.started) {
HyperLog.i(TAG, "Stopping Tor");
- // blixtTor.stopTor(new PromiseWrapper() {
- // @Override
- // void onSuccess(@Nullable Object value) {
- // HyperLog.i(TAG, "Tor stopped");
- // }
-
- // @Override
- // void onFail(Throwable throwable) {
- // HyperLog.e(TAG, "Fail while stopping Tor", throwable);
- // }
- // });
- future.set(success ? Result.success() : Result.failure());
- killLndProcess();
- return;
- } else {
- HyperLog.w(TAG, "MainActivity was started when shutting down sync work. I will not stop Tor");
- }
+ blixtTor.stopTor(new PromiseWrapper() {
+ @Override
+ void onSuccess(@Nullable Object value) {
+ HyperLog.i(TAG,"Tor stopped");
+ }
+
+ @Override
+ void onFail(Throwable throwable) {
+ HyperLog.e(TAG, "Fail while stopping Tor", throwable);
+ }
+ });
+// } else {
+// HyperLog.w(TAG, "MainActivity was started when shutting down sync work. I will not stop Tor");
+// }
}
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
HyperLog.i(TAG, "Calling future.set(Result.success());");
- future.set(success ? Result.success() : Result.failure());
+ completer.set(success ? Result.success() : Result.failure());
}
}, 1500);
}
- private boolean startTor() {
+ private boolean startTor(CallbackToFutureAdapter.Completer completer) {
HyperLog.i(TAG, "Starting Tor");
- // blixtTor.startTor(new PromiseWrapper() {
- // @Override
- // void onSuccess(@Nullable Object value) {
- // HyperLog.i(TAG, "Tor started");
- // torStarted = true;
- // }
-
- // @Override
- // void onFail(Throwable throwable) {
- // HyperLog.e(TAG, "Failed to start Tor", throwable);
- // future.set(Result.failure());
- // }
- // });
- // int torTries = 0;
- // while (!torStarted) {
- // if (torTries++ > 30) {
- // HyperLog.e(TAG, "Couldn't start Tor.");
- // future.set(Result.failure());
- // return false;
- // }
- // HyperLog.i(TAG, "Waiting for Tor to start");
- // try {
- // Thread.sleep(1500);
- // } catch (InterruptedException e) {
- // e.printStackTrace();
- // }
- // }
+ blixtTor.startTor(new PromiseWrapper() {
+ @Override
+ void onSuccess(@Nullable Object value) {
+ HyperLog.i(TAG, "Tor started");
+ HyperLog.i(TAG, "torSocksPort: " + (int) value);
+ torStarted = true;
+ torSocksPort = (int) value;
+ }
+
+ @Override
+ void onFail(Throwable throwable) {
+ HyperLog.e(TAG, "Failed to start Tor", throwable);
+ blixtTor.stopTor(new PromiseWrapper() {
+ @Override
+ void onSuccess(@Nullable Object value) {}
+
+ @Override
+ void onFail(Throwable throwable) {}
+ });
+ completer.set(Result.failure());
+ }
+ });
+ int torTries = 0;
+ while (!torStarted) {
+ if (torTries++ > 40) {
+ HyperLog.e(TAG, "Couldn't start Tor.");
+ completer.set(Result.failure());
+ return false;
+ }
+ HyperLog.i(TAG, "Waiting for Tor to start");
+ try {
+ Thread.sleep(1500);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
return true;
}
@@ -361,12 +396,10 @@ private void startLnd() throws RemoteException {
Bundle bundle = new Bundle();
String params = "--lnddir=" + getApplicationContext().getFilesDir().getPath();
if (torEnabled) {
- // HyperLog.d(TAG, "Adding Tor params for starting lnd");
- // int socksPort = BlixtTorUtils.getSocksPort();
- // int controlPort = BlixtTorUtils.getControlPort();
- // params += " --tor.active --tor.socks=127.0.0.1:" + socksPort + " --tor.control=127.0.0.1:" + controlPort;
- // // params += " --tor.v3 --listen=localhost";
- // params += " --nolisten";
+ String controlSocket = "unix://" + getApplicationContext().getDir(TorService.class.getSimpleName(), Context.MODE_PRIVATE).getAbsolutePath() + "/data/ControlSocket";
+ HyperLog.d(TAG, "Adding Tor params for starting lnd, torSocksPort: " + torSocksPort + ", controlSocket: " + controlSocket);
+ params += " --tor.active --tor.socks=127.0.0.1:" + torSocksPort + " --tor.control=" + controlSocket;
+ params += " --nolisten";
}
else {
// If Tor isn't active, make sure we aren't
@@ -435,6 +468,16 @@ private void unbindLndMobileService() {
}
}
+ private boolean getPersistentServicesEnabled() {
+ SQLiteDatabase db = dbSupplier.get();
+ String persistentServicesEnabled = AsyncLocalStorageUtil.getItemImpl(db, "persistentServicesEnabled");
+ if (persistentServicesEnabled != null) {
+ return persistentServicesEnabled.equals("true");
+ }
+ HyperLog.w(TAG, "Could not find persistentServicesEnabled in asyncStorage");
+ return false;
+ }
+
private boolean getTorEnabled() {
SQLiteDatabase db = dbSupplier.get();
String torEnabled = AsyncLocalStorageUtil.getItemImpl(db, "torEnabled");
diff --git a/android/app/src/main/java/com/blixtwallet/LndMobileService.java b/android/app/src/main/java/com/blixtwallet/LndMobileService.java
index 3bdf9c9f3..75f1e9e0f 100644
--- a/android/app/src/main/java/com/blixtwallet/LndMobileService.java
+++ b/android/app/src/main/java/com/blixtwallet/LndMobileService.java
@@ -1,10 +1,16 @@
package com.blixtwallet;
import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
+import android.database.sqlite.SQLiteDatabase;
import android.os.IBinder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -14,7 +20,6 @@
import android.util.Base64;
import android.util.Log;
-
import lndmobile.Callback;
import lndmobile.Lndmobile;
import lndmobile.RecvStream;
@@ -27,6 +32,8 @@
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
+import com.reactnativecommunity.asyncstorage.AsyncLocalStorageUtil;
+import com.reactnativecommunity.asyncstorage.ReactDatabaseSupplier;
import com.google.protobuf.ByteString;
@@ -34,6 +41,7 @@
public class LndMobileService extends Service {
private static final String TAG = "LndMobileService";
+ private final int ONGOING_NOTIFICATION_ID = 1;
boolean lndStarted = false;
boolean subscribeInvoicesStreamActive = false;
Set streamsStarted = new HashSet();
@@ -70,6 +78,10 @@ public class LndMobileService extends Service {
private Map streamMethods = new HashMap<>();
private Map writeStreams = new HashMap<>();
+
+
+ private NotificationManager notificationManager;
+
private static boolean isReceiveStream(Method m) {
return m.toString().contains("RecvStream");
}
@@ -521,6 +533,55 @@ void sendToClients(Message msg) {
}
}
+ private boolean getPersistentServicesEnabled(Context context) {
+ ReactDatabaseSupplier dbSupplier = ReactDatabaseSupplier.getInstance(context);
+ SQLiteDatabase db = dbSupplier.get();
+ String persistentServicesEnabled = AsyncLocalStorageUtil.getItemImpl(db, "persistentServicesEnabled");
+ if (persistentServicesEnabled != null) {
+ return persistentServicesEnabled.equals("true");
+ }
+ HyperLog.w(TAG, "Could not find persistentServicesEnabled in asyncStorage");
+ return false;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startid) {
+ HyperLog.v(TAG, "onStartCommand()");
+ if (intent != null && intent.getAction() != null && intent.getAction().equals("com.blixtwallet.android.intent.action.STOP")) {
+ Log.i(TAG, "Received stopForeground Intent");
+ stopForeground(true);
+ stopSelf();
+ return START_NOT_STICKY;
+ } else {
+ boolean persistentServicesEnabled = getPersistentServicesEnabled(this);
+ // persistent services on, start service as foreground-svc
+ if (persistentServicesEnabled) {
+ Intent notificationIntent = new Intent (this, MainActivity.class);
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ NotificationChannel chan = new NotificationChannel(BuildConfig.APPLICATION_ID, "blixt", NotificationManager.IMPORTANCE_NONE);
+ chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
+ notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ assert notificationManager != null;
+ notificationManager.createNotificationChannel(chan);
+ }
+ Notification notification = new Notification.Builder(this, BuildConfig.APPLICATION_ID)
+ .setContentTitle("LND")
+ .setContentText("LND is running in the background")
+ .setSmallIcon(R.drawable.ic_stat_ic_notification)
+ .setContentIntent(pendingIntent)
+ .setTicker("Blixt Wallet")
+ .setOngoing(true)
+ .build();
+ startForeground(ONGOING_NOTIFICATION_ID, notification);
+ }
+ }
+
+ // else noop, instead of calling startService, start will be handled by binding
+ return startid;
+ }
+
@Override
public IBinder onBind(Intent intent) {
HyperLog.v(TAG, "onBind()");
@@ -596,6 +657,9 @@ private boolean killLndProcess() {
}
private void stopLnd(Messenger recipient, int request) {
+ if (notificationManager != null) {
+ notificationManager.cancelAll();
+ }
Lndmobile.stopDaemon(
lnrpc.LightningOuterClass.StopRequest.newBuilder().build().toByteArray(),
new Callback() {
diff --git a/android/app/src/main/java/com/blixtwallet/LndMobileTools.java b/android/app/src/main/java/com/blixtwallet/LndMobileTools.java
index 93e9c7c51..1d212333d 100644
--- a/android/app/src/main/java/com/blixtwallet/LndMobileTools.java
+++ b/android/app/src/main/java/com/blixtwallet/LndMobileTools.java
@@ -1,7 +1,5 @@
package com.blixtwallet;
-// import com.blixtwallet.tor.BlixtTorUtils;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
@@ -70,6 +68,8 @@
import com.hypertrack.hyperlog.HyperLog;
+import org.torproject.jni.TorService;
+
// TODO break this class up
class LndMobileTools extends ReactContextBaseJavaModule {
final String TAG = "LndMobileTools";
@@ -78,6 +78,17 @@ public LndMobileTools(ReactApplicationContext reactContext) {
super(reactContext);
}
+ private boolean getPersistentServicesEnabled(Context context) {
+ ReactDatabaseSupplier dbSupplier = ReactDatabaseSupplier.getInstance(context);
+ SQLiteDatabase db = dbSupplier.get();
+ String persistentServicesEnabled = AsyncLocalStorageUtil.getItemImpl(db, "persistentServicesEnabled");
+ if (persistentServicesEnabled != null) {
+ return persistentServicesEnabled.equals("true");
+ }
+ HyperLog.w(TAG, "Could not find persistentServicesEnabled in asyncStorage");
+ return false;
+ }
+
@Override
public String getName() {
return "LndMobileTools";
@@ -631,6 +642,12 @@ public void deleteTLSCerts(Promise promise) {
@ReactMethod
public void restartApp() {
+ Intent stopTorIntent = new Intent(getReactApplicationContext(), TorService.class);
+ stopTorIntent.setAction("org.torproject.android.intent.action.STOP");
+ getReactApplicationContext().stopService(stopTorIntent);
+ Intent stopLndIntent = new Intent(getReactApplicationContext(), LndMobileService.class);
+ stopLndIntent.setAction("com.blixtwallet.android.intent.action.STOP");
+ getReactApplicationContext().startService(stopLndIntent);
ProcessPhoenix.triggerRebirth(getReactApplicationContext());
}
diff --git a/android/app/src/main/java/com/blixtwallet/MainActivity.java b/android/app/src/main/java/com/blixtwallet/MainActivity.java
index f5d89e072..d94b6153d 100644
--- a/android/app/src/main/java/com/blixtwallet/MainActivity.java
+++ b/android/app/src/main/java/com/blixtwallet/MainActivity.java
@@ -6,6 +6,7 @@
import com.facebook.react.defaults.DefaultReactActivityDelegate;
import android.os.Bundle;
+import androidx.core.content.ContextCompat;
/*
* Blixt imports here:
@@ -20,6 +21,9 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.ref.WeakReference;
+
+import dev.doubledot.doki.ui.DokiActivity;
public class MainActivity extends ReactActivity {
@@ -59,11 +63,13 @@ protected ReactActivityDelegate createReactActivityDelegate() {
static byte[] tmpChanBackup;
+ public static WeakReference currentActivity;
// react-native-screens override
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
+ currentActivity = new WeakReference<>(MainActivity.this);
started = true;
}
@@ -132,4 +138,12 @@ else if (requestCode == INTENT_EXPORTCHANBACKUPFILE && resultCode == Activity.RE
}
}
}
+
+ public void showMsg() {
+ startActivity(new Intent(MainActivity.this, DokiActivity.class));
+ }
+
+ public static MainActivity getActivity() {
+ return currentActivity.get();
+ }
}
diff --git a/android/app/src/main/java/com/blixtwallet/MainApplication.java b/android/app/src/main/java/com/blixtwallet/MainApplication.java
index 47c25e6ba..d3fc769b2 100644
--- a/android/app/src/main/java/com/blixtwallet/MainApplication.java
+++ b/android/app/src/main/java/com/blixtwallet/MainApplication.java
@@ -39,7 +39,7 @@ protected List getPackages() {
packages.add(new LndMobileToolsPackage());
packages.add(new GossipFileScheduledSyncPackage());
packages.add(new LndMobileScheduledSyncPackage());
- // packages.add(new BlixtTorPackage());
+ packages.add(new BlixtTorPackage());
packages.add(new RealTimeBlurPackage());
return packages;
diff --git a/android/app/src/main/java/com/msopentech/thali/android/installer/AndroidTorInstaller.java b/android/app/src/main/java/com/msopentech/thali/android/installer/AndroidTorInstaller.java
new file mode 100644
index 000000000..8a13f7a99
--- /dev/null
+++ b/android/app/src/main/java/com/msopentech/thali/android/installer/AndroidTorInstaller.java
@@ -0,0 +1,89 @@
+package com.msopentech.thali.android.installer;/*
+Copyright (c) Microsoft Open Technologies, Inc.
+All Rights Reserved
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
+License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED,
+INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+See the Apache 2 License for the specific language governing permissions and limitations under the License.
+*/
+//package com.msopentech.thali.android.installer;
+
+
+import android.content.Context;
+import android.util.Log;
+//import com.msopentech.thali.toronionproxy.TorInstaller;
+//import org.torproject.android.binary.TorResourceInstaller;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Installs Tor for an Android app. This is a wrapper around the TorResourceInstaller
.
+ *
+ * Since this class only deals with installing Tor, it is up to the developer to implement
+ * the openBridgesStream
which will give the bridges for pluggable transports. A
+ * typical implementation looks like:
+ *
+ *
+ * public InputStream openBridgesStream() throws IOException {
+ * return context.getResources().openRawResource(R.raw.bridges);
+ * }
+ *
+ */
+public class AndroidTorInstaller {
+
+// private final TorResourceInstaller resourceInstaller;
+
+ private static final String TAG = "AndroidTorInstaller";
+
+ protected final Context context;
+
+ protected File torrcFile;
+
+ /**
+ * The configDir will be the location of tor configuration files. It contains the files, geoip, geoip6,
+ * bridges.txt and the default torrc file.
+ *
+ * The location of tor executable will be in the Android native library directory for the app.
+ */
+ public AndroidTorInstaller(Context context, File configDir) {
+// this.resourceInstaller = new TorResourceInstaller(context, configDir);
+ this.context = context;
+ }
+
+ public void updateTorConfigCustom(String content) throws IOException, TimeoutException {
+ if(torrcFile == null) {
+ throw new FileNotFoundException("Unable to find torrc file. Have you installed Tor resources?");
+ }
+// resourceInstaller.updateTorConfigCustom(torrcFile, content);
+ }
+
+// @Override
+ public void setup() throws IOException {
+// try {
+// File torFile = resourceInstaller.installResources();
+// if(torFile != null) {
+// Log.d("AndroidTorInstaller", "tor executable = " + torFile.getAbsolutePath());
+// } else {
+// Log.w(TAG, "Failed to setup tor. No tor executable installed");
+// throw new IOException("Failed to Failed to setup tor. No tor executable installed");
+// }
+
+// this.torrcFile = resourceInstaller.getTorrcFile();
+// if(torrcFile != null) {
+// Log.d("AndroidTorInstaller", "torrc = " + torrcFile.getAbsolutePath());
+// } else {
+// Log.w(TAG, "Failed to setup tor. No torrc file installed");
+// throw new IOException("Failed to Failed to setup tor. No torrc file installed");
+// }
+
+// } catch (TimeoutException e) {
+// Log.w(TAG, "Failed to setup tor: " + e.getMessage());
+// throw new IOException(e);
+// }
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable-hdpi/ic_stat_ic_notification.png b/android/app/src/main/res/drawable-hdpi/ic_stat_ic_notification.png
new file mode 100644
index 000000000..f73549062
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/ic_stat_ic_notification.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/ic_stat_ic_notification.png b/android/app/src/main/res/drawable-mdpi/ic_stat_ic_notification.png
new file mode 100644
index 000000000..7681d8777
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/ic_stat_ic_notification.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/ic_stat_ic_notification.png b/android/app/src/main/res/drawable-xhdpi/ic_stat_ic_notification.png
new file mode 100644
index 000000000..3f341f60d
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/ic_stat_ic_notification.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_stat_ic_notification.png b/android/app/src/main/res/drawable-xxhdpi/ic_stat_ic_notification.png
new file mode 100644
index 000000000..59ac95b9f
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/ic_stat_ic_notification.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/ic_stat_ic_notification.png b/android/app/src/main/res/drawable-xxxhdpi/ic_stat_ic_notification.png
new file mode 100644
index 000000000..e2093af54
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/ic_stat_ic_notification.png differ
diff --git a/locales/en.json b/locales/en.json
index 745fec29d..41e3e4f0f 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -985,6 +985,9 @@
},
"enforceSpeedloaderOnStartup": {
"title": "Enforce gossip sync on startup"
+ },
+ "persistentServices": {
+ "title": "Enable persistent LND and Tor (if enabled)"
}
}
},
diff --git a/src/migration/app-migration.ts b/src/migration/app-migration.ts
index 3ca4b335d..02efd77cf 100644
--- a/src/migration/app-migration.ts
+++ b/src/migration/app-migration.ts
@@ -290,6 +290,12 @@ export const appMigration: IAppMigration[] = [
{
async beforeLnd(db, i) {
await db.executeSql("ALTER TABLE contact ADD label TEXT NULL");
+ }
+ },
+ // Version 35
+ {
+ async beforeLnd(db, i) {
+ setItemObject(StorageItem.persistentServicesEnabled, false);
},
},
// Version 35
@@ -299,5 +305,11 @@ export const appMigration: IAppMigration[] = [
await setItemObject(StorageItem.lastScheduledGossipSync, 0);
await setItemObject(StorageItem.lastScheduledGossipSyncAttempt, 0);
}
- }
+ },
+ // Version 34
+ {
+ async beforeLnd(db, i) {
+ setItemObject(StorageItem.persistentServicesWarningShown, false);
+ },
+ },
];
diff --git a/src/state/NotificationManager.ts b/src/state/NotificationManager.ts
index 86ffe20f4..fd99a4023 100644
--- a/src/state/NotificationManager.ts
+++ b/src/state/NotificationManager.ts
@@ -41,7 +41,7 @@ export const notificationManager: INotificationManagerModel = {
}
} else if (PLATFORM === "android") {
const granted = await PermissionsAndroid.request(
- "android.permission.POST_NOTIFICATIONS" // FIXME PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS needs newer react-native version
+ PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
if (granted === "denied" || granted === "never_ask_again") {
log.w("Post notification permission was denied", [granted]);
diff --git a/src/state/Settings.ts b/src/state/Settings.ts
index 5b8035b82..d272fdb87 100644
--- a/src/state/Settings.ts
+++ b/src/state/Settings.ts
@@ -81,6 +81,8 @@ export interface ISettingsModel {
changeLndLogLevel: Thunk;
changeLndCompactDb: Thunk;
changeEnforceSpeedloaderOnStartup: Thunk;
+ changePersistentServicesEnabled: Thunk;
+ changePersistentServicesWarningShown: Thunk;
setBitcoinUnit: Action;
setFiatUnit: Action;
@@ -121,6 +123,8 @@ export interface ISettingsModel {
setLndLogLevel: Action;
setLndCompactDb: Action;
setEnforceSpeedloaderOnStartup: Action;
+ setPersistentServicesEnabled: Action;
+ setPersistentServicesWarningShown: Action;
bitcoinUnit: keyof IBitcoinUnits;
fiatUnit: keyof IFiatRates;
@@ -161,6 +165,8 @@ export interface ISettingsModel {
lndCompactDb: boolean;
zeroConfPeers: string[];
enforceSpeedloaderOnStartup: boolean;
+ persistentServicesEnabled: boolean;
+ persistentServicesWarningShown: boolean;
}
export const settings: ISettingsModel = {
@@ -235,6 +241,12 @@ export const settings: ISettingsModel = {
actions.setEnforceSpeedloaderOnStartup(
await getItemObject(StorageItem.enforceSpeedloaderOnStartup || false),
);
+ actions.setPersistentServicesEnabled(
+ (await getItemObject(StorageItem.persistentServicesEnabled)) ?? false,
+ );
+ actions.setPersistentServicesWarningShown(
+ (await getItemObject(StorageItem.persistentServicesWarningShown)) ?? false,
+ );
log.d("Done");
}),
@@ -439,6 +451,16 @@ export const settings: ISettingsModel = {
actions.setEnforceSpeedloaderOnStartup(payload);
}),
+ changePersistentServicesEnabled: thunk(async (actions, payload) => {
+ await setItemObject(StorageItem.persistentServicesEnabled, payload);
+ actions.setPersistentServicesEnabled(payload);
+ }),
+
+ changePersistentServicesWarningShown: thunk(async (actions, payload) => {
+ await setItemObject(StorageItem.persistentServicesWarningShown, payload);
+ actions.setPersistentServicesWarningShown(payload);
+ }),
+
setBitcoinUnit: action((state, payload) => {
state.bitcoinUnit = payload;
}),
@@ -556,6 +578,12 @@ export const settings: ISettingsModel = {
setEnforceSpeedloaderOnStartup: action((state, payload) => {
state.enforceSpeedloaderOnStartup = payload;
}),
+ setPersistentServicesEnabled: action((state, payload) => {
+ state.persistentServicesEnabled = payload;
+ }),
+ setPersistentServicesWarningShown: action((state, payload) => {
+ state.persistentServicesWarningShown = payload;
+ }),
bitcoinUnit: "bitcoin",
fiatUnit: "USD",
@@ -596,4 +624,6 @@ export const settings: ISettingsModel = {
lndLogLevel: DEFAULT_LND_LOG_LEVEL,
lndCompactDb: false,
enforceSpeedloaderOnStartup: false,
+ persistentServicesEnabled: false,
+ persistentServicesWarningShown: false,
};
diff --git a/src/state/index.ts b/src/state/index.ts
index 4dd3ffdf2..14fc9e49c 100644
--- a/src/state/index.ts
+++ b/src/state/index.ts
@@ -240,13 +240,13 @@ export const model: IStoreModel = {
try {
actions.setTorLoading(true);
if (PLATFORM === "android") {
- await NativeModules.BlixtTor.startTor();
+ socksPort = await NativeModules.BlixtTor.startTor();
} else if (PLATFORM === "ios") {
const tor = Tor({
stopDaemonOnBackground: false,
startDaemonOnActive: true,
});
- socksPort = await tor.startIfNotStarted();
+ await tor.startIfNotStarted();
}
log.i("socksPort", [socksPort]);
if (socksPort === 0 && PLATFORM === "ios") {
@@ -285,7 +285,12 @@ export const model: IStoreModel = {
}
}
}
-
+ let persistentServicesEnabled = await getItemObjectAsyncStorage(StorageItem.persistentServicesEnabled) ?? false;
+ let persistentServicesWarningShown = await getItemObjectAsyncStorage(StorageItem.persistentServicesWarningShown) ?? false;
+ if (persistentServicesEnabled && !persistentServicesWarningShown) {
+ await setItemObject(StorageItem.persistentServicesWarningShown, true);
+ await NativeModules.BlixtTor.showMsg();
+ }
log.v("Running LndMobile.initialize()");
const initReturn = await initialize();
log.i("initialize done", [initReturn]);
diff --git a/src/storage/app.ts b/src/storage/app.ts
index 4ee528aa4..b587ff773 100644
--- a/src/storage/app.ts
+++ b/src/storage/app.ts
@@ -81,6 +81,8 @@ export enum StorageItem { // const enums not supported in Babel 7...
lndCompactDb = "lndCompactDb",
zeroConfPeers = "zeroConfPeers",
enforceSpeedloaderOnStartup = "enforceSpeedloaderOnStartup",
+ persistentServicesEnabled = "persistentServicesEnabled",
+ persistentServicesWarningShown = "persistentServicesWarningShown",
}
export const setItem = async (key: StorageItem, value: string) =>
@@ -181,6 +183,8 @@ export const clearApp = async () => {
removeItem(StorageItem.lndLogLevel),
removeItem(StorageItem.lndCompactDb),
removeItem(StorageItem.enforceSpeedloaderOnStartup),
+ removeItem(StorageItem.persistentServicesEnabled),
+ removeItem(StorageItem.persistentServicesWarningShown),
]);
};
@@ -264,5 +268,7 @@ export const setupApp = async () => {
setItem(StorageItem.lndLogLevel, DEFAULT_LND_LOG_LEVEL),
setItemObject(StorageItem.lndCompactDb, false),
setItemObject(StorageItem.enforceSpeedloaderOnStartup, false),
+ setItemObject(StorageItem.persistentServicesEnabled, false),
+ setItemObject(StorageItem.persistentServicesWarningShown, false),
]);
};
diff --git a/src/windows/InitProcess/DEV_Commands.tsx b/src/windows/InitProcess/DEV_Commands.tsx
index 22b879f71..4dfdeabeb 100644
--- a/src/windows/InitProcess/DEV_Commands.tsx
+++ b/src/windows/InitProcess/DEV_Commands.tsx
@@ -107,6 +107,9 @@ export default function DEV_Commands({ navigation, continueCallback }: IProps) {
toast(e.message);
}
}}>contact label fix
+
@@ -514,6 +517,7 @@ export default function DEV_Commands({ navigation, continueCallback }: IProps) {
+
diff --git a/src/windows/Settings/Settings.tsx b/src/windows/Settings/Settings.tsx
index 3b12fc344..c12229095 100644
--- a/src/windows/Settings/Settings.tsx
+++ b/src/windows/Settings/Settings.tsx
@@ -697,6 +697,7 @@ ${t("LN.inbound.dialog.msg3")}`;
text: t("buttons.yes", { ns: namespaces.common }),
onPress: async () => {
try {
+ await NativeModules.BlixtTor.stopTor();
await NativeModules.LndMobile.stopLnd();
await NativeModules.LndMobileTools.killLnd();
} catch (e) {
@@ -1363,6 +1364,14 @@ ${t("experimental.tor.disabled.msg2")}`;
await changeEnforceSpeedloaderOnStartup(!enforceSpeedloaderOnStartup);
};
+ // Persistent services
+ const persistentServicesEnabled = useStoreState((store) => store.settings.persistentServicesEnabled);
+ const changePersistentServicesEnabled = useStoreActions((store) => store.settings.changePersistentServicesEnabled);
+ const changePersistentServicesEnabledPress = async () => {
+ await changePersistentServicesEnabled(!persistentServicesEnabled);
+ restartNeeded();
+ };
+
return (
@@ -2070,6 +2079,13 @@ ${t("experimental.tor.disabled.msg2")}`;
}
+
+
+
+ {t("debug.persistentServices.title")}
+
+
+
{t("debug.title")}
@@ -2339,7 +2355,13 @@ ${t("experimental.tor.disabled.msg2")}`;
/>
-
+
+
+
+ {t("debug.bimodalPathFinding.title")}
+
+
+