From e7f233db5b9499fdb8b7bbc779006129d0daeb38 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Wed, 3 Mar 2021 16:03:49 -0500 Subject: [PATCH] Add Device Transfer via WiFi Direct groundwork. --- .../java/org/signal/core/util/ThreadUtil.java | 6 + device-transfer/app/build.gradle | 30 ++ .../app/src/main/AndroidManifest.xml | 22 + .../devicetransfer/app/MainActivity.java | 152 +++++++ .../drawable-v24/ic_launcher_foreground.xml | 31 ++ .../res/drawable/ic_launcher_background.xml | 171 +++++++ .../src/main/res/drawable/ic_refresh_20.xml | 9 + .../app/src/main/res/layout/activity_main.xml | 58 +++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 6 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3593 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 5339 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2636 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 3388 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4926 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 7472 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 7909 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 11873 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 10652 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 16570 bytes .../app/src/main/res/values-night/themes.xml | 16 + .../app/src/main/res/values/colors.xml | 10 + .../app/src/main/res/values/strings.xml | 3 + .../app/src/main/res/values/themes.xml | 16 + device-transfer/lib/build.gradle | 30 ++ .../lib/src/main/AndroidManifest.xml | 20 + .../org/signal/devicetransfer/ClientTask.java | 22 + .../DeviceToDeviceTransferService.java | 248 +++++++++++ .../devicetransfer/DeviceTransferClient.java | 339 ++++++++++++++ .../devicetransfer/DeviceTransferServer.java | 290 ++++++++++++ .../org/signal/devicetransfer/IpExchange.java | 148 +++++++ .../org/signal/devicetransfer/ServerTask.java | 22 + .../devicetransfer/ShutdownCallback.java | 10 + .../signal/devicetransfer/TransferMode.java | 12 + .../org/signal/devicetransfer/WifiDirect.java | 416 ++++++++++++++++++ .../WifiDirectUnavailableException.java | 29 ++ .../lib/witness-verifications.gradle | 105 +++++ settings.gradle | 5 + 38 files changed, 2232 insertions(+) create mode 100644 device-transfer/app/build.gradle create mode 100644 device-transfer/app/src/main/AndroidManifest.xml create mode 100644 device-transfer/app/src/main/java/org/signal/devicetransfer/app/MainActivity.java create mode 100644 device-transfer/app/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 device-transfer/app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 device-transfer/app/src/main/res/drawable/ic_refresh_20.xml create mode 100644 device-transfer/app/src/main/res/layout/activity_main.xml create mode 100644 device-transfer/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 device-transfer/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 device-transfer/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 device-transfer/app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 device-transfer/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 device-transfer/app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 device-transfer/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 device-transfer/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 device-transfer/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 device-transfer/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 device-transfer/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 device-transfer/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 device-transfer/app/src/main/res/values-night/themes.xml create mode 100644 device-transfer/app/src/main/res/values/colors.xml create mode 100644 device-transfer/app/src/main/res/values/strings.xml create mode 100644 device-transfer/app/src/main/res/values/themes.xml create mode 100644 device-transfer/lib/build.gradle create mode 100644 device-transfer/lib/src/main/AndroidManifest.xml create mode 100644 device-transfer/lib/src/main/java/org/signal/devicetransfer/ClientTask.java create mode 100644 device-transfer/lib/src/main/java/org/signal/devicetransfer/DeviceToDeviceTransferService.java create mode 100644 device-transfer/lib/src/main/java/org/signal/devicetransfer/DeviceTransferClient.java create mode 100644 device-transfer/lib/src/main/java/org/signal/devicetransfer/DeviceTransferServer.java create mode 100644 device-transfer/lib/src/main/java/org/signal/devicetransfer/IpExchange.java create mode 100644 device-transfer/lib/src/main/java/org/signal/devicetransfer/ServerTask.java create mode 100644 device-transfer/lib/src/main/java/org/signal/devicetransfer/ShutdownCallback.java create mode 100644 device-transfer/lib/src/main/java/org/signal/devicetransfer/TransferMode.java create mode 100644 device-transfer/lib/src/main/java/org/signal/devicetransfer/WifiDirect.java create mode 100644 device-transfer/lib/src/main/java/org/signal/devicetransfer/WifiDirectUnavailableException.java create mode 100644 device-transfer/lib/witness-verifications.gradle diff --git a/core-util/src/main/java/org/signal/core/util/ThreadUtil.java b/core-util/src/main/java/org/signal/core/util/ThreadUtil.java index 54d68ba432..7c2b02d39b 100644 --- a/core-util/src/main/java/org/signal/core/util/ThreadUtil.java +++ b/core-util/src/main/java/org/signal/core/util/ThreadUtil.java @@ -87,4 +87,10 @@ public final class ThreadUtil { throw new AssertionError(e); } } + + public static void interruptableSleep(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException ignored) { } + } } diff --git a/device-transfer/app/build.gradle b/device-transfer/app/build.gradle new file mode 100644 index 0000000000..6bbc6ff010 --- /dev/null +++ b/device-transfer/app/build.gradle @@ -0,0 +1,30 @@ +apply plugin: 'com.android.application' + +android { + buildToolsVersion BUILD_TOOL_VERSION + compileSdkVersion COMPILE_SDK + + defaultConfig { + applicationId "org.signal.devicetransfer.app" + versionCode 1 + versionName "1.0" + + minSdkVersion MINIMUM_SDK + targetSdkVersion TARGET_SDK + } + + compileOptions { + sourceCompatibility JAVA_VERSION + targetCompatibility JAVA_VERSION + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.2.1' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + + testImplementation 'junit:junit:4.12' + + implementation project(':device-transfer') +} diff --git a/device-transfer/app/src/main/AndroidManifest.xml b/device-transfer/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..f52af0ad3d --- /dev/null +++ b/device-transfer/app/src/main/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + diff --git a/device-transfer/app/src/main/java/org/signal/devicetransfer/app/MainActivity.java b/device-transfer/app/src/main/java/org/signal/devicetransfer/app/MainActivity.java new file mode 100644 index 0000000000..2f19ea0c3e --- /dev/null +++ b/device-transfer/app/src/main/java/org/signal/devicetransfer/app/MainActivity.java @@ -0,0 +1,152 @@ +package org.signal.devicetransfer.app; + +import android.Manifest; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.NotificationManagerCompat; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; +import org.signal.devicetransfer.ClientTask; +import org.signal.devicetransfer.DeviceToDeviceTransferService; +import org.signal.devicetransfer.DeviceToDeviceTransferService.TransferNotificationData; +import org.signal.devicetransfer.ServerTask; +import org.signal.devicetransfer.TransferMode; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Random; + +public class MainActivity extends AppCompatActivity { + + private static final String TRANSFER_NOTIFICATION_CHANNEL = "DEVICE_TO_DEVICE_TRANSFER"; + + private LinearLayout list; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + if (Build.VERSION.SDK_INT > 26) { + NotificationChannel deviceTransfer = new NotificationChannel(TRANSFER_NOTIFICATION_CHANNEL, "Device Transfer", NotificationManager.IMPORTANCE_DEFAULT); + NotificationManagerCompat.from(this).createNotificationChannel(deviceTransfer); + } + + list = findViewById(R.id.list); + + final TransferNotificationData data = new TransferNotificationData(1337, + TRANSFER_NOTIFICATION_CHANNEL, + R.drawable.ic_refresh_20); + + findViewById(R.id.start_server).setOnClickListener(v -> { + DeviceToDeviceTransferService.startServer(this, + 8888, + new ServerReceiveRandomBytes(), + data, + PendingIntent.getActivity(this, + 0, + new Intent(this, MainActivity.class), + 0)); + + list.removeAllViews(); + }); + + findViewById(R.id.start_client).setOnClickListener(v -> { + DeviceToDeviceTransferService.startClient(this, + 8888, + new ClientSendRandomBytes(), + data, + PendingIntent.getActivity(this, + 0, + new Intent(this, MainActivity.class), + 0)); + + list.removeAllViews(); + }); + + findViewById(R.id.stop).setOnClickListener(v -> { + DeviceToDeviceTransferService.stop(this); + }); + + findViewById(R.id.enable_permission).setOnClickListener(v -> { + if (Build.VERSION.SDK_INT >= 23 && checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 420); + } + }); + + EventBus.getDefault().register(this); + } + + @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) + public void onEventMainThread(@NonNull TransferMode event) { + TextView text = new TextView(this); + text.setText(event.toString()); + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + text.setLayoutParams(params); + list.addView(text); + } + + private static class ClientSendRandomBytes implements ClientTask { + + private static final String TAG = "ClientSend"; + + private final int rounds = 1000; + + @Override + public void run(@NonNull Context context, @NonNull OutputStream outputStream) throws IOException { + Random r = new Random(System.currentTimeMillis()); + byte[] data = new byte[8192]; + r.nextBytes(data); + + long start = System.currentTimeMillis(); + Log.i(TAG, "Sending " + ((data.length * rounds) / 1024 / 1024) + "MB of random data!!!"); + for (int i = 0; i < rounds; i++) { + outputStream.write(data); + outputStream.flush(); + } + long end = System.currentTimeMillis(); + Log.i(TAG, "Sending took: " + (end - start)); + } + } + + private static class ServerReceiveRandomBytes implements ServerTask { + + private static final String TAG = "ServerReceive"; + + @Override + public void run(@NonNull Context context, @NonNull InputStream inputStream) throws IOException { + long start = System.currentTimeMillis(); + byte[] data = new byte[8192]; + int result = 0; + + int i = 0; + Log.i(TAG, "Start drinking from the fire hose!"); + while (result >= 0) { + result = inputStream.read(data, 0, 8192); + i++; + if (i % 10000 == 0) { + Log.i(TAG, "Round: " + i); + } + } + long end = System.currentTimeMillis(); + Log.i(TAG, "Receive took: " + (end - start)); + } + } +} diff --git a/device-transfer/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/device-transfer/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000000..18c0565852 --- /dev/null +++ b/device-transfer/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/device-transfer/app/src/main/res/drawable/ic_launcher_background.xml b/device-transfer/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000000..c71b77f4d2 --- /dev/null +++ b/device-transfer/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/device-transfer/app/src/main/res/drawable/ic_refresh_20.xml b/device-transfer/app/src/main/res/drawable/ic_refresh_20.xml new file mode 100644 index 0000000000..915e0bbbca --- /dev/null +++ b/device-transfer/app/src/main/res/drawable/ic_refresh_20.xml @@ -0,0 +1,9 @@ + + + diff --git a/device-transfer/app/src/main/res/layout/activity_main.xml b/device-transfer/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000000..3a767e8ba7 --- /dev/null +++ b/device-transfer/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,58 @@ + + + + +