WIP: Bluetooth can actually transfer indexes and apks over HTTPish!

It is very hacky, and I did it through the non-swap interface, and it
only works once then the state stuffs up and it no longer accepts incomming
connections, but it worked! Now to smooth out all the things.
This commit is contained in:
Peter Serwylo 2015-07-20 23:57:16 +10:00
parent 9da6893ac3
commit 9180c9f5c0
16 changed files with 220 additions and 513 deletions

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<org.fdroid.fdroid.views.swap.BluetoothDeviceListView
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white">
</org.fdroid.fdroid.views.swap.BluetoothDeviceListView>

View File

@ -39,7 +39,6 @@ import org.fdroid.fdroid.localrepo.peers.BluetoothFinder;
import org.fdroid.fdroid.localrepo.peers.BonjourFinder; import org.fdroid.fdroid.localrepo.peers.BonjourFinder;
import org.fdroid.fdroid.localrepo.peers.Peer; import org.fdroid.fdroid.localrepo.peers.Peer;
import org.fdroid.fdroid.localrepo.type.BluetoothSwap; import org.fdroid.fdroid.localrepo.type.BluetoothSwap;
import org.fdroid.fdroid.localrepo.type.BonjourBroadcast;
import org.fdroid.fdroid.localrepo.type.SwapType; import org.fdroid.fdroid.localrepo.type.SwapType;
import org.fdroid.fdroid.localrepo.type.WifiSwap; import org.fdroid.fdroid.localrepo.type.WifiSwap;
import org.fdroid.fdroid.net.WifiStateChangeService; import org.fdroid.fdroid.net.WifiStateChangeService;
@ -65,8 +64,10 @@ import java.util.TimerTask;
public class SwapService extends Service { public class SwapService extends Service {
private static final String TAG = "SwapManager"; private static final String TAG = "SwapManager";
private static final String SHARED_PREFERENCES = "swap-state"; public static final String SHARED_PREFERENCES = "swap-state";
private static final String KEY_APPS_TO_SWAP = "appsToSwap"; private static final String KEY_APPS_TO_SWAP = "appsToSwap";
private static final String KEY_BLUETOOTH_ENABLED = "bluetoothEnabled";
private static final String KEY_WIFI_ENABLED = "wifiEnabled";
@NonNull @NonNull
private Set<String> appsToSwap = new HashSet<>(); private Set<String> appsToSwap = new HashSet<>();
@ -353,6 +354,25 @@ public class SwapService extends Service {
} }
// =============================================================
// Remember which swap technologies a user used in the past
// =============================================================
private void persistPreferredSwapTypes() {
persistence().edit()
.putBoolean(KEY_BLUETOOTH_ENABLED, bluetoothSwap.isConnected())
.putBoolean(KEY_WIFI_ENABLED, wifiSwap.isConnected())
.commit();
}
private boolean wasBluetoothEnabled() {
return persistence().getBoolean(KEY_BLUETOOTH_ENABLED, false);
}
private boolean wasWifiEnabled() {
return persistence().getBoolean(KEY_WIFI_ENABLED, false);
}
// ========================================== // ==========================================
// Local repo stop/start/restart handling // Local repo stop/start/restart handling
// ========================================== // ==========================================
@ -409,6 +429,14 @@ public class SwapService extends Service {
// Interacting with Bluetooth adapter // Interacting with Bluetooth adapter
// ========================================== // ==========================================
public BonjourFinder getBonjourFinder() {
return bonjourFinder;
}
public BluetoothFinder getBluetoothFinder() {
return bluetoothFinder;
}
public boolean isBluetoothDiscoverable() { public boolean isBluetoothDiscoverable() {
return bluetoothSwap.isConnected(); return bluetoothSwap.isConnected();
} }
@ -487,6 +515,16 @@ public class SwapService extends Service {
IntentFilter filter = new IntentFilter(BLUETOOTH_STATE_CHANGE); IntentFilter filter = new IntentFilter(BLUETOOTH_STATE_CHANGE);
filter.addAction(WIFI_STATE_CHANGE); filter.addAction(WIFI_STATE_CHANGE);
LocalBroadcastManager.getInstance(this).registerReceiver(receiveSwapStatusChanged, filter); LocalBroadcastManager.getInstance(this).registerReceiver(receiveSwapStatusChanged, filter);
if (wasBluetoothEnabled()) {
Log.d(TAG, "Previously the user enabled Bluetooth swap, so enabling again automatically.");
bluetoothSwap.startInBackground();
}
if (wasWifiEnabled()) {
Log.d(TAG, "Previously the user enabled Wifi swap, so enabling again automatically.");
wifiSwap.startInBackground();
}
} }
/** /**
@ -505,6 +543,7 @@ public class SwapService extends Service {
detachService(); detachService();
} }
} }
persistPreferredSwapTypes();
} }
}; };
@ -520,15 +559,10 @@ public class SwapService extends Service {
} }
public void disableAllSwapping() { public void disableAllSwapping() {
new AsyncTask<Void, Void, Void>() { Log.i(TAG, "Asked to stop swapping, will stop bluetooth, wifi, and move service to BG for GC.");
@Override getBluetoothSwap().stopInBackground();
protected Void doInBackground(Void... params) { getWifiSwap().stopInBackground();
getBluetoothSwap().stop(); detachService();
getWifiSwap().stop();
detachService();
return null;
}
}.execute();
} }
@Override @Override

View File

@ -8,9 +8,8 @@ import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.util.Log; import android.util.Log;
import org.fdroid.fdroid.net.bluetooth.BluetoothServer; import org.fdroid.fdroid.localrepo.type.BluetoothSwap;
// TODO: Still to be implemented
public class BluetoothFinder extends PeerFinder<BluetoothPeer> { public class BluetoothFinder extends PeerFinder<BluetoothPeer> {
private static final String TAG = "BluetoothFinder"; private static final String TAG = "BluetoothFinder";
@ -69,8 +68,8 @@ public class BluetoothFinder extends PeerFinder<BluetoothPeer> {
if (adapter.isDiscovering()) { if (adapter.isDiscovering()) {
// TODO: Can we reset the discovering timeout, so that it doesn't, e.g. time out // TODO: Can we reset the discovering timeout, so that it doesn't, e.g. time out
// in 3 seconds because we had already almost completed the previous scan? // in 3 seconds because we had already almost completed the previous scan?
Log.d(TAG, "Requested bluetooth scan when already scanning, will cancel previous scan before continuing."); Log.d(TAG, "Requested bluetooth scan when already scanning, so will ignore request.");
adapter.cancelDiscovery(); return;
} }
if (!adapter.startDiscovery()) { if (!adapter.startDiscovery()) {
@ -90,7 +89,7 @@ public class BluetoothFinder extends PeerFinder<BluetoothPeer> {
} }
private void onDeviceFound(BluetoothDevice device) { private void onDeviceFound(BluetoothDevice device) {
if (device != null && device.getName() != null && device.getName().startsWith(BluetoothServer.BLUETOOTH_NAME_TAG)) { if (device != null && device.getName() != null && device.getName().startsWith(BluetoothSwap.BLUETOOTH_NAME_TAG)) {
foundPeer(new BluetoothPeer(device)); foundPeer(new BluetoothPeer(device));
} }
} }

View File

@ -4,9 +4,8 @@ import android.bluetooth.BluetoothDevice;
import android.os.Parcel; import android.os.Parcel;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.net.bluetooth.BluetoothServer; import org.fdroid.fdroid.localrepo.type.BluetoothSwap;
// TODO: Still to be implemented.
public class BluetoothPeer implements Peer { public class BluetoothPeer implements Peer {
private BluetoothDevice device; private BluetoothDevice device;
@ -22,7 +21,7 @@ public class BluetoothPeer implements Peer {
@Override @Override
public String getName() { public String getName() {
return device.getName().replaceAll("^" + BluetoothServer.BLUETOOTH_NAME_TAG, ""); return device.getName().replaceAll("^" + BluetoothSwap.BLUETOOTH_NAME_TAG, "");
} }
@Override @Override
@ -37,7 +36,7 @@ public class BluetoothPeer implements Peer {
@Override @Override
public String getRepoAddress() { public String getRepoAddress() {
return "bluetooth://" + device.getAddress() + "/fdroid/repo"; return "bluetooth://" + device.getAddress().replace(':', '-') + "/fdroid/repo";
} }
@Override @Override

View File

@ -68,6 +68,7 @@ public class BonjourFinder extends PeerFinder<BonjourPeer> implements ServiceLis
@Override @Override
protected void onPostExecute(Void result) { protected void onPostExecute(Void result) {
// TODO: This is not threadsafe - cancelling the discovery will make jmdns null, but it could happen after this check and before call to addServiceListener().
if (jmdns != null) { if (jmdns != null) {
Log.d(TAG, "Adding mDNS service listeners for " + HTTP_SERVICE_TYPE + " and " + HTTPS_SERVICE_TYPE); Log.d(TAG, "Adding mDNS service listeners for " + HTTP_SERVICE_TYPE + " and " + HTTPS_SERVICE_TYPE);
jmdns.addServiceListener(HTTP_SERVICE_TYPE, BonjourFinder.this); jmdns.addServiceListener(HTTP_SERVICE_TYPE, BonjourFinder.this);
@ -80,13 +81,16 @@ public class BonjourFinder extends PeerFinder<BonjourPeer> implements ServiceLis
} }
private void listServices() { private void listServices() {
final JmDNS mdns = jmdns;
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
Log.d(TAG, "Explicitly querying for services, in addition to waiting for notifications."); Log.d(TAG, "Explicitly querying for services, in addition to waiting for notifications.");
addFDroidServices(jmdns.list(HTTP_SERVICE_TYPE)); addFDroidServices(mdns.list(HTTP_SERVICE_TYPE));
addFDroidServices(jmdns.list(HTTPS_SERVICE_TYPE)); addFDroidServices(mdns.list(HTTPS_SERVICE_TYPE));
return null; return null;
} }

View File

@ -6,6 +6,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import org.fdroid.fdroid.localrepo.SwapService; import org.fdroid.fdroid.localrepo.SwapService;
@ -14,11 +15,15 @@ import org.fdroid.fdroid.net.bluetooth.BluetoothServer;
public class BluetoothSwap extends SwapType { public class BluetoothSwap extends SwapType {
private static final String TAG = "BluetoothBroadcastType"; private static final String TAG = "BluetoothBroadcastType";
public final static String BLUETOOTH_NAME_TAG = "FDroid:";
@NonNull @NonNull
private final BluetoothAdapter adapter; private final BluetoothAdapter adapter;
private final BluetoothServer server; @Nullable
private BluetoothServer server;
private String deviceBluetoothName = null;
public static SwapType create(@NonNull Context context) { public static SwapType create(@NonNull Context context) {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@ -27,12 +32,11 @@ public class BluetoothSwap extends SwapType {
} else { } else {
return new BluetoothSwap(context, adapter); return new BluetoothSwap(context, adapter);
} }
}; }
private BluetoothSwap(@NonNull Context context, @NonNull BluetoothAdapter adapter) { private BluetoothSwap(@NonNull Context context, @NonNull BluetoothAdapter adapter) {
super(context); super(context);
this.adapter = adapter; this.adapter = adapter;
this.server = new BluetoothServer(context, context.getFilesDir());
context.registerReceiver(new BroadcastReceiver() { context.registerReceiver(new BroadcastReceiver() {
@Override @Override
@ -52,17 +56,39 @@ public class BluetoothSwap extends SwapType {
}, new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED)); }, new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED));
} }
@Override
public boolean isConnected() {
return server != null && server.isRunning() && super.isConnected();
}
@Override @Override
public void start() { public void start() {
if (server.isAlive()) { if (server != null) {
Log.d(TAG, "Attempting to start Bluetooth swap, but it appears to be running already."); Log.d(TAG, "Attempting to start Bluetooth swap, but it appears to be running already. Will cancel it so it can be restarted.");
return; server.close();
server = null;
} }
server = new BluetoothServer(this, context.getFilesDir());
sendBroadcast(SwapService.EXTRA_STARTING); sendBroadcast(SwapService.EXTRA_STARTING);
//store the original bluetoothname, and update this one to be unique
deviceBluetoothName = adapter.getName();
Log.d(TAG, "Prefixing Bluetooth adapter name with " + BLUETOOTH_NAME_TAG + " to make it identifiable as a swap device.");
if (!deviceBluetoothName.startsWith(BLUETOOTH_NAME_TAG))
adapter.setName(BLUETOOTH_NAME_TAG + deviceBluetoothName);
if (!adapter.getName().startsWith(BLUETOOTH_NAME_TAG)) {
Log.e(TAG, "Couldn't change the name of the Bluetooth adapter, it will not get recognized by other swap clients.");
// TODO: Should we bail here?
}
if (!adapter.isEnabled()) { if (!adapter.isEnabled()) {
Log.d(TAG, "Bluetooth adapter is disabled, attempting to enable.");
if (!adapter.enable()) { if (!adapter.enable()) {
Log.d(TAG, "Could not enable Bluetooth adapter, so bailing out of Bluetooth swap.");
setConnected(false); setConnected(false);
return; return;
} }
@ -79,7 +105,7 @@ public class BluetoothSwap extends SwapType {
@Override @Override
public void stop() { public void stop() {
if (server.isAlive()) { if (server != null && server.isAlive()) {
server.close(); server.close();
setConnected(false); setConnected(false);
} else { } else {
@ -87,6 +113,11 @@ public class BluetoothSwap extends SwapType {
} }
} }
protected void onStopped() {
Log.d(TAG, "Resetting bluetooth device name to " + deviceBluetoothName + " after swapping.");
adapter.setName(deviceBluetoothName);
}
@Override @Override
public String getBroadcastAction() { public String getBroadcastAction() {
return SwapService.BLUETOOTH_STATE_CHANGE; return SwapService.BLUETOOTH_STATE_CHANGE;

View File

@ -2,6 +2,7 @@ package org.fdroid.fdroid.localrepo.type;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v4.content.LocalBroadcastManager; import android.support.v4.content.LocalBroadcastManager;
@ -38,10 +39,13 @@ public abstract class SwapType {
sendBroadcast(SwapService.EXTRA_STARTED); sendBroadcast(SwapService.EXTRA_STARTED);
} else { } else {
isConnected = false; isConnected = false;
onStopped();
sendBroadcast(SwapService.EXTRA_STOPPED); sendBroadcast(SwapService.EXTRA_STOPPED);
} }
} }
protected void onStopped() {}
/** /**
* Sends either a {@link org.fdroid.fdroid.localrepo.SwapService#EXTRA_STARTING}, * Sends either a {@link org.fdroid.fdroid.localrepo.SwapService#EXTRA_STARTING},
* {@link org.fdroid.fdroid.localrepo.SwapService#EXTRA_STARTED} or * {@link org.fdroid.fdroid.localrepo.SwapService#EXTRA_STARTED} or
@ -55,7 +59,7 @@ public abstract class SwapType {
} }
} }
public final boolean isConnected() { public boolean isConnected() {
return isConnected; return isConnected;
} }
@ -84,4 +88,15 @@ public abstract class SwapType {
} }
}.execute(); }.execute();
} }
public void stopInBackground() {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
stop();
return null;
}
}.execute();
}
} }

View File

@ -60,19 +60,24 @@ public class WifiSwap extends SwapType {
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
Log.i(TAG, "we've been asked to stop the webserver: " + msg.obj); Log.i(TAG, "we've been asked to stop the webserver: " + msg.obj);
setConnected(false);
localHttpd.stop(); localHttpd.stop();
} }
}; };
try { try {
Log.d(TAG, "Starting swap webserver..."); Log.d(TAG, "Starting swap webserver...");
sendBroadcast(SwapService.EXTRA_STARTING);
localHttpd.start(); localHttpd.start();
setConnected(true);
Log.d(TAG, "Swap webserver started."); Log.d(TAG, "Swap webserver started.");
} catch (BindException e) { } catch (BindException e) {
int prev = FDroidApp.port; int prev = FDroidApp.port;
FDroidApp.port = FDroidApp.port + new Random().nextInt(1111); FDroidApp.port = FDroidApp.port + new Random().nextInt(1111);
setConnected(false);
Log.w(TAG, "port " + prev + " occupied, trying on " + FDroidApp.port + "!"); Log.w(TAG, "port " + prev + " occupied, trying on " + FDroidApp.port + "!");
context.startService(new Intent(context, WifiStateChangeService.class)); context.startService(new Intent(context, WifiStateChangeService.class));
} catch (IOException e) { } catch (IOException e) {
setConnected(false);
Log.e(TAG, "Could not start local repo HTTP server: " + e); Log.e(TAG, "Could not start local repo HTTP server: " + e);
Log.e(TAG, Log.getStackTraceString(e)); Log.e(TAG, Log.getStackTraceString(e));
} }

View File

@ -3,6 +3,7 @@ package org.fdroid.fdroid.net;
import android.content.Context; import android.content.Context;
import android.util.Log; import android.util.Log;
import org.apache.commons.io.input.BoundedInputStream; import org.apache.commons.io.input.BoundedInputStream;
import org.fdroid.fdroid.net.bluetooth.BluetoothClient;
import org.fdroid.fdroid.net.bluetooth.BluetoothConnection; import org.fdroid.fdroid.net.bluetooth.BluetoothConnection;
import org.fdroid.fdroid.net.bluetooth.FileDetails; import org.fdroid.fdroid.net.bluetooth.FileDetails;
import org.fdroid.fdroid.net.bluetooth.httpish.Request; import org.fdroid.fdroid.net.bluetooth.httpish.Request;
@ -23,21 +24,15 @@ public class BluetoothDownloader extends Downloader {
private FileDetails fileDetails; private FileDetails fileDetails;
private final String sourcePath; private final String sourcePath;
public BluetoothDownloader(BluetoothConnection connection, String sourcePath, Context ctx) throws IOException { public BluetoothDownloader(String macAddress, String sourcePath, Context ctx) throws IOException {
super(ctx); super(ctx);
this.connection = connection; this.connection = new BluetoothClient(macAddress).openConnection();
this.sourcePath = sourcePath; this.sourcePath = sourcePath;
} }
public BluetoothDownloader(BluetoothConnection connection, String sourcePath, File destFile) throws FileNotFoundException, MalformedURLException { public BluetoothDownloader(String macAddress, String sourcePath, File destFile) throws IOException {
super(destFile); super(destFile);
this.connection = connection; this.connection = new BluetoothClient(macAddress).openConnection();
this.sourcePath = sourcePath;
}
public BluetoothDownloader(BluetoothConnection connection, String sourcePath, OutputStream output) throws MalformedURLException {
super(output);
this.connection = connection;
this.sourcePath = sourcePath; this.sourcePath = sourcePath;
} }

View File

@ -11,8 +11,8 @@ public class DownloaderFactory {
public static Downloader create(String url, Context context) throws IOException { public static Downloader create(String url, Context context) throws IOException {
Uri uri = Uri.parse(url); Uri uri = Uri.parse(url);
if (isBluetoothAddress(uri)) { if (isBluetoothAddress(uri)) {
// TODO: Don't pass null!!! String macAddress = uri.getHost().replace("-", ":");
return new BluetoothDownloader(null, uri.getPath(), context); return new BluetoothDownloader(macAddress, uri.getPath(), context);
} else if (isOnionAddress(url)) { } else if (isOnionAddress(url)) {
return new TorHttpDownloader(url, context); return new TorHttpDownloader(url, context);
} else { } else {
@ -23,8 +23,8 @@ public class DownloaderFactory {
public static Downloader create(String url, File destFile) throws IOException { public static Downloader create(String url, File destFile) throws IOException {
Uri uri = Uri.parse(url); Uri uri = Uri.parse(url);
if (isBluetoothAddress(uri)) { if (isBluetoothAddress(uri)) {
// TODO: Don't pass null!!! String macAddress = uri.getHost().replace("-", ":");
return new BluetoothDownloader(null, uri.getPath(), destFile); return new BluetoothDownloader(macAddress, uri.getPath(), destFile);
} else if (isOnionAddress(url)) { } else if (isOnionAddress(url)) {
return new TorHttpDownloader(url, destFile); return new TorHttpDownloader(url, destFile);
} else { } else {

View File

@ -1,5 +1,6 @@
package org.fdroid.fdroid.net.bluetooth; package org.fdroid.fdroid.net.bluetooth;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothSocket;
@ -10,12 +11,16 @@ public class BluetoothClient {
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static final String TAG = "BluetoothClient"; private static final String TAG = "BluetoothClient";
private BluetoothDevice device; private final BluetoothDevice device;
public BluetoothClient(BluetoothDevice device) { public BluetoothClient(BluetoothDevice device) {
this.device = device; this.device = device;
} }
public BluetoothClient(String macAddress) {
device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(macAddress);
}
public BluetoothConnection openConnection() throws IOException { public BluetoothConnection openConnection() throws IOException {
BluetoothSocket socket = device.createInsecureRfcommSocketToServiceRecord(BluetoothConstants.fdroidUuid()); BluetoothSocket socket = device.createInsecureRfcommSocketToServiceRecord(BluetoothConstants.fdroidUuid());
BluetoothConnection connection = new BluetoothConnection(socket); BluetoothConnection connection = new BluetoothConnection(socket);

View File

@ -1,6 +1,7 @@
package org.fdroid.fdroid.net.bluetooth; package org.fdroid.fdroid.net.bluetooth;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothSocket;
import android.os.Build; import android.os.Build;
import android.util.Log; import android.util.Log;

View File

@ -3,25 +3,19 @@ package org.fdroid.fdroid.net.bluetooth;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.Build;
import android.util.Log; import android.util.Log;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.net.HttpDownloader; import org.fdroid.fdroid.localrepo.type.BluetoothSwap;
import org.fdroid.fdroid.net.bluetooth.httpish.Request; import org.fdroid.fdroid.net.bluetooth.httpish.Request;
import org.fdroid.fdroid.net.bluetooth.httpish.Response; import org.fdroid.fdroid.net.bluetooth.httpish.Response;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -37,23 +31,23 @@ public class BluetoothServer extends Thread {
private static final String TAG = "BluetoothServer"; private static final String TAG = "BluetoothServer";
private BluetoothServerSocket serverSocket; private BluetoothServerSocket serverSocket;
private List<Connection> clients = new ArrayList<>(); private List<ClientConnection> clients = new ArrayList<>();
private final Context context;
private String deviceBluetoothName = null;
public final static String BLUETOOTH_NAME_TAG = "FDroid:";
private final File webRoot; private final File webRoot;
private final BluetoothSwap swap;
private boolean isRunning = false;
public BluetoothServer(Context context, File webRoot) { public BluetoothServer(BluetoothSwap swap, File webRoot) {
this.context = context.getApplicationContext();
this.webRoot = webRoot; this.webRoot = webRoot;
this.swap = swap;
} }
public boolean isRunning() { return isRunning; }
public void close() { public void close() {
for (Connection connection : clients) { for (ClientConnection clientConnection : clients) {
connection.interrupt(); clientConnection.interrupt();
} }
interrupt(); interrupt();
@ -61,61 +55,53 @@ public class BluetoothServer extends Thread {
if (serverSocket != null) { if (serverSocket != null) {
Utils.closeQuietly(serverSocket); Utils.closeQuietly(serverSocket);
} }
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
adapter.setName(deviceBluetoothName.replaceAll("/^" + BLUETOOTH_NAME_TAG + "/",""));
} }
@Override @Override
public void run() { public void run() {
isRunning = true;
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
//store the original bluetoothname, and update this one to be unique
deviceBluetoothName = adapter.getName();
if (!deviceBluetoothName.startsWith(BLUETOOTH_NAME_TAG))
adapter.setName(BLUETOOTH_NAME_TAG + deviceBluetoothName);
try { try {
serverSocket = adapter.listenUsingInsecureRfcommWithServiceRecord("FDroid App Swap", BluetoothConstants.fdroidUuid()); serverSocket = adapter.listenUsingInsecureRfcommWithServiceRecord("FDroid App Swap", BluetoothConstants.fdroidUuid());
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "Error starting Bluetooth server socket, will stop the server now - " + e.getMessage()); Log.e(TAG, "Error starting Bluetooth server socket, will stop the server now: " + e.getMessage());
swap.stop();
isRunning = false;
return; return;
} }
while (true) { while (true) {
if (isInterrupted()) { if (isInterrupted()) {
Log.d(TAG, "Server stopped so will terminate loop looking for client connections.");
break; break;
} }
try { try {
BluetoothSocket clientSocket = serverSocket.accept(); BluetoothSocket clientSocket = serverSocket.accept();
if (clientSocket != null && !isInterrupted()) { if (clientSocket != null) {
Connection client = new Connection(context, clientSocket, webRoot); if (!isInterrupted()) {
Log.d(TAG, "Server stopped after socket accepted from client, but before initiating connection.");
break;
}
ClientConnection client = new ClientConnection(clientSocket, webRoot);
client.start(); client.start();
clients.add(client); clients.add(client);
} else {
break;
} }
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "Error receiving client connection over Bluetooth server socket, will continue listening for other clients - " + e.getMessage()); Log.e(TAG, "Error receiving client connection over Bluetooth server socket, will continue listening for other clients: " + e.getMessage());
} }
} }
isRunning = false;
} }
private static class Connection extends Thread { private static class ClientConnection extends Thread {
private final Context context;
private final BluetoothSocket socket; private final BluetoothSocket socket;
private final File webRoot; private final File webRoot;
public Connection(Context context, BluetoothSocket socket, File webRoot) { public ClientConnection(BluetoothSocket socket, File webRoot) {
this.context = context.getApplicationContext();
this.socket = socket; this.socket = socket;
this.webRoot = webRoot; this.webRoot = webRoot;
} }
@ -142,13 +128,11 @@ public class BluetoothServer extends Thread {
handleRequest(incomingRequest).send(connection); handleRequest(incomingRequest).send(connection);
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "Error receiving incoming connection over bluetooth - " + e.getMessage()); Log.e(TAG, "Error receiving incoming connection over bluetooth - " + e.getMessage());
break;
} }
if (isInterrupted()) if (isInterrupted())
break; break;
} }
} }
@ -160,7 +144,6 @@ public class BluetoothServer extends Thread {
Response.Builder builder = null; Response.Builder builder = null;
try { try {
// HttpDownloader downloader = new HttpDownloader("http://127.0.0.1:" + ( FDroidApp.port) + "/" + request.getPath(), context);
int statusCode = 404; int statusCode = 404;
int totalSize = -1; int totalSize = -1;

View File

@ -1,376 +0,0 @@
package org.fdroid.fdroid.views.swap;
import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.ContentLoadingProgressBar;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.localrepo.SwapService;
import org.fdroid.fdroid.net.BluetoothDownloader;
import org.fdroid.fdroid.net.bluetooth.BluetoothClient;
import org.fdroid.fdroid.net.bluetooth.BluetoothConnection;
import org.fdroid.fdroid.net.bluetooth.BluetoothServer;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
public class BluetoothDeviceListView extends ListView implements
SwapWorkflowActivity.InnerView,
ListView.OnItemClickListener {
private static final String TAG = "BluetoothDeviceListView";
private Adapter adapter = null;
private MenuItem scanMenuItem;
private MenuItem cancelMenuItem;
private boolean firstScan = true;
public BluetoothDeviceListView(Context context) {
super(context);
}
public BluetoothDeviceListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public BluetoothDeviceListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public BluetoothDeviceListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public boolean buildMenu(Menu menu, @NonNull MenuInflater menuInflater) {
menuInflater.inflate(R.menu.swap_scan, menu);
final int flags = MenuItemCompat.SHOW_AS_ACTION_ALWAYS | MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT;
scanMenuItem = menu.findItem(R.id.action_scan);
scanMenuItem.setVisible(true);
MenuItemCompat.setShowAsAction(scanMenuItem, flags);
scanMenuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
initiateBluetoothScan();
return true;
}
});
cancelMenuItem = menu.findItem(R.id.action_cancel);
cancelMenuItem.setVisible(false);
MenuItemCompat.setShowAsAction(cancelMenuItem, flags);
cancelMenuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
cancelBluetoothScan();
return true;
}
});
return true;
}
@Override
public int getStep() {
return SwapService.STEP_BLUETOOTH;
}
@Override
public int getPreviousStep() {
return SwapService.STEP_JOIN_WIFI;
}
@Override
public int getToolbarColour() {
return R.color.swap_blue;
}
@Override
public String getToolbarTitle() {
return getContext().getString(R.string.swap_use_bluetooth);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
adapter = new Adapter(
getContext(),
R.layout.select_local_apps_list_item
);
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View headerView = inflater.inflate(R.layout.swap_bluetooth_header, this, false);
addHeaderView(headerView);
setAdapter(adapter);
setOnItemClickListener(this);
final BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter();
final TextView deviceName = (TextView) headerView.findViewById(R.id.device_name);
deviceName.setText(bluetooth.getName());
final TextView address = (TextView) headerView.findViewById(R.id.device_address);
address.setText(bluetooth.getAddress());
initiateBluetoothScan();
// populateBondedDevices();
}
private void cancelBluetoothScan() {
Log.d(TAG, "Cancelling bluetooth scan.");
cancelMenuItem.setVisible(false);
scanMenuItem.setVisible(true);
final BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter();
bluetooth.cancelDiscovery();
getLoadingIndicator().hide();
}
private ContentLoadingProgressBar getLoadingIndicator() {
return ((ContentLoadingProgressBar)findViewById(R.id.loading_indicator));
}
private void initiateBluetoothScan()
{
Log.d(TAG, "Starting bluetooth scan...");
if (cancelMenuItem != null) {
cancelMenuItem.setVisible(true);
scanMenuItem.setVisible(false);
}
final ContentLoadingProgressBar loadingBar = getLoadingIndicator();
loadingBar.show();
final BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter();
if (firstScan) {
final BroadcastReceiver deviceFoundReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (BluetoothDevice.ACTION_FOUND.equals(intent.getAction())) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d(TAG, "Found bluetooth device: " + device.toString());
if (device != null && device.getName() != null)
if (device.getName().contains(BluetoothServer.BLUETOOTH_NAME_TAG)) {
boolean exists = false;
for (int i = 0; i < adapter.getCount(); i++) {
if (adapter.getItem(i).getAddress().equals(device.getAddress())) {
exists = true;
break;
}
}
if (!exists) {
adapter.add(device);
}
}
}
}
};
final BroadcastReceiver scanCompleteReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Scan complete: " + intent.getAction());
loadingBar.hide();
cancelMenuItem.setVisible(false);
scanMenuItem.setVisible(true);
}
};
getContext().registerReceiver(deviceFoundReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
getContext().registerReceiver(scanCompleteReceiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED));
firstScan = false;
}
else
{
if (bluetooth.isDiscovering())
{
bluetooth.cancelDiscovery();
}
}
if (!bluetooth.startDiscovery()) {
// TODO: Discovery did not start for some reason :(
Log.e(TAG, "Could not start bluetooth discovery, but am not sure why :(");
Toast.makeText(getContext(),"There was a problem looking for Bluetooth devices",Toast.LENGTH_SHORT).show();
}
}
private void populateBondedDevices()
{
for (BluetoothDevice device : BluetoothAdapter.getDefaultAdapter().getBondedDevices()) {
adapter.add(device);
}
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// "position" includes the header view, so ignore that.
if (position == 0) {
return;
}
BluetoothDevice device = adapter.getItem(position - 1);
// TODO: I think that I can connect regardless of the bond state.
// It sounds like when I attempt to connect to a non-bonded peer, then
// Android initiates the pairing dialog on our behalf.
BluetoothClient client = new BluetoothClient(device);
try {
Log.d(TAG, "Testing bluetooth connection (opening connection first).");
BluetoothConnection connection = client.openConnection();
ByteArrayOutputStream stream = new ByteArrayOutputStream(4096);
BluetoothDownloader downloader = new BluetoothDownloader(connection, "/", stream);
downloader.downloadUninterrupted();
String result = stream.toString();
Log.d(TAG, "Download complete.");
Log.d(TAG, result);
Log.d(TAG, "Downloading again...");
downloader = new BluetoothDownloader(connection, "/fdroid/repo/index.xml", stream);
downloader.downloadUninterrupted();
result = stream.toString();
Log.d(TAG, "Download complete.");
Log.d(TAG, result);
/*Log.d(TAG, "Creating HEAD request for resource at \"/\"...");
Request head = Request.createGET("/", connection);
Log.d(TAG, "Sending request...");
Response response = head.send();
Log.d(TAG, "Response from bluetooth: " + response.getStatusCode());
String contents = response.readContents();
Log.d(TAG, contents);*/
} catch (IOException e) {
Log.e(TAG, "Error: " + e.getMessage());
}
/*if (device.getBondState() == BluetoothDevice.BOND_NONE) {
// attempt to bond
} else if (device.getBondState() == BluetoothDevice.BOND_BONDING) {
// wait for bonding to finish
} else if (device.getBondState() == BluetoothDevice.BOND_BONDED) {
// connect
BluetoothClient client = new BluetoothClient(device);
}*/
}
private class Adapter extends ArrayAdapter<BluetoothDevice> {
public Adapter(Context context, int resource) {
super(context, resource);
}
public Adapter(Context context, int resource, int textViewResourceId) {
super(context, resource, textViewResourceId);
}
public Adapter(Context context, int resource, BluetoothDevice[] objects) {
super(context, resource, objects);
}
public Adapter(Context context, int resource, int textViewResourceId, BluetoothDevice[] objects) {
super(context, resource, textViewResourceId, objects);
}
public Adapter(Context context, int resource, List<BluetoothDevice> objects) {
super(context, resource, objects);
}
public Adapter(Context context, int resource, int textViewResourceId, List<BluetoothDevice> objects) {
super(context, resource, textViewResourceId, objects);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.simple_list_item_3, null);
} else {
view = convertView;
}
BluetoothDevice device = getItem(position);
TextView nameView = (TextView)view.findViewById(android.R.id.text1);
TextView addressView = (TextView)view.findViewById(android.R.id.text2);
//TextView descriptionView = (TextView)view.findViewById(R.id.text3);
nameView.setText(device.getName() == null ? getContext().getString(R.string.unknown) : device.getName());
addressView.setText(device.getAddress());
//descriptionView.setText(bondStateToLabel(device.getBondState()));
return view;
}
private String bondStateToLabel(int deviceBondState)
{
if (deviceBondState == BluetoothDevice.BOND_BONDED) {
// TODO: Is the term "Bonded device" common parlance among phone users?
// It sounds a bit technical to me, maybe something more lay like "Previously connected".
// Although it is technically not as accurate, it would make sense to more people...
return getContext().getString(R.string.swap_bluetooth_bonded_device);
} else if (deviceBondState == BluetoothDevice.BOND_BONDING) {
return getContext().getString(R.string.swap_bluetooth_bonding_device);
} else {
// TODO: Might be a little bit harsh, makes it sound more malicious than it should.
return getContext().getString(R.string.swap_bluetooth_unknown_device);
}
}
}
}

View File

@ -124,7 +124,7 @@ public class SelectAppsView extends ListView implements
@Override @Override
public int getPreviousStep() { public int getPreviousStep() {
return getState().isConnectingWithPeer() ? SwapService.STEP_JOIN_WIFI : SwapService.STEP_INTRO; return getState().isConnectingWithPeer() ? SwapService.STEP_INTRO : SwapService.STEP_JOIN_WIFI;
} }
@ColorRes @ColorRes

View File

@ -39,9 +39,9 @@ import org.fdroid.fdroid.data.NewRepoConfig;
import org.fdroid.fdroid.localrepo.LocalRepoManager; import org.fdroid.fdroid.localrepo.LocalRepoManager;
import org.fdroid.fdroid.localrepo.SwapService; import org.fdroid.fdroid.localrepo.SwapService;
import org.fdroid.fdroid.localrepo.peers.Peer; import org.fdroid.fdroid.localrepo.peers.Peer;
import org.fdroid.fdroid.net.bluetooth.BluetoothServer;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
@ -105,11 +105,12 @@ public class SwapWorkflowActivity extends AppCompatActivity {
private final ServiceConnection serviceConnection = new ServiceConnection() { private final ServiceConnection serviceConnection = new ServiceConnection() {
@Override @Override
public void onServiceConnected(ComponentName className, IBinder binder) { public void onServiceConnected(ComponentName className, IBinder binder) {
Log.d(TAG, "Swap service connected, enabling SwapManager to communicate with SwapService."); Log.d(TAG, "Swap service connected. Will hold onto it so we can talk to it regularly.");
service = ((SwapService.Binder)binder).getService(); service = ((SwapService.Binder)binder).getService();
showRelevantView(); showRelevantView();
} }
// TODO: What causes this? Do we need to stop swapping explicitly when this is invoked?
@Override @Override
public void onServiceDisconnected(ComponentName className) { public void onServiceDisconnected(ComponentName className) {
Log.d(TAG, "Swap service disconnected"); Log.d(TAG, "Swap service disconnected");
@ -133,6 +134,9 @@ public class SwapWorkflowActivity extends AppCompatActivity {
@Override @Override
public void onBackPressed() { public void onBackPressed() {
if (currentView.getStep() == SwapService.STEP_INTRO) { if (currentView.getStep() == SwapService.STEP_INTRO) {
if (service != null) {
service.disableAllSwapping();
}
finish(); finish();
} else { } else {
int nextStep = currentView.getPreviousStep(); int nextStep = currentView.getPreviousStep();
@ -235,6 +239,10 @@ public class SwapWorkflowActivity extends AppCompatActivity {
case SwapService.STEP_SUCCESS: case SwapService.STEP_SUCCESS:
showSwapConnected(); showSwapConnected();
break; break;
case SwapService.STEP_CONNECTING:
// TODO: Properly decide what to do here...
inflateInnerView(R.layout.swap_blank);
break;
} }
} }
@ -287,6 +295,10 @@ public class SwapWorkflowActivity extends AppCompatActivity {
} }
private void showIntro() { private void showIntro() {
// If we were previously swapping with a specific client, forget that we were doing that,
// as we are starting over now.
getService().swapWith(null);
if (!getService().isEnabled()) { if (!getService().isEnabled()) {
prepareInitialRepo(); prepareInitialRepo();
} }
@ -303,7 +315,7 @@ public class SwapWorkflowActivity extends AppCompatActivity {
} }
public void sendFDroid() { public void sendFDroid() {
// TODO: What is availble here? Currently we support Bluetooth (see main menu in F-Droid) // TODO: What is available here? Currently we support Bluetooth (see main menu in F-Droid)
// and Android Beam (try touching two devices together when in the app details view). // and Android Beam (try touching two devices together when in the app details view).
} }
@ -314,8 +326,8 @@ public class SwapWorkflowActivity extends AppCompatActivity {
if (updateSwappableAppsTask == null && !hasPreparedLocalRepo) { if (updateSwappableAppsTask == null && !hasPreparedLocalRepo) {
updateSwappableAppsTask = new PrepareFullSwapRepo(getService().getAppsToSwap()); updateSwappableAppsTask = new PrepareFullSwapRepo(getService().getAppsToSwap());
updateSwappableAppsTask.execute(); updateSwappableAppsTask.execute();
} else if (!attemptToShowNfc()) { } else {
showWifiQr(); onLocalRepoPrepared();
} }
} }
@ -355,10 +367,6 @@ public class SwapWorkflowActivity extends AppCompatActivity {
inflateInnerView(R.layout.swap_join_wifi); inflateInnerView(R.layout.swap_join_wifi);
} }
private void showBluetoothDeviceList() {
inflateInnerView(R.layout.swap_bluetooth_devices);
}
public void showWifiQr() { public void showWifiQr() {
inflateInnerView(R.layout.swap_wifi_qr); inflateInnerView(R.layout.swap_wifi_qr);
} }
@ -384,6 +392,7 @@ public class SwapWorkflowActivity extends AppCompatActivity {
} }
public void swapWith(Peer peer) { public void swapWith(Peer peer) {
getService().stopScanningForPeers();
getService().swapWith(peer); getService().swapWith(peer);
showSelectApps(); showSelectApps();
} }
@ -443,7 +452,7 @@ public class SwapWorkflowActivity extends AppCompatActivity {
/** /**
* The process for setting up bluetooth is as follows: * The process for setting up bluetooth is as follows:
* * Assume we have bluetooth available (otherwise the button which allowed us to start * * Assume we have bluetooth available (otherwise the button which allowed us to start
* the bluetooth process should not have been available). TODO: Remove button if bluetooth unavailable. * the bluetooth process should not have been available).
* * Ask user to enable (if not enabled yet). * * Ask user to enable (if not enabled yet).
* * Start bluetooth server socket. * * Start bluetooth server socket.
* * Enable bluetooth discoverability, so that people can connect to our server socket. * * Enable bluetooth discoverability, so that people can connect to our server socket.
@ -609,36 +618,53 @@ public class SwapWorkflowActivity extends AppCompatActivity {
} }
/**
* Helper class to try and make sense of what the swap workflow is currently doing.
* The more technologies are involved in the process (e.g. Bluetooth/Wifi/NFC/etc)
* the harder it becomes to reason about and debug the whole thing. Thus,this class
* will periodically dump the state to logcat so that it is easier to see when certain
* protocols are enabled/disabled.
*
* To view only this output from logcat:
*
* adb logcat | grep 'Swap Status'
*
* To exclude this output from logcat (it is very noisy):
*
* adb logcat | grep -v 'Swap Status'
*
*/
class SwapDebug { class SwapDebug {
private StringBuilder status = new StringBuilder("\n");
public void logStatus() { public void logStatus() {
append("service = " + service); String message = "";
if (service != null) { if (service == null) {
append("Swap Services:"); message = "No swap service";
append(" service.getBluetoothSwap() = " + service.getBluetoothSwap()); } else {
append(" service.getBluetoothSwap().isConnected() = " + service.getBluetoothSwap().isConnected()); {
append(" service.getWifiSwap() = " + service.getWifiSwap()); String bluetooth = service.getBluetoothSwap().isConnected() ? "Yes" : " No";
append(" service.getWifiSwap().isConnected() = " + service.getWifiSwap().isConnected()); String wifi = service.getWifiSwap().isConnected() ? "Yes" : " No";
append(" service.getWifiSwap().getBonjour() = " + service.getWifiSwap().getBonjour()); String mdns = service.getWifiSwap().getBonjour().isConnected() ? "Yes" : " No";
append(" service.getWifiSwap().getBonjour().isConnected() = " + service.getWifiSwap().getBonjour().isConnected()); message += "Broadcast { BT: " + bluetooth + ", WiFi: " + wifi + ", mDNS: " + mdns + "}, ";
append("Discovering Services:"); }
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); {
if (adapter != null) { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
String bluetooth = "N/A";
if (adapter != null) {
Map<Integer, String> scanModes = new HashMap<>(3);
scanModes.put(BluetoothAdapter.SCAN_MODE_CONNECTABLE, "CONNECTABLE");
scanModes.put(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, "CONNECTABLE_DISCOVERABLE");
scanModes.put(BluetoothAdapter.SCAN_MODE_NONE, "NONE");
bluetooth = "\"" + adapter.getName() + "\" - " + scanModes.get(adapter.getScanMode());
}
Map<Integer, String> scanModes = new HashMap<>(3); String wifi = service.getBonjourFinder().isScanning() ? "Yes" : " No";
scanModes.put(BluetoothAdapter.SCAN_MODE_CONNECTABLE, "SCAN_MODE_CONNECTABLE"); message += "Discover { BT: " + bluetooth + ", WiFi: " + wifi + "}";
scanModes.put(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, "SCAN_MODE_CONNECTABLE_DISCOVERABLE");
scanModes.put(BluetoothAdapter.SCAN_MODE_NONE, "SCAN_MODE_NONE");
append(" Bluetooth.isEnabled() = " + adapter.isEnabled());
append(" Bluetooth.isDiscovering() = " + adapter.isDiscovering());
append(" Bluetooth.getScanMode() = " + scanModes.get(adapter.getScanMode()));
} }
} }
Log.d("SwapStatus", status.toString());
Log.d("Swap Status", new Date().toLocaleString() + " " + message);
new Timer().schedule(new TimerTask() { new Timer().schedule(new TimerTask() {
@Override @Override
@ -646,13 +672,9 @@ public class SwapWorkflowActivity extends AppCompatActivity {
new SwapDebug().logStatus(); new SwapDebug().logStatus();
} }
}, },
2000 1000
); );
} }
private void append(String line) {
status.append(" ").append(line).append("\n");
}
} }
} }