From f8ea3cc77fe25204e562ec954008e78449bfa86c Mon Sep 17 00:00:00 2001 From: m2049r <30435443+m2049r@users.noreply.github.com> Date: Mon, 4 Sep 2017 20:43:05 +0200 Subject: [PATCH] use AsyncTask with 5MB stack in lots of places --- .../xmrwallet/GenerateReviewFragment.java | 115 +++++++++++------- .../com/m2049r/xmrwallet/LoginActivity.java | 7 +- .../com/m2049r/xmrwallet/ReceiveFragment.java | 72 ++++++----- .../com/m2049r/xmrwallet/WalletActivity.java | 6 +- .../util/MoneroThreadPoolExecutor.java | 56 +++++++++ app/src/main/res/values/strings.xml | 1 + 6 files changed, 173 insertions(+), 84 deletions(-) create mode 100644 app/src/main/java/com/m2049r/xmrwallet/util/MoneroThreadPoolExecutor.java diff --git a/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java b/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java index f2624bc0..0bd24e21 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/GenerateReviewFragment.java @@ -17,8 +17,10 @@ package com.m2049r.xmrwallet; import android.content.Context; +import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.Fragment; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -28,15 +30,13 @@ import android.widget.TextView; import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.WalletManager; -import com.m2049r.xmrwallet.service.MoneroHandlerThread; - -import java.io.File; +import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor; public class GenerateReviewFragment extends Fragment { static final String TAG = "GenerateReviewFragment"; - static final public String VIEW_DETAILS = "details"; - static final public String VIEW_ACCEPT = "accept"; - static final public String VIEW_WALLET = "wallet"; + static final public String VIEW_TYPE_DETAILS = "details"; + static final public String VIEW_TYPE_ACCEPT = "accept"; + static final public String VIEW_TYPE_WALLET = "wallet"; ProgressBar pbProgress; TextView tvWalletName; @@ -76,16 +76,12 @@ public class GenerateReviewFragment extends Fragment { showProgress(); - Bundle b = getArguments(); - String type = b.getString("type"); - if (!type.equals(VIEW_WALLET)) { - String path = b.getString("path"); - String password = b.getString("password"); - tvWalletName.setText(new File(path).getName()); - show(path, password, type); - } else { - show(walletCallback.getWallet(), null, type); - } + Bundle args = getArguments(); + String path = args.getString("path"); + String password = args.getString("password"); + String type = args.getString("type"); + new AsyncShow().executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR, + path, password, type); return view; } @@ -96,40 +92,64 @@ public class GenerateReviewFragment extends Fragment { acceptCallback.onAccept(name, password); } - private void show(final String walletPath, final String password, final String type) { - new Thread(null, - new Runnable() { - @Override - public void run() { - final Wallet wallet = WalletManager.getInstance().openWallet(walletPath, password); - getActivity().runOnUiThread(new Runnable() { - public void run() { - show(wallet, password, type); - wallet.close(); - } - }); - } - } - , "DetailsReview", MoneroHandlerThread.THREAD_STACK_SIZE).start(); - } + private class AsyncShow extends AsyncTask { + String type; + String password; - private void show(final Wallet wallet, final String password, final String type) { - if (type.equals(GenerateReviewFragment.VIEW_ACCEPT)) { - tvWalletPassword.setText(password); - bAccept.setVisibility(View.VISIBLE); - bAccept.setEnabled(true); + String name; + String address; + String seed; + String viewKey; + boolean isWatchOnly; + + @Override + protected Boolean doInBackground(String... params) { + if (params.length != 3) return false; + String walletPath = params[0]; + password = params[1]; + type = params[2]; + + Wallet wallet; + boolean closeWallet; + if (type.equals(GenerateReviewFragment.VIEW_TYPE_WALLET)) { + wallet = GenerateReviewFragment.this.walletCallback.getWallet(); + closeWallet = false; + } else { + wallet = WalletManager.getInstance().openWallet(walletPath, password); + closeWallet = true; + } + if (wallet.getStatus() != Wallet.Status.Status_Ok) return false; + name = wallet.getName(); + address = wallet.getAddress(); + seed = wallet.getSeed(); + viewKey = wallet.getSecretViewKey(); + isWatchOnly = wallet.isWatchOnly(); + if (closeWallet) wallet.close(); + return true; } - tvWalletName.setText(wallet.getName()); - tvWalletAddress.setText(wallet.getAddress()); - tvWalletMnemonic.setText(wallet.getSeed()); - tvWalletViewKey.setText(wallet.getSecretViewKey()); - String spend = wallet.isWatchOnly() ? "" : "not available - use seed for recovery"; - if (spend.length() > 0) { //TODO should be == 64, but spendkey is not in the API yet - tvWalletSpendKey.setText(spend); - } else { - tvWalletSpendKey.setText(getString(R.string.generate_wallet_watchonly)); + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + if (result) { + if (type.equals(GenerateReviewFragment.VIEW_TYPE_ACCEPT)) { + tvWalletPassword.setText(password); + bAccept.setVisibility(View.VISIBLE); + bAccept.setEnabled(true); + } + tvWalletName.setText(name); + tvWalletAddress.setText(address); + tvWalletMnemonic.setText(seed); + tvWalletViewKey.setText(viewKey); + String spend = isWatchOnly ? "" : "not available - use seed for recovery"; + if (spend.length() > 0) { //TODO should be == 64, but spendkey is not in the API yet + tvWalletSpendKey.setText(spend); + } else { + tvWalletSpendKey.setText(getString(R.string.generate_wallet_watchonly)); + } + } + hideProgress(); } - hideProgress(); } GenerateReviewFragment.Listener acceptCallback = null; @@ -141,6 +161,7 @@ public class GenerateReviewFragment extends Fragment { public interface ListenerWithWallet { Wallet getWallet(); + } @Override diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java index 9c02f9cd..8ed4a0a8 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java @@ -121,7 +121,7 @@ public class LoginActivity extends AppCompatActivity promptPassword(walletName, new PasswordAction() { @Override public void action(String walletName, String password) { - startDetails(walletFile, password, GenerateReviewFragment.VIEW_DETAILS); + startDetails(walletFile, password, GenerateReviewFragment.VIEW_TYPE_DETAILS); } }); } else { // this cannot really happen as we prefilter choices @@ -646,6 +646,7 @@ public class LoginActivity extends AppCompatActivity ////////////////////////////////////////// static final String MNEMONIC_LANGUAGE = "English"; // see mnemonics/electrum-words.cpp for more + // TODO make this an AsyncTask? public void createWallet(final String name, final String password, final WalletCreator walletCreator) { final GenerateFragment genFragment = (GenerateFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_container); @@ -666,7 +667,7 @@ public class LoginActivity extends AppCompatActivity cacheFile.delete(); File keysFile = new File(newWalletFolder, name + ".keys"); keysFile.delete(); - final File addressFile = new File(newWalletFolder, name + ".address.txt"); + File addressFile = new File(newWalletFolder, name + ".address.txt"); addressFile.delete(); if (cacheFile.exists() || keysFile.exists() || addressFile.exists()) { @@ -678,7 +679,7 @@ public class LoginActivity extends AppCompatActivity File newWalletFile = new File(newWalletFolder, name); boolean success = walletCreator.createWallet(newWalletFile, password); if (success) { - startDetails(newWalletFile, password, GenerateReviewFragment.VIEW_ACCEPT); + startDetails(newWalletFile, password, GenerateReviewFragment.VIEW_TYPE_ACCEPT); } else { Toast.makeText(LoginActivity.this, getString(R.string.generate_wallet_create_failed), Toast.LENGTH_LONG).show(); diff --git a/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java b/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java index fe138529..efceac14 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/ReceiveFragment.java @@ -18,6 +18,7 @@ package com.m2049r.xmrwallet; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.Fragment; import android.text.Editable; @@ -34,6 +35,7 @@ import android.widget.EditText; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; +import android.widget.Toast; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; @@ -43,8 +45,8 @@ import com.google.zxing.qrcode.QRCodeWriter; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.WalletManager; -import com.m2049r.xmrwallet.service.MoneroHandlerThread; import com.m2049r.xmrwallet.util.Helper; +import com.m2049r.xmrwallet.util.MoneroThreadPoolExecutor; import java.util.HashMap; import java.util.Map; @@ -187,38 +189,50 @@ public class ReceiveFragment extends Fragment { } } - private void show(final String address) { - getActivity().runOnUiThread(new Runnable() { - public void run() { - tvAddress.setText(address); - etPaymentId.setEnabled(true); - etAmount.setEnabled(true); - bPaymentId.setEnabled(true); - bGenerate.setEnabled(true); - hideProgress(); - generateQr(); - } - }); + private void show(String address) { + tvAddress.setText(address); + etPaymentId.setEnabled(true); + etAmount.setEnabled(true); + bPaymentId.setEnabled(true); + bGenerate.setEnabled(true); + hideProgress(); + generateQr(); } - private void show(final String walletPath, final String password) { - new Thread(null, - new Runnable() { - @Override - public void run() { - final Wallet wallet = WalletManager.getInstance().openWallet(walletPath, password); - getActivity().runOnUiThread(new Runnable() { - public void run() { - String address = wallet.getAddress(); - wallet.close(); - show(address); - } - }); - } - } - , "Receive", MoneroHandlerThread.THREAD_STACK_SIZE).start(); + private void show(String walletPath, String password) { + new ReceiveFragment.AsyncShow().executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR, + walletPath, password); } + private class AsyncShow extends AsyncTask { + String password; + + String address; + + @Override + protected Boolean doInBackground(String... params) { + if (params.length != 2) return false; + String walletPath = params[0]; + password = params[1]; + Wallet wallet = WalletManager.getInstance().openWallet(walletPath, password); + address = wallet.getAddress(); + wallet.close(); + return true; + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + if (result) { + show(address); + } else { + Toast.makeText(getActivity(), getString(R.string.receive_cannot_open), Toast.LENGTH_LONG).show(); + hideProgress(); + } + } + } + + private boolean amountOk() { String amountEntry = etAmount.getText().toString(); if (amountEntry.isEmpty()) return true; diff --git a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java index ea0e12a5..af2985ca 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java @@ -24,7 +24,6 @@ import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.net.Uri; -import android.net.UrlQuerySanitizer; import android.os.Bundle; import android.os.IBinder; import android.os.PowerManager; @@ -35,8 +34,6 @@ import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.util.Log; -import android.view.Menu; -import android.view.MenuInflater; import android.view.MenuItem; import android.widget.Toast; @@ -49,7 +46,6 @@ import com.m2049r.xmrwallet.util.BarcodeData; import com.m2049r.xmrwallet.util.Helper; import com.m2049r.xmrwallet.util.TxData; -import java.io.File; import java.util.HashMap; import java.util.Map; @@ -594,7 +590,7 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment. switch (which) { case DialogInterface.BUTTON_POSITIVE: Bundle extras = new Bundle(); - extras.putString("type", GenerateReviewFragment.VIEW_WALLET); + extras.putString("type", GenerateReviewFragment.VIEW_TYPE_WALLET); replaceFragment(new GenerateReviewFragment(), null, extras); break; case DialogInterface.BUTTON_NEGATIVE: diff --git a/app/src/main/java/com/m2049r/xmrwallet/util/MoneroThreadPoolExecutor.java b/app/src/main/java/com/m2049r/xmrwallet/util/MoneroThreadPoolExecutor.java new file mode 100644 index 00000000..27bb64c2 --- /dev/null +++ b/app/src/main/java/com/m2049r/xmrwallet/util/MoneroThreadPoolExecutor.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 m2049r + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.m2049r.xmrwallet.util; + +import com.m2049r.xmrwallet.service.MoneroHandlerThread; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + + +public class MoneroThreadPoolExecutor { + private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); + private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); + private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; + private static final int KEEP_ALIVE_SECONDS = 30; + + private static final ThreadFactory sThreadFactory = new ThreadFactory() { + private final AtomicInteger mCount = new AtomicInteger(1); + + public Thread newThread(Runnable r) { + return new Thread(null, r, "MoneroTask #" + mCount.getAndIncrement(), MoneroHandlerThread.THREAD_STACK_SIZE); + } + }; + + private static final BlockingQueue sPoolWorkQueue = + new LinkedBlockingQueue<>(128); + + public static final Executor MONERO_THREAD_POOL_EXECUTOR; + + static { + ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( + CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, + sPoolWorkQueue, sThreadFactory); + threadPoolExecutor.allowCoreThreadTimeOut(true); + MONERO_THREAD_POOL_EXECUTOR = threadPoolExecutor; + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d93c00b4..f0a95210 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -177,6 +177,7 @@ (optional) Amount (optional) + Could not open wallet! Sensitive data will now be shown.\nLook over your shoulder! I\'m safe