Skip to content
Snippets Groups Projects
Commit fda185fa authored by Pier Angelo Vendrame's avatar Pier Angelo Vendrame :jack_o_lantern: Committed by Beatriz Rizental
Browse files

TB 42247: Android helpers for the TorProvider

GeckoView is missing some API we use on desktop for the integration
with the tor daemon, such as subprocess.
Therefore, we need to implement them in Java and plumb the data
back and forth between JS and Java.
parent 172c7014
Branches
Tags
1 merge request!1507Rebase Tor Browser onto 136.0a1
Showing
with 1488 additions and 0 deletions
......@@ -136,3 +136,4 @@ pref("browser.torsettings.log_level", "Warn");
pref("browser.torMoat.loglevel", "Warn");
pref("browser.tordomainisolator.loglevel", "Warn");
pref("browser.torcircuitpanel.loglevel", "Log");
pref("browser.tor_android.log_level", "Info");
......@@ -248,6 +248,8 @@ public final class GeckoRuntime implements Parcelable {
private final ProfilerController mProfilerController;
private final GeckoScreenChangeListener mScreenChangeListener;
private TorAndroidIntegration mTorIntegration;
private GeckoRuntime() {
mWebExtensionController = new WebExtensionController(this);
mContentBlockingController = new ContentBlockingController();
......@@ -516,6 +518,8 @@ public final class GeckoRuntime implements Parcelable {
mScreenChangeListener.enable();
}
mTorIntegration = new TorAndroidIntegration(context);
mProfilerController.addMarker(
"GeckoView Initialization START", mProfilerController.getProfilerTime());
return true;
......@@ -626,6 +630,10 @@ public final class GeckoRuntime implements Parcelable {
mScreenChangeListener.disable();
}
if (mTorIntegration != null) {
mTorIntegration.shutdown();
}
GeckoThread.forceQuit();
}
......@@ -1026,6 +1034,14 @@ public final class GeckoRuntime implements Parcelable {
return mPushController;
}
/**
* Get the Tor integration controller for this runtime.
*/
@UiThread
public @NonNull TorAndroidIntegration getTorIntegrationController() {
return mTorIntegration;
}
/**
* Appends notes to crash report.
*
......
......@@ -2622,6 +2622,24 @@ public class GeckoSession {
return mEventDispatcher.queryBoolean("GeckoView:IsPdfJs");
}
/**
* Try to get last circuit used in this session, if possible.
*
* @return The circuit information as a {@link GeckoResult} object.
*/
@AnyThread
public @NonNull GeckoResult<GeckoBundle> getTorCircuit() {
return mEventDispatcher.queryBundle("GeckoView:GetTorCircuit");
}
/**
* Change the circuit for this session.
*/
@UiThread
public void newTorCircuit() {
mEventDispatcher.dispatch("GeckoView:NewTorCircuit", null);
}
/**
* Set this GeckoSession as active or inactive, which represents if the session is currently
* visible or not. Setting a GeckoSession to inactive will significantly reduce its memory
......
package org.mozilla.geckoview;
import org.mozilla.gecko.util.GeckoBundle;
// Class to receive BootstrappingStatus object from TorConnect.sys.mjs ~ln698
public class TorBootstrappingStatus {
public int progress; // percent of bootstrap progress
public boolean hasWarning; // Whether this bootstrap has a warning in the tor log
public TorBootstrappingStatus(GeckoBundle bundle) {
progress = bundle.getInt("progress");
hasWarning = bundle.getBoolean("hasWarning");
}
}
package org.mozilla.geckoview;
import org.mozilla.gecko.util.GeckoBundle;
// Class to receive ConnectStage object from TorConnect.sys.mjs ~ln677
public class TorConnectStage {
public class Error {
public String code;
public String message;
public String phase;
public String reason;
public Error(GeckoBundle bundle) {
code = bundle.getString("code");
message = bundle.getString("message");
phase = bundle.getString("phase");
reason = bundle.getString("reason");
}
}
public TorConnectStageName name;
// The TorConnectStage prior to this bootstrap attempt. Only set during the "Bootstrapping" stage.
public TorConnectStageName bootstrapTrigger;
public Error error;
public String defaultRegion;
public Boolean potentiallyBlocked;
public Boolean tryAgain;
public TorBootstrappingStatus bootstrappingStatus;
public TorConnectStage(GeckoBundle bundle) {
name = TorConnectStageName.fromString(bundle.getString("name"));
if (bundle.getString("bootstrapTrigger") != null) {
bootstrapTrigger = TorConnectStageName.fromString(bundle.getString("bootstrapTrigger"));
}
defaultRegion = bundle.getString("defaultRegion");
potentiallyBlocked = bundle.getBoolean("potentiallyBlocked");
tryAgain = bundle.getBoolean("tryAgain");
if (bundle.getBundle("error") != null) {
error = new Error(bundle.getBundle("error"));
}
bootstrappingStatus = new TorBootstrappingStatus(bundle.getBundle("bootstrappingStatus"));
}
public Boolean isBootstrapped() {
return name.isBootstrapped();
}
}
package org.mozilla.geckoview;
import java.security.InvalidParameterException;
public enum TorConnectStageName {
// These names should match entries from TorConnectStage in TorConnect.sys.mjs at ~ln163.
Disabled("Disabled"),
Loading("Loading"),
Start("Start"),
Bootstrapping("Bootstrapping"),
Offline("Offline"),
ChooseRegion("ChooseRegion"),
RegionNotFound("RegionNotFound"),
ConfirmRegion("ConfirmRegion"),
FinalError("FinalError"),
Bootstrapped("Bootstrapped");
private String valueText;
TorConnectStageName(String valueText) {
this.valueText = valueText;
}
public Boolean isBootstrapped() {
return this == Bootstrapped;
}
public String getString() {
return this.valueText;
}
public static TorConnectStageName fromString(String text) {
for (TorConnectStageName tcs : TorConnectStageName.values()) {
if (tcs.valueText.equalsIgnoreCase(text)) {
return tcs;
}
}
if (BuildConfig.BUILD_TYPE == "debug") {
throw new InvalidParameterException("Unknown TorConnectStageName " + text);
}
return null;
}
}
package org.mozilla.geckoview;
import android.util.Log;
import org.mozilla.gecko.util.GeckoBundle;
public class TorSettings {
public enum BridgeSource {
Invalid(-1),
BuiltIn(0),
BridgeDB(1),
UserProvided(2);
private int source;
BridgeSource(final int source) {
this.source = source;
}
public static BridgeSource fromInt(int i) {
switch (i) {
case -1:
return Invalid;
case 0:
return BuiltIn;
case 1:
return BridgeDB;
case 2:
return UserProvided;
}
return Invalid;
}
public int toInt() {
return this.source;
}
}
public enum ProxyType {
Invalid(-1),
Socks4(0),
Socks5(1),
HTTPS(2);
private int type;
ProxyType(final int type) {
this.type = type;
}
public int toInt() {
return type;
}
public static ProxyType fromInt(int i) {
switch (i) {
case -1:
return Invalid;
case 0:
return Socks4;
case 1:
return Socks5;
case 2:
return HTTPS;
}
return Invalid;
}
}
public enum BridgeBuiltinType {
/* TorSettings.sys.mjs ~ln43: string: obfs4|meek-azure|snowflake|etc */
Invalid("invalid"),
Obfs4("obfs4"),
MeekAzure("meek-azure"),
Snowflake("snowflake");
private String type;
BridgeBuiltinType(String type) {
this.type = type;
}
public String toString() {
return type;
}
public static BridgeBuiltinType fromString(String s) {
switch (s) {
case "obfs4":
return Obfs4;
case "meek-azure":
return MeekAzure;
case "snowflake":
return Snowflake;
}
return Invalid;
}
}
private boolean loaded = false;
public boolean enabled = true;
// bridges section
public boolean bridgesEnabled = false;
public BridgeSource bridgesSource = BridgeSource.Invalid;
public BridgeBuiltinType bridgesBuiltinType = BridgeBuiltinType.Invalid;
public String[] bridgeBridgeStrings;
// proxy section
public boolean proxyEnabled = false;
public ProxyType proxyType = ProxyType.Invalid;
public String proxyAddress = "";
public int proxyPort = 0;
public String proxyUsername = "";
public String proxyPassword = "";
// firewall section
public boolean firewallEnabled = false;
public int[] firewallAllowedPorts;
public TorSettings() {}
public TorSettings(GeckoBundle bundle) {
try {
GeckoBundle bridges = bundle.getBundle("bridges");
GeckoBundle proxy = bundle.getBundle("proxy");
GeckoBundle firewall = bundle.getBundle("firewall");
bridgesEnabled = bridges.getBoolean("enabled");
bridgesSource = BridgeSource.fromInt(bridges.getInt("source"));
bridgesBuiltinType = BridgeBuiltinType.fromString(bridges.getString("builtin_type"));
bridgeBridgeStrings = bridges.getStringArray("bridge_strings");
firewallEnabled = firewall.getBoolean("enabled");
firewallAllowedPorts = firewall.getIntArray("allowed_ports");
proxyEnabled = proxy.getBoolean("enabled");
proxyAddress = proxy.getString("address");
proxyUsername = proxy.getString("username");
proxyPassword = proxy.getString("password");
proxyPort = proxy.getInt("port");
proxyType = ProxyType.fromInt(proxy.getInt("type"));
loaded = true;
} catch (Exception e) {
Log.e("TorSettings", "bundle access error: " + e.toString(), e);
}
}
public GeckoBundle asGeckoBundle() {
GeckoBundle bundle = new GeckoBundle();
GeckoBundle bridges = new GeckoBundle();
GeckoBundle proxy = new GeckoBundle();
GeckoBundle firewall = new GeckoBundle();
bridges.putBoolean("enabled", bridgesEnabled);
bridges.putInt("source", bridgesSource.toInt());
bridges.putString("builtin_type", bridgesBuiltinType.toString());
bridges.putStringArray("bridge_strings", bridgeBridgeStrings);
firewall.putBoolean("enabled", firewallEnabled);
firewall.putIntArray("allowed_ports", firewallAllowedPorts);
proxy.putBoolean("enabled", proxyEnabled);
proxy.putString("address", proxyAddress);
proxy.putString("username", proxyUsername);
proxy.putString("password", proxyPassword);
proxy.putInt("port", proxyPort);
proxy.putInt("type", proxyType.toInt());
bundle.putBundle("bridges", bridges);
bundle.putBundle("proxy", proxy);
bundle.putBundle("firewall", firewall);
return bundle;
}
public boolean isLoaded() {
return this.loaded;
}
}
package org.mozilla.geckoview.androidlegacysettings;
import android.content.Context;
import android.content.SharedPreferences;
import java.util.Locale;
import org.mozilla.gecko.GeckoAppShell;
// tor-android-service utils/Prefs.java
/* package */ class Prefs {
private static final String PREF_BRIDGES_ENABLED = "pref_bridges_enabled";
private static final String PREF_BRIDGES_LIST = "pref_bridges_list";
private static SharedPreferences prefs;
// OrbotConstants
private static final String PREF_TOR_SHARED_PREFS = "org.torproject.android_preferences";
// tor-android-service utils/TorServiceUtil.java
private static void setContext() {
if (prefs == null) {
prefs =
GeckoAppShell.getApplicationContext()
.getSharedPreferences(PREF_TOR_SHARED_PREFS, Context.MODE_MULTI_PROCESS);
}
}
public static boolean getBoolean(String key, boolean def) {
setContext();
return prefs.getBoolean(key, def);
}
public static void putBoolean(String key, boolean value) {
setContext();
prefs.edit().putBoolean(key, value).apply();
}
public static void putString(String key, String value) {
setContext();
prefs.edit().putString(key, value).apply();
}
public static String getString(String key, String def) {
setContext();
return prefs.getString(key, def);
}
public static boolean bridgesEnabled() {
setContext();
// for Locale.getDefault().getLanguage().equals("fa"), bridges were enabled by default (and
// it was meek). This was a default set in 2019 code, but it is not a good default anymore,
// so we removed the check.
return prefs.getBoolean(PREF_BRIDGES_ENABLED, false);
}
public static String getBridgesList() {
setContext();
String list = prefs.getString(PREF_BRIDGES_LIST, "");
// list might be empty if the default PT was used, so check also if bridges are enabled.
if (list.isEmpty() && prefs.getBoolean(PREF_BRIDGES_ENABLED, false)) {
// Even though the check on the fa locale is not good to enable bridges by default, we
// still check it here, because if the list was empty, it was likely that it was the
// choice for users with this locale.
return (Locale.getDefault().getLanguage().equals("fa")) ? "meek" : "obfs4";
}
return list;
}
}
package org.mozilla.geckoview.androidlegacysettings;
import org.mozilla.geckoview.TorSettings;
public class TorLegacyAndroidSettings {
private static String PREF_USE_MOZ_PREFS = "tor_use_moz_prefs";
public static boolean unmigrated() {
return !Prefs.getBoolean(PREF_USE_MOZ_PREFS, false);
}
public static void setUnmigrated() {
Prefs.putBoolean(PREF_USE_MOZ_PREFS, false);
}
public static void setMigrated() {
Prefs.putBoolean(PREF_USE_MOZ_PREFS, true);
}
public static TorSettings loadTorSettings() {
TorSettings settings = new TorSettings();
// always true, tor is enabled in TB
settings.enabled = true;
settings.bridgesEnabled = Prefs.bridgesEnabled();
// tor-android-service CustomTorInstaller.java
/*
BridgesList is an overloaded field, which can cause some confusion.
The list can be:
1) a filter like obfs4, meek, or snowflake OR
2) it can be a custom bridge
For (1), we just pass back all bridges, the filter will occur
elsewhere in the library.
For (2) we return the bridge list as a raw stream.
If length is greater than 9, then we know this is a custom bridge
*/
String userDefinedBridgeList = Prefs.getBridgesList();
boolean userDefinedBridge = userDefinedBridgeList.length() > 9;
// Terrible hack. Must keep in sync with topl::addBridgesFromResources.
if (!userDefinedBridge) {
settings.bridgesSource = TorSettings.BridgeSource.BuiltIn;
switch (userDefinedBridgeList) {
case "obfs4":
case "snowflake":
settings.bridgesBuiltinType =
TorSettings.BridgeBuiltinType.fromString(userDefinedBridgeList);
break;
case "meek":
settings.bridgesBuiltinType = TorSettings.BridgeBuiltinType.MeekAzure;
break;
default:
settings.bridgesSource = TorSettings.BridgeSource.Invalid;
break;
}
} else {
settings.bridgesSource = TorSettings.BridgeSource.UserProvided; // user provided
settings.bridgeBridgeStrings = userDefinedBridgeList.split("\r\n");
}
// Tor Browser Android doesn't take proxy and firewall settings
settings.proxyEnabled = false;
settings.firewallEnabled = false;
settings.firewallAllowedPorts = new int[0];
return settings;
}
}
......@@ -12,6 +12,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
EventDispatcher: "resource://gre/modules/Messaging.sys.mjs",
PdfJs: "resource://pdf.js/PdfJs.sys.mjs",
RFPHelper: "resource://gre/modules/RFPHelper.sys.mjs",
TorAndroidIntegration: "resource://gre/modules/TorAndroidIntegration.sys.mjs",
TorDomainIsolator: "resource://gre/modules/TorDomainIsolator.sys.mjs",
});
......@@ -262,6 +263,7 @@ export class GeckoViewStartup {
this.#migratePreferences();
lazy.TorAndroidIntegration.init();
lazy.TorDomainIsolator.init();
Services.obs.addObserver(this, "browser-idle-startup-tasks-finished");
......
......@@ -8,6 +8,7 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
isProductURL: "chrome://global/content/shopping/ShoppingProduct.mjs",
ShoppingProduct: "chrome://global/content/shopping/ShoppingProduct.mjs",
TorDomainIsolator: "resource://gre/modules/TorDomainIsolator.sys.mjs",
});
export class GeckoViewContent extends GeckoViewModule {
......@@ -39,6 +40,8 @@ export class GeckoViewContent extends GeckoViewModule {
"GeckoView:ZoomToInput",
"GeckoView:IsPdfJs",
"GeckoView:GetWebCompatInfo",
"GeckoView:GetTorCircuit",
"GeckoView:NewTorCircuit",
]);
}
......@@ -189,6 +192,7 @@ export class GeckoViewContent extends GeckoViewModule {
}
// Bundle event handler.
// eslint-disable-next-line complexity
onEvent(aEvent, aData, aCallback) {
debug`onEvent: event=${aEvent}, data=${aData}`;
......@@ -332,6 +336,12 @@ export class GeckoViewContent extends GeckoViewModule {
case "GeckoView:HasCookieBannerRuleForBrowsingContextTree":
this._hasCookieBannerRuleForBrowsingContextTree(aCallback);
break;
case "GeckoView:GetTorCircuit":
this._getTorCircuit(aCallback);
break;
case "GeckoView:NewTorCircuit":
this._newTorCircuit(aCallback);
break;
}
}
......@@ -476,6 +486,25 @@ export class GeckoViewContent extends GeckoViewModule {
}
}
_getTorCircuit(aCallback) {
if (this.browser && aCallback) {
const domain = lazy.TorDomainIsolator.getDomainForBrowser(this.browser);
const nodes = lazy.TorDomainIsolator.getCircuit(
this.browser,
domain,
this.browser.contentPrincipal.originAttributes.userContextId
);
aCallback?.onSuccess({ domain, nodes });
} else {
aCallback?.onSuccess(null);
}
}
_newTorCircuit(aCallback) {
lazy.TorDomainIsolator.newCircuitForBrowser(this.browser);
aCallback?.onSuccess();
}
async _containsFormData(aCallback) {
aCallback.onSuccess(await this.actor.containsFormData());
}
......
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
EventDispatcher: "resource://gre/modules/Messaging.sys.mjs",
TorConnect: "resource://gre/modules/TorConnect.sys.mjs",
TorConnectTopics: "resource://gre/modules/TorConnect.sys.mjs",
TorSettingsTopics: "resource://gre/modules/TorSettings.sys.mjs",
TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
TorProviderTopics: "resource://gre/modules/TorProviderBuilder.sys.mjs",
TorSettings: "resource://gre/modules/TorSettings.sys.mjs",
});
const Prefs = Object.freeze({
logLevel: "browser.tor_android.log_level",
});
const logger = console.createInstance({
maxLogLevelPref: Prefs.logLevel,
prefix: "TorAndroidIntegration",
});
const EmittedEvents = Object.freeze({
settingsReady: "GeckoView:Tor:SettingsReady",
settingsChanged: "GeckoView:Tor:SettingsChanged",
connectStateChanged: "GeckoView:Tor:ConnectStateChanged", // deprecation path
connectStageChanged: "GeckoView:Tor:ConnectStageChanged", // new replacement path
connectError: "GeckoView:Tor:ConnectError",
bootstrapProgress: "GeckoView:Tor:BootstrapProgress",
bootstrapComplete: "GeckoView:Tor:BootstrapComplete",
torLogs: "GeckoView:Tor:Logs",
});
const ListenedEvents = Object.freeze({
settingsGet: "GeckoView:Tor:SettingsGet",
// The data is passed directly to TorSettings.
settingsSet: "GeckoView:Tor:SettingsSet",
bootstrapBegin: "GeckoView:Tor:BootstrapBegin",
// Optionally takes a countryCode, as data.countryCode.
bootstrapBeginAuto: "GeckoView:Tor:BootstrapBeginAuto",
bootstrapCancel: "GeckoView:Tor:BootstrapCancel",
bootstrapGetState: "GeckoView:Tor:BootstrapGetState",
startAgain: "GeckoView:Tor:StartAgain",
quickstartGet: "GeckoView:Tor:QuickstartGet",
quickstartSet: "GeckoView:Tor:QuickstartSet",
countryNamesGet: "GeckoView:Tor:CountryNamesGet",
});
class TorAndroidIntegrationImpl {
#initialized = false;
/**
* Register our listeners.
* We want this function to block GeckoView initialization, so it should not be
* async. Any async task should be moved to #deferredInit, instead.
*/
init() {
if (this.#initialized) {
logger.warn("Something tried to initilize us again.");
return;
}
this.#initialized = true;
lazy.EventDispatcher.instance.registerListener(
this,
Object.values(ListenedEvents)
);
Services.obs.addObserver(this, lazy.TorProviderTopics.TorLog);
for (const topic in lazy.TorConnectTopics) {
Services.obs.addObserver(this, lazy.TorConnectTopics[topic]);
}
for (const topic in lazy.TorSettingsTopics) {
Services.obs.addObserver(this, lazy.TorSettingsTopics[topic]);
}
lazy.TorProviderBuilder.init();
// On Android immediately call firstWindowLoaded. This should be safe to
// call since it will await the initialisation of the TorProvider set up
// by TorProviderBuilder.init.
lazy.TorProviderBuilder.firstWindowLoaded();
this.#deferredInit();
}
/**
* Perform our init tasks that should not block the initialization of
* GeckoView. This function will not be awaited, so errors can only be logged.
*/
async #deferredInit() {
try {
await lazy.TorSettings.init();
await lazy.TorConnect.init();
} catch (e) {
logger.error("Cannot initialize TorSettings or TorConnect", e);
}
}
observe(subj, topic) {
switch (topic) {
// TODO: Replace with StageChange.
case lazy.TorConnectTopics.StateChange:
lazy.EventDispatcher.instance.sendRequest({
type: EmittedEvents.connectStateChanged,
state: subj.wrappedJSObject.state ?? "",
});
break;
case lazy.TorConnectTopics.StageChange:
lazy.EventDispatcher.instance.sendRequest({
type: EmittedEvents.connectStageChanged,
stage: subj.wrappedJSObject ?? "",
});
break;
case lazy.TorConnectTopics.RegionNamesChange:
// TODO: Respond to change in region names if we are showing a
// TorConnectStage that uses them.
break;
case lazy.TorConnectTopics.BootstrapProgress:
lazy.EventDispatcher.instance.sendRequest({
type: EmittedEvents.bootstrapProgress,
progress: subj.wrappedJSObject.progress ?? 0,
hasWarnings: subj.wrappedJSObject.hasWarnings ?? false,
});
break;
case lazy.TorConnectTopics.BootstrapComplete:
lazy.EventDispatcher.instance.sendRequest({
type: EmittedEvents.bootstrapComplete,
});
break;
// TODO: Replace with StageChange stage.error.
case lazy.TorConnectTopics.Error:
lazy.EventDispatcher.instance.sendRequest({
type: EmittedEvents.connectError,
code: subj.wrappedJSObject.code ?? "",
message: subj.wrappedJSObject.message ?? "",
phase: subj.wrappedJSObject.cause?.phase ?? "",
reason: subj.wrappedJSObject.cause?.reason ?? "",
});
break;
case lazy.TorProviderTopics.TorLog:
lazy.EventDispatcher.instance.sendRequest({
type: EmittedEvents.torLogs,
logType: subj.wrappedJSObject.type ?? "",
message: subj.wrappedJSObject.msg ?? "",
timestamp: subj.wrappedJSObject.timestamp ?? "",
});
break;
case lazy.TorSettingsTopics.Ready:
lazy.EventDispatcher.instance.sendRequest({
type: EmittedEvents.settingsReady,
settings: lazy.TorSettings.getSettings(),
});
break;
case lazy.TorSettingsTopics.SettingsChanged:
// For Android we push also the settings object to avoid a round trip on
// the event dispatcher.
lazy.EventDispatcher.instance.sendRequest({
type: EmittedEvents.settingsChanged,
changes: subj.wrappedJSObject.changes ?? [],
settings: lazy.TorSettings.getSettings(),
});
break;
}
}
async onEvent(event, data, callback) {
logger.debug(`Received event ${event}`, data);
try {
switch (event) {
case ListenedEvents.settingsGet:
callback?.onSuccess(lazy.TorSettings.getSettings());
return;
case ListenedEvents.settingsSet:
// TODO: Handle setting throw? This can throw if data.settings is
// incorrectly formatted, but more like it can throw when the settings
// fail to be passed onto the TorProvider. tor-browser#43405.
await lazy.TorSettings.changeSettings(data.settings);
break;
case ListenedEvents.bootstrapBegin:
lazy.TorConnect.beginBootstrapping();
break;
case ListenedEvents.bootstrapBeginAuto:
// TODO: The countryCode should be set to "automatic" by the caller
// rather than `null`, so we can just pass in `data.countryCode`
// directly.
lazy.TorConnect.beginBootstrapping(data.countryCode || "automatic");
break;
case ListenedEvents.bootstrapCancel:
lazy.TorConnect.cancelBootstrapping();
break;
// TODO: Replace with TorConnect.stage.
case ListenedEvents.bootstrapGetState:
callback?.onSuccess(lazy.TorConnect.state);
return;
case ListenedEvents.startAgain:
lazy.TorConnect.startAgain();
break;
case ListenedEvents.quickstartGet:
callback?.onSuccess(lazy.TorConnect.quickstart);
return;
case ListenedEvents.quickstartSet:
lazy.TorConnect.quickstart = data.enabled;
break;
case ListenedEvents.countryNamesGet:
callback?.onSuccess(lazy.TorConnect.getRegionNames());
return;
}
callback?.onSuccess();
} catch (e) {
logger.error(`Error while handling event ${event}`, e);
callback?.onError(e);
}
}
}
export const TorAndroidIntegration = new TorAndroidIntegrationImpl();
......@@ -213,6 +213,7 @@ EXTRA_JS_MODULES += [
"Sqlite.sys.mjs",
"SubDialog.sys.mjs",
"Timer.sys.mjs",
"TorAndroidIntegration.sys.mjs",
"TorConnect.sys.mjs",
"TorSettings.sys.mjs",
"TorStrings.sys.mjs",
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment