diff --git a/remote-reader/app/build.gradle b/remote-reader/app/build.gradle index 004d282..eaa99e8 100644 --- a/remote-reader/app/build.gradle +++ b/remote-reader/app/build.gradle @@ -1,14 +1,14 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 23 - buildToolsVersion "23.0.1" + compileSdkVersion 29 + buildToolsVersion "23.0.3" defaultConfig { applicationId "com.vsmartcard.remotesmartcardreader.app" // NFC reader mode was added in KitKat minSdkVersion 19 - targetSdkVersion 23 + targetSdkVersion 29 versionCode 6 versionName "2.2" } @@ -38,7 +38,7 @@ android.applicationVariants.all { variant -> } else { newApkName = "${appName}-${output.baseName}-${variant.versionName}-unaligned.apk" } - output.outputFile = new File(output.outputFile.parent, newApkName) + //output.outputFile = new File(output.outputFile.parent, newApkName) } } @@ -47,12 +47,12 @@ repositories { } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - testCompile 'junit:junit:4.12' - compile 'com.android.support:appcompat-v7:23.2.1' - compile 'com.android.support:design:23.2.1' - compile 'com.android.support:support-v4:23.2.1' + implementation fileTree(dir: 'libs', include: ['*.jar']) + testImplementation 'junit:junit:4.12' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.3.0' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' - compile 'com.journeyapps:zxing-android-embedded:3.2.0@aar' - compile 'com.google.zxing:core:3.2.1' + implementation 'com.journeyapps:zxing-android-embedded:3.2.0@aar' + implementation 'com.google.zxing:core:3.2.1' } diff --git a/remote-reader/app/src/main/java/com/example/android/common/logger/LogFragment.java b/remote-reader/app/src/main/java/com/example/android/common/logger/LogFragment.java index b302acd..5695d9f 100644 --- a/remote-reader/app/src/main/java/com/example/android/common/logger/LogFragment.java +++ b/remote-reader/app/src/main/java/com/example/android/common/logger/LogFragment.java @@ -33,7 +33,6 @@ package com.example.android.common.logger; import android.graphics.Typeface; import android.os.Bundle; -import android.support.v4.app.Fragment; import android.text.Editable; import android.text.TextWatcher; import android.view.Gravity; @@ -42,6 +41,8 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ScrollView; +import androidx.fragment.app.Fragment; + /** * Simple fraggment which contains a LogView and uses is to output log data it receives * through the LogNode interface. diff --git a/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/AppCompatPreferenceActivity.java b/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/AppCompatPreferenceActivity.java index 870efc0..79e3d30 100644 --- a/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/AppCompatPreferenceActivity.java +++ b/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/AppCompatPreferenceActivity.java @@ -3,15 +3,16 @@ package com.vsmartcard.remotesmartcardreader.app; import android.content.res.Configuration; import android.os.Bundle; import android.preference.PreferenceActivity; -import android.support.annotation.LayoutRes; -import android.support.annotation.Nullable; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AppCompatDelegate; -import android.support.v7.widget.Toolbar; import android.view.MenuInflater; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.LayoutRes; +import androidx.annotation.Nullable; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatDelegate; +import androidx.appcompat.widget.Toolbar; + /** * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls * to be used with AppCompat. diff --git a/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/MainActivity.java b/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/MainActivity.java index aec7caa..c81eda1 100644 --- a/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/MainActivity.java +++ b/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/MainActivity.java @@ -31,20 +31,21 @@ import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; import android.provider.Settings; -import android.support.design.widget.FloatingActionButton; -import android.support.design.widget.Snackbar; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; + import com.example.android.common.logger.Log; import com.example.android.common.logger.LogFragment; import com.example.android.common.logger.LogWrapper; import com.example.android.common.logger.MessageOnlyLogFilter; +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.android.material.snackbar.Snackbar; import com.vsmartcard.remotesmartcardreader.app.screaders.*; @TargetApi(Build.VERSION_CODES.KITKAT) @@ -197,11 +198,12 @@ public class MainActivity extends AppCompatActivity implements NfcAdapter.Reader } private void vpcdConnect(SCReader scReader) { - SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(this); - int port = Integer.parseInt(SP.getString("port", Integer.toString(VPCDWorker.DEFAULT_PORT))); - String hostname = SP.getString("hostname", VPCDWorker.DEFAULT_HOSTNAME); + final SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(this); + final int port = Integer.parseInt(SP.getString("port", Integer.toString(VPCDWorker.DEFAULT_PORT))); + final String hostname = SP.getString("hostname", VPCDWorker.DEFAULT_HOSTNAME); + final boolean listen = SP.getBoolean("listen", VPCDWorker.DEFAULT_LISTEN); vpcdTest = new VPCDWorker(); - vpcdTest.execute(new VPCDWorker.VPCDWorkerParams(hostname, port, scReader)); + vpcdTest.execute(new VPCDWorker.VPCDWorkerParams(hostname, port, scReader, listen)); } private void vpcdDisconnect() { @@ -231,6 +233,7 @@ public class MainActivity extends AppCompatActivity implements NfcAdapter.Reader @Override public void onNewIntent(Intent intent) { // onResume gets called after this to handle the intent + super.onNewIntent(intent); setIntent(intent); } diff --git a/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/MyLogFragment.java b/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/MyLogFragment.java index 43a5222..efcfc62 100644 --- a/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/MyLogFragment.java +++ b/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/MyLogFragment.java @@ -23,12 +23,12 @@ import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.os.Bundle; -import android.support.design.widget.Snackbar; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import com.example.android.common.logger.LogFragment; +import com.google.android.material.snackbar.Snackbar; public class MyLogFragment extends LogFragment { diff --git a/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/SettingsActivity.java b/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/SettingsActivity.java index ba15b05..85b402f 100644 --- a/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/SettingsActivity.java +++ b/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/SettingsActivity.java @@ -32,11 +32,13 @@ import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceActivity; import android.provider.Settings; -import android.support.design.widget.Snackbar; -import android.support.v7.app.ActionBar; import android.preference.PreferenceFragment; import android.preference.PreferenceManager; import android.view.MenuItem; + +import androidx.appcompat.app.ActionBar; + +import com.google.android.material.snackbar.Snackbar; import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentResult; diff --git a/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/VPCDWorker.java b/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/VPCDWorker.java index 63580f5..d0e6b73 100644 --- a/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/VPCDWorker.java +++ b/remote-reader/app/src/main/java/com/vsmartcard/remotesmartcardreader/app/VPCDWorker.java @@ -20,7 +20,8 @@ package com.vsmartcard.remotesmartcardreader.app; import android.os.AsyncTask; -import android.support.annotation.Nullable; + +import androidx.annotation.Nullable; import com.example.android.common.logger.Log; import com.vsmartcard.remotesmartcardreader.app.screaders.SCReader; @@ -28,8 +29,17 @@ import com.vsmartcard.remotesmartcardreader.app.screaders.SCReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.Inet4Address; import java.net.InetAddress; +import java.net.InterfaceAddress; +import java.net.NetworkInterface; +import java.net.ServerSocket; import java.net.Socket; +import java.net.SocketTimeoutException; +import java.util.Enumeration; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; class VPCDWorker extends AsyncTask { @@ -37,18 +47,22 @@ class VPCDWorker extends AsyncTask { final String hostname; final int port; final SCReader reader; - VPCDWorkerParams(String hostname, int port, SCReader reader) { + final boolean listen; + VPCDWorkerParams(String hostname, int port, SCReader reader, boolean listen) { this.hostname = hostname; this.port = port; this.reader = reader; + this.listen = listen; } } public static final int DEFAULT_PORT = 35963; // default URI when used in emulator public static final String DEFAULT_HOSTNAME = "10.0.2.2"; + public static final boolean DEFAULT_LISTEN = false; private SCReader reader; + private ServerSocket listenSocket; private Socket socket; private InputStream inputStream; private OutputStream outputStream; @@ -74,15 +88,22 @@ class VPCDWorker extends AsyncTask { public Void doInBackground(VPCDWorkerParams... params) { try { reader = params[0].reader; - Log.i(this.getClass().getName(), "Connecting to " + params[0].hostname + ":" + Integer.toString(params[0].port) + "..."); - vpcdConnect(params[0].hostname, params[0].port); - Log.i(this.getClass().getName(), "Connected to VPCD"); + vpcdConnection(params[0]); while (!isCancelled()) { + vpcdAccept(); byte[] out = null; byte[] in = receiveFromVPCD(); - if (in == null) - break; + if (in == null) { + if (listenSocket == null) { + Log.i(this.getClass().getName(), "End of stream, finishing"); + break; + } else { + Log.i(this.getClass().getName(), "End of stream, closing connection"); + vpcdCloseClient(); + continue; // back to accept + } + } if (in.length == VPCD_CTRL_LEN) { switch (in[0]) { @@ -168,7 +189,79 @@ class VPCDWorker extends AsyncTask { outputStream.flush(); } + private void vpcdConnection(VPCDWorkerParams params) throws IOException { + if (params.listen){ + vpcdListen(params.port); + return; + } + + Log.i(this.getClass().getName(), "Connecting to " + params.hostname + ":" + Integer.toString(params.port) + "..."); + vpcdConnect(params.hostname, params.port); + Log.i(this.getClass().getName(), "Connected to VPCD"); + } + + private void vpcdListen(int port) throws IOException { + listenSocket = new ServerSocket(port); + + final List ifaceAddresses = new LinkedList<>(); + final Enumeration ifaces = NetworkInterface.getNetworkInterfaces(); + while(ifaces.hasMoreElements()){ + final NetworkInterface iface = ifaces.nextElement(); + if (!iface.isUp() || iface.isLoopback() || iface.isVirtual()) { + continue; + } + for(InterfaceAddress addr : iface.getInterfaceAddresses()){ + final InetAddress inetAddr = addr.getAddress(); + ifaceAddresses.add(inetAddr.getHostAddress()); + } + } + + Log.i(this.getClass().getName(), "Listening on port " + port + ". Local addresses: " + join(", ", ifaceAddresses)); + } + + private void vpcdAccept() throws IOException { + if(listenSocket == null){ + return; + } + + if (socket != null){ + return; // Already accepted, only one client allowed + } + + Log.i(this.getClass().getName(),"Waiting for connections..."); + while(!isCancelled()) { + listenSocket.setSoTimeout(1000); + try { + socket = listenSocket.accept(); + } catch (SocketTimeoutException ignored){} + if (socket != null){ + break; + } + } + + Log.i(this.getClass().getName(),"Connected, " + socket.getInetAddress()); + listenSocket.setSoTimeout(0); + outputStream = socket.getOutputStream(); + inputStream = socket.getInputStream(); + } + + private void vpcdCloseClient(){ + try { + outputStream.close(); + } catch (IOException ignored) { } + try { + inputStream.close(); + } catch (IOException ignored) { } + try { + socket.close(); + } catch (IOException ignored) { } + outputStream = null; + inputStream = null; + socket = null; + } + private void vpcdConnect(String hostname, int port) throws IOException { + listenSocket = null; socket = new Socket(InetAddress.getByName(hostname), port); outputStream = socket.getOutputStream(); inputStream = socket.getInputStream(); @@ -182,5 +275,25 @@ class VPCDWorker extends AsyncTask { socket.close(); Log.i(this.getClass().getName(), "Disconnected from VPCD"); } + if (listenSocket != null) { + Log.i(this.getClass().getName(), "Closing listening socket"); + listenSocket.close(); + } + } + + /** + * Usage of API level 24+ would allow streams(), join can be removed. + */ + private static String join(String separator, List input) { + if (input == null || input.size() <= 0) return ""; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < input.size(); i++) { + sb.append(input.get(i)); + if (i != input.size() - 1) { + sb.append(separator); + } + } + return sb.toString(); + } } \ No newline at end of file diff --git a/remote-reader/app/src/main/res/layout/activity_main.xml b/remote-reader/app/src/main/res/layout/activity_main.xml index 26d7d00..d0ecb38 100644 --- a/remote-reader/app/src/main/res/layout/activity_main.xml +++ b/remote-reader/app/src/main/res/layout/activity_main.xml @@ -1,5 +1,5 @@ - - - - - - + + - - + diff --git a/remote-reader/app/src/main/res/layout/content_main.xml b/remote-reader/app/src/main/res/layout/content_main.xml index 4d4bdc7..a917d34 100644 --- a/remote-reader/app/src/main/res/layout/content_main.xml +++ b/remote-reader/app/src/main/res/layout/content_main.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/remote-reader/app/src/main/res/values/strings.xml b/remote-reader/app/src/main/res/values/strings.xml index 6ff39ac..dc2e46f 100644 --- a/remote-reader/app/src/main/res/values/strings.xml +++ b/remote-reader/app/src/main/res/values/strings.xml @@ -13,6 +13,9 @@ 500 milliseconds 1 second 2 seconds + 4 seconds + 10 seconds + 30 seconds 125 @@ -20,6 +23,9 @@ 500 1000 2000 + 4000 + 10000 + 30000 125 milliseconds @@ -29,6 +35,8 @@ 2 seconds 3 seconds 4 seconds + 10 seconds + 30 seconds 125 @@ -38,6 +46,8 @@ 2000 3000 4000 + 10000 + 30000 Settings @@ -45,5 +55,6 @@ Clear Log Settings + Listen for connections diff --git a/remote-reader/app/src/main/res/xml/settings.xml b/remote-reader/app/src/main/res/xml/settings.xml index 41c6e0a..5d72274 100644 --- a/remote-reader/app/src/main/res/xml/settings.xml +++ b/remote-reader/app/src/main/res/xml/settings.xml @@ -32,6 +32,12 @@ android:inputType="number" android:hint="Default: 35963" /> + +