Преглед на файлове

Fixed autoconnect to VPN after phone restart (always-on VPN setting must be enabled manually)

dev
Denys Podymskyy преди 4 години
родител
ревизия
3073095931
променени са 3 файла, в които са добавени 89 реда и са изтрити 47 реда
  1. +64
    -12
      ui/src/main/java/com/getbubblenow/android/model/TunnelManager.kt
  2. +25
    -31
      ui/src/main/java/com/getbubblenow/android/repository/DataRepository.java
  3. +0
    -4
      ui/src/main/java/com/getbubblenow/android/viewmodel/MainViewModel.java

+ 64
- 12
ui/src/main/java/com/getbubblenow/android/model/TunnelManager.kt Целия файл

@@ -40,12 +40,24 @@ class TunnelManager(private val configStore: ConfigStore) : BaseObservable() {
return tunnel
}

/**
* Get first found tunnel in map with given name
*/
fun getTunnelByName(tunnelName: String): ObservableTunnel? {
return tunnelMap.find { it.name == tunnelName }
}

fun create(name: String, config: Config?): CompletionStage<ObservableTunnel> {
if (Tunnel.isNameInvalid(name))
return CompletableFuture.failedFuture(IllegalArgumentException(context.getString(R.string.tunnel_error_invalid_name)))
if (tunnelMap.containsKey(name))
if (tunnelMap.containsKey(name)) {
return CompletableFuture.failedFuture(IllegalArgumentException(context.getString(R.string.tunnel_error_already_exists, name)))
return getAsyncWorker().supplyAsync { configStore.create(name, config!!) }.thenApply { addToList(name, it, Tunnel.State.DOWN) }
}
return getAsyncWorker().supplyAsync {
configStore.create(name, config!!)
}.thenApply {
addToList(name, it, Tunnel.State.DOWN)
}
}

