From f7bd06e040f90f445d9081c2b0ec263757738d0e Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 13 Jul 2018 12:03:02 +0300 Subject: [PATCH] Updated on 2026-08-14 --- app/build.gradle | 4 +- .../request/VerificationServerProtocol.java | 193 ++++++++++++++++++ .../network/task/VerificationServerTask.java | 42 ++++ .../com/tangem/data/nfc/ReadCardInfoTask.java | 22 +- .../tangem/domain/cardReader/NfcManager.java | 22 ++ .../tangem/presentation/fragment/Main.java | 37 ++-- 6 files changed, 297 insertions(+), 23 deletions(-) create mode 100644 app/src/main/java/com/tangem/data/network/request/VerificationServerProtocol.java create mode 100644 app/src/main/java/com/tangem/data/network/task/VerificationServerTask.java diff --git a/app/build.gradle b/app/build.gradle index d7ffe39557..18afdc7471 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,7 +19,6 @@ android { versionName "0.85.5." + generateVersionName() testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } - buildTypes { release { debuggable false @@ -34,11 +33,11 @@ android { signingConfig signingConfigs.debug } } - compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + buildToolsVersion '27.0.3' } dependencies { @@ -61,6 +60,7 @@ dependencies { implementation 'org.bitcoinj:bitcoinj-core:0.14.4' implementation 'me.dm7.barcodescanner:zxing:1.9.8' implementation 'info.hoang8f:android-segmented:1.0.6' + implementation 'com.google.code.gson:gson:2.8.5' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' diff --git a/app/src/main/java/com/tangem/data/network/request/VerificationServerProtocol.java b/app/src/main/java/com/tangem/data/network/request/VerificationServerProtocol.java new file mode 100644 index 0000000000..0b008ac56d --- /dev/null +++ b/app/src/main/java/com/tangem/data/network/request/VerificationServerProtocol.java @@ -0,0 +1,193 @@ +package com.tangem.data.network.request; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.tangem.domain.wallet.TangemCard; +import com.tangem.util.Util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.lang.reflect.Type; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.sql.Date; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Locale; + +public class VerificationServerProtocol { + + public static class Request + { + public Request(CustomCommand command, Class answerClass) + { + this.command=command; + this.answerClass=answerClass; + } + public String error; + public CustomCommand command; + public CustomAnswer answer; + public Type answerClass; + + public void doPost(String hostURL) throws IOException { + URL url = new URL(hostURL+"/"+command.getURL()); + URLConnection con = url.openConnection(); + HttpURLConnection http = (HttpURLConnection) con; + try { + http.setConnectTimeout(30000); + http.setReadTimeout(30000); + http.setRequestMethod("POST"); // PUT is another valid option + http.setDoOutput(true); + + String sRequestBody = getGson().toJson(this.command); + + byte[] out = sRequestBody.getBytes(StandardCharsets.UTF_8); + int length = out.length; + + http.setFixedLengthStreamingMode(length); + http.setRequestProperty("Content-Type", "application/json"); + http.connect(); + try (OutputStream os = http.getOutputStream()) { + os.write(out); + } + try (InputStream is = http.getInputStream()) { + try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { + answer = getGson().fromJson(br, answerClass); + } + } + } + finally { + http.disconnect(); + } + } + } + + static abstract class CustomCommand { + public abstract String getURL(); + + public byte[] toBytes() + { + return getGson().toJson(this).getBytes(StandardCharsets.UTF_8); + } + + @Override + public String toString() { + return getGson().toJson(this); + } + + } + + static class CustomAnswer { + public String error; + + @Override + public String toString() { + return getGson().toJson(this); + } + } + + public static class Verify { + + static class RequestItem { + public String CID; + public String publicKey; + } + + static class Command extends CustomCommand { + public RequestItem[] requests; + + @Override + public String getURL() { + return "verify"; + } + } + + public static class ResultItem { + public ResultItem(RequestItem request) { + CID = request.CID; + } + + public String error; + public String CID; + public Boolean passed; + } + + public static class Answer extends CustomAnswer { + public ResultItem[] results; + } + + public static Request prepare(TangemCard card) + { + Command c=new Command(); + c.requests=new RequestItem[1]; + c.requests[0]=new RequestItem(); + c.requests[0].CID=Util.bytesToHex(card.getCID()); + c.requests[0].publicKey=Util.bytesToHex(card.getCardPublicKey()); + + return new Request(c,Answer.class); + } + } + + public static class Validate { + + static class RequestItem { + public String CID; + public int counter; + public String signature; + } + + static class Command extends CustomCommand { + public RequestItem[] requests; + + @Override + public String getURL() { + return "validate"; + } + } + + static class ResultItem { + public ResultItem(RequestItem request) { + CID = request.CID; + } + public String error; + public String CID; + public Integer previousCounter; + public Boolean passed; + } + + static class Answer extends CustomAnswer { + public ResultItem[] results; + } + + public Request prepare(TangemCard card, int ValidationCounter, byte[] ValidationSignature) + { + Command c=new Command(); + c.requests=new RequestItem[1]; + c.requests[0].CID= Util.bytesToHex(card.getCID()); + c.requests[0].counter=ValidationCounter; + c.requests[0].signature=Util.bytesToHex(ValidationSignature); + return new Request(new Command(),Answer.class); + } + } + + + public static Date strToDate(String date) throws ParseException { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US); + return new Date(formatter.parse(date).getTime()); + } + + public static String dateToStr(Date date) { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US); + return formatter.format(date); + } + + public static Gson getGson() { + return new GsonBuilder().create(); + } + +} diff --git a/app/src/main/java/com/tangem/data/network/task/VerificationServerTask.java b/app/src/main/java/com/tangem/data/network/task/VerificationServerTask.java new file mode 100644 index 0000000000..6e83e322ac --- /dev/null +++ b/app/src/main/java/com/tangem/data/network/task/VerificationServerTask.java @@ -0,0 +1,42 @@ +package com.tangem.data.network.task; + +import android.os.AsyncTask; + +import com.tangem.data.network.request.VerificationServerProtocol; + +import java.util.ArrayList; +import java.util.List; + +public class VerificationServerTask extends AsyncTask> { + public static final String hostURL ="https://tangem-webapp.appspot.com"; + + public VerificationServerTask() { + + } + + protected List doInBackground(VerificationServerProtocol.Request... requests) { + List result = new ArrayList<>(); + for (int i = 0; i < requests.length; i++) { + result.add(requests[i]); + } + + for (VerificationServerProtocol.Request request : result) { + try { + request.doPost(hostURL); + } catch (Exception e) { + request.error = e.getMessage(); + if( request.error==null || request.error.isEmpty() ) + { + request.error = e.getClass().getName(); + } + } + } + + return result; + } + + public String getValidationNodeDescription() { + return hostURL; + } + + } diff --git a/app/src/main/java/com/tangem/data/nfc/ReadCardInfoTask.java b/app/src/main/java/com/tangem/data/nfc/ReadCardInfoTask.java index ce7e62dbac..f58b7d99d3 100644 --- a/app/src/main/java/com/tangem/data/nfc/ReadCardInfoTask.java +++ b/app/src/main/java/com/tangem/data/nfc/ReadCardInfoTask.java @@ -21,16 +21,23 @@ public class ReadCardInfoTask extends Thread { private Context mContext; private NfcManager mNfcManager; - private ArrayList lastRead_UnsuccessfullPINs = new ArrayList<>(); - private TangemCard.EncryptionMode lastRead_Encryption = null; - private String lastRead_UID; + // this fields are static to optimize process when need enter pin and scan card again + private static ArrayList lastRead_UnsuccessfullPINs = new ArrayList<>(); + private static TangemCard.EncryptionMode lastRead_Encryption = null; + private static String lastRead_UID; - public ReadCardInfoTask(Context context, NfcManager nfcManager, String lastRead_UID, IsoDep isoDep, CardProtocol.Notifications notifications) { + public static void resetLastReadInfo() { + lastRead_UID=""; + lastRead_Encryption=null; + lastRead_UnsuccessfullPINs.clear(); + } + + public ReadCardInfoTask(Context context, NfcManager nfcManager, IsoDep isoDep, CardProtocol.Notifications notifications) { mContext = context; mIsoDep = isoDep; mNotifications = notifications; mNfcManager = nfcManager; - this.lastRead_UID = lastRead_UID; +// ReadCardInfoTask.lastRead_UID = lastRead_UID; } @Override @@ -56,9 +63,7 @@ public class ReadCardInfoTask extends Thread { byte[] UID = mIsoDep.getTag().getId(); String sUID = Util.byteArrayToHexString(UID); if (!lastRead_UID.equals(sUID)) { - lastRead_UID = sUID; - lastRead_UnsuccessfullPINs.clear(); - lastRead_Encryption = null; + resetLastReadInfo(); } Log.i(TAG, "[-- Start read card info --]"); @@ -146,6 +151,7 @@ public class ReadCardInfoTask extends Thread { } } catch (Exception e) { e.printStackTrace(); + mNfcManager.notifyReadResult(false); } } diff --git a/app/src/main/java/com/tangem/domain/cardReader/NfcManager.java b/app/src/main/java/com/tangem/domain/cardReader/NfcManager.java index 9d796d76dc..941b1cff92 100644 --- a/app/src/main/java/com/tangem/domain/cardReader/NfcManager.java +++ b/app/src/main/java/com/tangem/domain/cardReader/NfcManager.java @@ -15,6 +15,7 @@ import android.os.Build; import android.os.Bundle; import android.support.v4.app.ActivityCompat; import android.util.Log; +import android.widget.Toast; import com.tangem.presentation.dialog.NFCEnableDialog; @@ -76,6 +77,27 @@ public class NfcManager { // } } + int errorCount=0; + public void notifyReadResult(boolean success) + { + if(success) + { + errorCount=0; + }else{ + errorCount++; + } + if( errorCount>=3 ) + { + disableReaderMode(); + mNfcAdapter=null; + Toast.makeText(mActivity,"NFC restarted!",Toast.LENGTH_SHORT).show(); + mActivity.runOnUiThread(()->{ + mNfcAdapter = NfcAdapter.getDefaultAdapter(mActivity); + enableReaderMode(); + }); + } + } + private void showNFCEnableDialog() { mEnableNfcDialog = new NFCEnableDialog(); mEnableNfcDialog.show(mActivity.getFragmentManager(), NFCEnableDialog.TAG); diff --git a/app/src/main/java/com/tangem/presentation/fragment/Main.java b/app/src/main/java/com/tangem/presentation/fragment/Main.java index 8b0b879adf..f8d8939c7f 100644 --- a/app/src/main/java/com/tangem/presentation/fragment/Main.java +++ b/app/src/main/java/com/tangem/presentation/fragment/Main.java @@ -76,7 +76,7 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis private int unsuccessReadCount = 0; private Tag lastTag = null; - private String lastRead_UID = ""; +// private String lastRead_UID = ""; public Main() { } @@ -119,9 +119,9 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis int cardIndex = viewHolder.getAdapterPosition(); if (cardIndex < 0 || cardIndex >= mCardListAdapter.getItemCount()) return; slCardUIDs.remove(mCardListAdapter.getCard(cardIndex).getUID()); - if (mCardListAdapter.getCard(cardIndex).getUID() == lastRead_UID) { - lastRead_UID = ""; - } +// if (mCardListAdapter.getCard(cardIndex).getUID() == lastRead_UID) { +// lastRead_UID = ""; +// } mCardListAdapter.removeCard(cardIndex); if (mCardListAdapter.getItemCount() == 0 && getActivity().getClass() == MainActivity.class) { ((MainActivity) getActivity()).hideCleanButton(); @@ -142,6 +142,7 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis @Override public void onResume() { super.onResume(); + ReadCardInfoTask.resetLastReadInfo(); mNfcManager.onResume(); } @@ -201,8 +202,13 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis } } - } else if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_ENTER_PIN_ACTIVITY) { - if (lastTag != null) onTagDiscovered(lastTag); + } else if (requestCode == REQUEST_CODE_ENTER_PIN_ACTIVITY) { + if( resultCode == Activity.RESULT_OK && lastTag != null) + { + onTagDiscovered(lastTag); + }else{ + ReadCardInfoTask.resetLastReadInfo(); + } } } @@ -233,13 +239,14 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis } lastTag = tag; - readCardInfoTask = new ReadCardInfoTask(getActivity(), mNfcManager, lastRead_UID, isoDep, this); + readCardInfoTask = new ReadCardInfoTask(getActivity(), mNfcManager, isoDep, this); readCardInfoTask.start(); Log.i(TAG, "onTagDiscovered " + Arrays.toString(tag.getId())); } catch (Exception e) { e.printStackTrace(); + mNfcManager.notifyReadResult(false); } } @@ -290,6 +297,7 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis readCardInfoTask = null; if (cardProtocol != null) { if (cardProtocol.getError() == null) { + mNfcManager.notifyReadResult(true); progressBar.post(() -> { rlProgressBar.setVisibility(View.GONE); progressBar.setProgress(100); @@ -334,7 +342,6 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis for (RequestWalletInfoTask rt : requestTasks) { rt.cancel(true); } - lastRead_UID = ""; }); @@ -349,12 +356,15 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis slCardUIDs.remove(cardProtocol.getCard().getUID()); if (cardProtocol.getError() instanceof CardProtocol.TangemException_InvalidPIN) { doEnterPIN(); - } else if (cardProtocol.getError() instanceof CardProtocol.TangemException_ExtendedLengthNotSupported) { - if (!NoExtendedLengthSupportDialog.allreadyShowed) { - new NoExtendedLengthSupportDialog().show(Objects.requireNonNull(getActivity()).getFragmentManager(), NoExtendedLengthSupportDialog.TAG); - } } else { + if (cardProtocol.getError() instanceof CardProtocol.TangemException_ExtendedLengthNotSupported) { + if (!NoExtendedLengthSupportDialog.allreadyShowed) { + new NoExtendedLengthSupportDialog().show(Objects.requireNonNull(getActivity()).getFragmentManager(), NoExtendedLengthSupportDialog.TAG); + } + } lastTag = null; + ReadCardInfoTask.resetLastReadInfo(); + mNfcManager.notifyReadResult(false); } } }); @@ -376,6 +386,7 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis public void OnReadCancel() { readCardInfoTask = null; + ReadCardInfoTask.resetLastReadInfo(); progressBar.postDelayed(() -> { try { rlProgressBar.setVisibility(View.GONE); @@ -410,7 +421,7 @@ public class Main extends Fragment implements NfcAdapter.ReaderCallback, CardLis for (RequestWalletInfoTask rt : requestTasks) { rt.cancel(true); } - lastRead_UID = ""; + ReadCardInfoTask.resetLastReadInfo(); } public void refreshCard(TangemCard card) {