mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Make hotspot SSID and passphrase persistent
This commit is contained in:
committed by
Sebastian Kürten
parent
7474ad8606
commit
e22e9dcade
@@ -22,4 +22,11 @@ public interface SettingsManager {
|
||||
* namespace.
|
||||
*/
|
||||
void mergeSettings(Settings s, String namespace) throws DbException;
|
||||
|
||||
/**
|
||||
* Merges the given settings with any existing settings in the given
|
||||
* namespace.
|
||||
*/
|
||||
void mergeSettings(Transaction txn, Settings s, String namespace)
|
||||
throws DbException;
|
||||
}
|
||||
|
||||
@@ -37,4 +37,10 @@ class SettingsManagerImpl implements SettingsManager {
|
||||
public void mergeSettings(Settings s, String namespace) throws DbException {
|
||||
db.transaction(false, txn -> db.mergeSettings(txn, s, namespace));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mergeSettings(Transaction txn, Settings s, String namespace)
|
||||
throws DbException {
|
||||
db.mergeSettings(txn, s, namespace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
||||
import static android.content.Context.WIFI_P2P_SERVICE;
|
||||
import static android.content.Context.WIFI_SERVICE;
|
||||
@@ -34,8 +34,10 @@ import static android.net.wifi.p2p.WifiP2pManager.ERROR;
|
||||
import static android.net.wifi.p2p.WifiP2pManager.NO_SERVICE_REQUESTS;
|
||||
import static android.net.wifi.p2p.WifiP2pManager.P2P_UNSUPPORTED;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.briar.android.util.UiUtils.observeForeverOnce;
|
||||
|
||||
class HotspotManager implements ActionListener {
|
||||
|
||||
@@ -61,25 +63,22 @@ class HotspotManager implements ActionListener {
|
||||
private final Context ctx;
|
||||
@IoExecutor
|
||||
private final Executor ioExecutor;
|
||||
private final SecureRandom random;
|
||||
private final LiveData<NetworkConfig> savedNetworkConfig;
|
||||
private final HotspotListener listener;
|
||||
private final WifiManager wifiManager;
|
||||
private final WifiP2pManager wifiP2pManager;
|
||||
private final Handler handler;
|
||||
private final String lockTag;
|
||||
|
||||
@Nullable
|
||||
// on API < 29 this is null because we cannot request a custom network name
|
||||
private String networkName = null;
|
||||
|
||||
private WifiManager.WifiLock wifiLock;
|
||||
private WifiP2pManager.Channel channel;
|
||||
|
||||
HotspotManager(Context ctx, @IoExecutor Executor ioExecutor,
|
||||
SecureRandom random, HotspotListener listener) {
|
||||
LiveData<NetworkConfig> savedNetworkConfig,
|
||||
HotspotListener listener) {
|
||||
this.ctx = ctx;
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.random = random;
|
||||
this.savedNetworkConfig = savedNetworkConfig;
|
||||
this.listener = listener;
|
||||
wifiManager = (WifiManager) ctx.getApplicationContext()
|
||||
.getSystemService(WIFI_SERVICE);
|
||||
@@ -106,14 +105,14 @@ class HotspotManager implements ActionListener {
|
||||
acquireLock();
|
||||
try {
|
||||
if (SDK_INT >= 29) {
|
||||
networkName = getNetworkName();
|
||||
String passphrase = getPassphrase();
|
||||
WifiP2pConfig config = new WifiP2pConfig.Builder()
|
||||
.setGroupOperatingBand(GROUP_OWNER_BAND_2GHZ)
|
||||
.setNetworkName(networkName)
|
||||
.setPassphrase(passphrase)
|
||||
.build();
|
||||
wifiP2pManager.createGroup(channel, config, this);
|
||||
observeForeverOnce(savedNetworkConfig, c -> {
|
||||
WifiP2pConfig config = new WifiP2pConfig.Builder()
|
||||
.setGroupOperatingBand(GROUP_OWNER_BAND_2GHZ)
|
||||
.setNetworkName(c.ssid)
|
||||
.setPassphrase(c.password)
|
||||
.build();
|
||||
wifiP2pManager.createGroup(channel, config, this);
|
||||
});
|
||||
} else {
|
||||
wifiP2pManager.createGroup(channel, this);
|
||||
}
|
||||
@@ -154,16 +153,6 @@ class HotspotManager implements ActionListener {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(29)
|
||||
private String getNetworkName() {
|
||||
return "DIRECT-" + getRandomString(2) + "-" +
|
||||
getRandomString(10);
|
||||
}
|
||||
|
||||
private String getPassphrase() {
|
||||
return getRandomString(8);
|
||||
}
|
||||
|
||||
void stopWifiP2pHotspot() {
|
||||
if (channel == null) return;
|
||||
wifiP2pManager.removeGroup(channel, new ActionListener() {
|
||||
@@ -251,13 +240,17 @@ class HotspotManager implements ActionListener {
|
||||
group.getNetworkName());
|
||||
}
|
||||
return false;
|
||||
} else if (networkName != null &&
|
||||
!networkName.equals(group.getNetworkName())) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("expected networkName: " + networkName);
|
||||
LOG.info("received networkName: " + group.getNetworkName());
|
||||
} else if (SDK_INT >= 29) {
|
||||
// if we get here, the savedNetworkConfig must have a value
|
||||
String networkName =
|
||||
requireNonNull(savedNetworkConfig.getValue()).ssid;
|
||||
if (!networkName.equals(group.getNetworkName())) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("expected networkName: " + networkName);
|
||||
LOG.info("received networkName: " + group.getNetworkName());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -304,26 +297,31 @@ class HotspotManager implements ActionListener {
|
||||
return "WIFI:S:" + ssid + ";T:WPA;P:" + password + ";;";
|
||||
}
|
||||
|
||||
static String getSsid(SecureRandom random) {
|
||||
return "DIRECT-" + getRandomString(random, 2) + "-" +
|
||||
getRandomString(random, 10);
|
||||
}
|
||||
|
||||
static String getPassword(SecureRandom random) {
|
||||
return getRandomString(random, 8);
|
||||
}
|
||||
|
||||
private static final String digits = "123456789"; // avoid 0
|
||||
private static final String letters = "abcdefghijkmnopqrstuvwxyz"; // no l
|
||||
private static final String LETTERS = "ABCDEFGHJKLMNPQRSTUVWXYZ"; // no I, O
|
||||
|
||||
private String getRandomString(int length) {
|
||||
private static String getRandomString(SecureRandom random, int length) {
|
||||
char[] c = new char[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (random.nextBoolean()) {
|
||||
c[i] = random(digits);
|
||||
c[i] = digits.charAt(random.nextInt(digits.length()));
|
||||
} else if (random.nextBoolean()) {
|
||||
c[i] = random(letters);
|
||||
c[i] = letters.charAt(random.nextInt(letters.length()));
|
||||
} else {
|
||||
c[i] = random(LETTERS);
|
||||
c[i] = LETTERS.charAt(random.nextInt(LETTERS.length()));
|
||||
}
|
||||
}
|
||||
return new String(c);
|
||||
}
|
||||
|
||||
private char random(String universe) {
|
||||
return universe.charAt(random.nextInt(universe.length()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import org.briarproject.bramble.api.db.TransactionManager;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.settings.Settings;
|
||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.hotspot.HotspotManager.HotspotListener;
|
||||
@@ -28,10 +30,12 @@ import java.util.logging.Logger;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
|
||||
@NotNullByDefault
|
||||
@@ -40,9 +44,14 @@ class HotspotViewModel extends DbViewModel
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(HotspotViewModel.class.getName());
|
||||
private static final String HOTSPOT_NAMESPACE = "hotspot";
|
||||
private static final String HOTSPOT_KEY_SSID = "ssid";
|
||||
private static final String HOTSPOT_KEY_PASS = "pass";
|
||||
|
||||
@IoExecutor
|
||||
private final Executor ioExecutor;
|
||||
private final SettingsManager settingsManager;
|
||||
private final SecureRandom random;
|
||||
private final AndroidNotificationManager notificationManager;
|
||||
private final HotspotManager hotspotManager;
|
||||
private final WebServerManager webServerManager;
|
||||
@@ -51,6 +60,8 @@ class HotspotViewModel extends DbViewModel
|
||||
new MutableLiveData<>();
|
||||
private final MutableLiveEvent<Boolean> peerConnected =
|
||||
new MutableLiveEvent<>();
|
||||
private final MutableLiveData<NetworkConfig> savedNetworkConfig =
|
||||
new MutableLiveData<>();
|
||||
|
||||
@Nullable
|
||||
// Field to temporarily store the network config received via onHotspotStarted()
|
||||
@@ -64,14 +75,48 @@ class HotspotViewModel extends DbViewModel
|
||||
TransactionManager db,
|
||||
AndroidExecutor androidExecutor,
|
||||
@IoExecutor Executor ioExecutor,
|
||||
SettingsManager settingsManager,
|
||||
SecureRandom secureRandom,
|
||||
AndroidNotificationManager notificationManager) {
|
||||
super(app, dbExecutor, lifecycleManager, db, androidExecutor);
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.settingsManager = settingsManager;
|
||||
this.random = secureRandom;
|
||||
this.notificationManager = notificationManager;
|
||||
hotspotManager =
|
||||
new HotspotManager(app, ioExecutor, secureRandom, this);
|
||||
new HotspotManager(app, ioExecutor, savedNetworkConfig, this);
|
||||
webServerManager = new WebServerManager(app, this);
|
||||
// get or set persistent SSID and password
|
||||
if (SDK_INT >= 29) getOrSetNetworkConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Store persistent Wi-Fi SSID and passphrase in Settings to improve UX
|
||||
* so that users don't have to change them when attempting to connect.
|
||||
* Works only on API 29 and above.
|
||||
*/
|
||||
@RequiresApi(29)
|
||||
private void getOrSetNetworkConfig() {
|
||||
runOnDbThread(false, txn -> {
|
||||
Settings settings =
|
||||
settingsManager.getSettings(txn, HOTSPOT_NAMESPACE);
|
||||
String ssid = settings.get(HOTSPOT_KEY_SSID);
|
||||
String pass = settings.get(HOTSPOT_KEY_PASS);
|
||||
if (ssid == null || pass == null) {
|
||||
ssid = HotspotManager.getSsid(random);
|
||||
pass = HotspotManager.getPassword(random);
|
||||
settings.put(HOTSPOT_KEY_SSID, ssid);
|
||||
settings.put(HOTSPOT_KEY_PASS, pass);
|
||||
settingsManager.mergeSettings(txn, settings, HOTSPOT_NAMESPACE);
|
||||
}
|
||||
savedNetworkConfig.postValue(new NetworkConfig(ssid, pass, null));
|
||||
}, error -> {
|
||||
handleException(error);
|
||||
// probably never happens, but if lets use non-persistent data
|
||||
String ssid = HotspotManager.getSsid(random);
|
||||
String pass = HotspotManager.getPassword(random);
|
||||
savedNetworkConfig.postValue(new NetworkConfig(ssid, pass, null));
|
||||
});
|
||||
}
|
||||
|
||||
@UiThread
|
||||
|
||||
Reference in New Issue
Block a user