fun delete(tunnel: ObservableTunnel): CompletionStage<Void> {
@@ -94,7 +106,9 @@ class TunnelManager(private val configStore: ConfigStore) : BaseObservable() {

fun onCreate() {
getAsyncWorker().supplyAsync { configStore.enumerate() }
.thenAcceptBoth(getAsyncWorker().supplyAsync { getBackend().runningTunnelNames }, this::onTunnelsLoaded)
.thenAcceptBoth(
getAsyncWorker().supplyAsync { getBackend().runningTunnelNames }
) { present, running -> this.onTunnelsLoaded(present, running) }
.whenComplete(ExceptionLoggers.E)
}

@@ -102,8 +116,10 @@ class TunnelManager(private val configStore: ConfigStore) : BaseObservable() {
for (name in present)
addToList(name, null, if (running.contains(name)) Tunnel.State.UP else Tunnel.State.DOWN)
val lastUsedName = getSharedPreferences().getString(KEY_LAST_USED_TUNNEL, null)
if (lastUsedName != null)

if (lastUsedName != null) {
lastUsedTunnel = tunnelMap[lastUsedName]
}
var toComplete: Array<CompletableFuture<Void>>
synchronized(delayedLoadRestoreTunnels) {
haveLoaded = true
@@ -123,7 +139,17 @@ class TunnelManager(private val configStore: ConfigStore) : BaseObservable() {

fun refreshTunnelStates() {
getAsyncWorker().supplyAsync { getBackend().runningTunnelNames }
.thenAccept { running: Set<String> -> for (tunnel in tunnelMap) tunnel.onStateChanged(if (running.contains(tunnel.name)) Tunnel.State.UP else Tunnel.State.DOWN) }
.thenAccept { running: Set<String> ->
for (tunnel in tunnelMap) {
tunnel.onStateChanged(
if (running.contains(tunnel.name)) {
Tunnel.State.UP
} else {
Tunnel.State.DOWN
}
)
}
}
.whenComplete(ExceptionLoggers.E)
}

@@ -139,12 +165,36 @@ class TunnelManager(private val configStore: ConfigStore) : BaseObservable() {
}
val previouslyRunning = getSharedPreferences().getStringSet(KEY_RUNNING_TUNNELS, null)
?: return CompletableFuture.completedFuture(null)
return CompletableFuture.allOf(*tunnelMap.filter { previouslyRunning.contains(it.name) }.map { setTunnelState(it, Tunnel.State.UP).toCompletableFuture() }.toTypedArray())

return CompletableFuture.allOf(
*tunnelMap
.filter { previouslyRunning.contains(it.name) }
.map { setTunnelState(it, Tunnel.State.UP).toCompletableFuture() }
.toTypedArray())
}

@SuppressLint("ApplySharedPref")
fun saveState() {
getSharedPreferences().edit().putStringSet(KEY_RUNNING_TUNNELS, tunnelMap.filter { it.state == Tunnel.State.UP }.map { it.name }.toSet()).commit()
getSharedPreferences().edit().putStringSet(
KEY_RUNNING_TUNNELS,
tunnelMap
.filter { it.state == Tunnel.State.UP }
.map { it.name }
.toSet()
).commit()
getSharedPreferences().edit().putBoolean(KEY_RESTORE_ON_BOOT, upTunnelsExist()).commit()
}

/**
* Check tunnel's map for tunnels with UP state.
*/
private fun upTunnelsExist() : Boolean {
tunnelMap.forEach {
if (it.state == Tunnel.State.UP) {
return true
}
}
return false
}

fun setTunnelConfig(tunnel: ObservableTunnel, config: Config): CompletionStage<Config> = getAsyncWorker().supplyAsync {
@@ -183,13 +233,15 @@ class TunnelManager(private val configStore: ConfigStore) : BaseObservable() {
}
}

fun setTunnelState(tunnel: ObservableTunnel, state: Tunnel.State): CompletionStage<Tunnel.State> = tunnel.configAsync
.thenCompose { getAsyncWorker().supplyAsync { getBackend().setState(tunnel, state, it) } }
.whenComplete { newState, e ->
fun setTunnelState(tunnel: ObservableTunnel, state: Tunnel.State): CompletionStage<Tunnel.State> =
tunnel.configAsync.thenCompose {
getAsyncWorker().supplyAsync { getBackend().setState(tunnel, state, it) }
}.whenComplete { newState, e ->
// Ensure onStateChanged is always called (failure or not), and with the correct state.
tunnel.onStateChanged(if (e == null) newState else tunnel.state)
if (e == null && newState == Tunnel.State.UP)
if (e == null && newState == Tunnel.State.UP) {
lastUsedTunnel = tunnel
}
saveState()
}

@@ -218,7 +270,7 @@ class TunnelManager(private val configStore: ConfigStore) : BaseObservable() {
}
}

fun getTunnelState(tunnel: ObservableTunnel): CompletionStage<Tunnel.State> = getAsyncWorker()
private fun getTunnelState(tunnel: ObservableTunnel): CompletionStage<Tunnel.State> = getAsyncWorker()
.supplyAsync { getBackend().getState(tunnel) }.thenApply(tunnel::onStateChanged)

fun getTunnelStatistics(tunnel: ObservableTunnel): CompletionStage<Statistics> = getAsyncWorker()


+ 25
- 31
ui/src/main/java/com/getbubblenow/android/repository/DataRepository.java Целия файл

@@ -114,7 +114,6 @@ public final class DataRepository {
baseUrl = url;
clientApi = ClientService.getInstance().createClientApi(url);
compositeDisposable = new CompositeDisposable();
Application.setTunnelManager(new TunnelManager(new FileConfigStore(context)));
}

public static void buildRepositoryInstance(final Context context, final String url) {
@@ -793,7 +792,7 @@ public final class DataRepository {
}.getMutableLiveData();
}

public MutableLiveData<StatusResource<Object>> createTunnel(Context context, String dataConfig) {
public MutableLiveData<StatusResource<Object>> createTunnel(final Context context, final String dataConfig) {
return new NetworkBoundStatusResource<Object>() {

@Override protected void createCall() {
@@ -865,52 +864,47 @@ public final class DataRepository {
}


private Config parseConfig(String data) throws IOException, BadConfigException {
private Config parseConfig(final String data) throws IOException, BadConfigException {
final byte[] configText = data.getBytes();
final Config config = Config.parse(new ByteArrayInputStream(configText));
return config;
return Config.parse(new ByteArrayInputStream(configText));
}


private ObservableTunnel createTunnel(final Context context, final boolean stateTunnel) {
//TODO implement config is null case
Config config = null;
final Config config;
try {
config = parseConfig(TunnelStore.getInstance(context).getConfig());
} catch (final IOException | BadConfigException e) {
return null;
}
final String name = TunnelStore.getInstance(context).getTunnelName();
final ObservableTunnel tunnel;
if (stateTunnel) {
tunnel = new ObservableTunnel(Application.getTunnelManager(), name, config, State.UP);
} else {
tunnel = new ObservableTunnel(Application.getTunnelManager(), name, config, State.DOWN);
}
// Application.getTunnelManager().setTunnelState(tunnel, tunnel.getState());
pendingTunnel = tunnel;
return tunnel;
return (stateTunnel) ? new ObservableTunnel(Application.getTunnelManager(), name, config, State.UP)
: new ObservableTunnel(Application.getTunnelManager(), name, config, State.DOWN);
}

public ObservableTunnel getTunnel(final Context context, final boolean connectionStateFlag) {
ObservableTunnel tunnel = Application.getTunnelManager().getLastUsedTunnel();
if (tunnel == null) {
tunnel = createTunnel(context, connectionStateFlag);
private void getTunnel(final Context context, final boolean connectionStateFlag) {

final String tunnelName = TunnelStore.getInstance(context).getTunnelName();

pendingTunnel = Application.getTunnelManager().getLastUsedTunnel();

if (pendingTunnel == null) {
pendingTunnel = Application.getTunnelManager().getTunnelByName(tunnelName);
}
pendingTunnel = tunnel;
return tunnel;
}

public boolean isVPNConnected(final Context context, final boolean connectionStateFlag) {
pendingTunnel = getTunnel(context, connectionStateFlag);
if (pendingTunnel == null) {
return false;
pendingTunnel = createTunnel(context, connectionStateFlag);
}
}

public boolean isVPNConnected(final Context context, final boolean connectionStateFlag) {
getTunnel(context, connectionStateFlag);
return pendingTunnel.getState() == State.DOWN;
}

public MutableLiveData<Boolean> connect(final Boolean checked, Context context) {
MutableLiveData<Boolean> liveData = new MutableLiveData<>();
public MutableLiveData<Boolean> connect(final Boolean checked, final Context context) {
final MutableLiveData<Boolean> liveData = new MutableLiveData<>();
if (pendingTunnel != null) {
Application.getBackendAsync().thenAccept(backend -> {
if (backend instanceof GoBackend) {
@@ -930,11 +924,11 @@ public final class DataRepository {
return liveData;
}

public MutableLiveData<Boolean> connectWithPermission(final boolean checked, Context context) {
MutableLiveData<Boolean> liveData = new MutableLiveData<>();
pendingTunnel.setStateAsync(Tunnel.State.of(checked)).whenComplete((observableTunnel, throwable) -> {
public MutableLiveData<Boolean> connectWithPermission(final boolean checked, final Context context) {
final MutableLiveData<Boolean> liveData = new MutableLiveData<>();
pendingTunnel.setStateAsync(Tunnel.State.of(checked)).whenComplete((observableTunnelState, throwable) -> {
if (throwable == null) {
if (observableTunnel == State.DOWN) {
if (observableTunnelState == State.DOWN) {
liveData.postValue(false);
} else {
liveData.postValue(true);


+ 0
- 4
ui/src/main/java/com/getbubblenow/android/viewmodel/MainViewModel.java Целия файл

@@ -38,10 +38,6 @@ public class MainViewModel extends BaseViewModel {
return !UserStore.USER_TOKEN_DEFAULT_VALUE.equals(UserStore.getInstance(context).getToken());
}

public ObservableTunnel getTunnel(Context context, boolean stateTunnel) {
return DataRepository.getRepositoryInstance().getTunnel(context,stateTunnel);
}

public MutableLiveData<Boolean> connectWithPermission(final boolean checked , Context context) {
return DataRepository.getRepositoryInstance().connectWithPermission(checked,context);
}


Зареждане…
Отказ
Запис