/* * Copyright 2011-2012 the original author or authors. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package piuk.blockchain.android; import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.ActivityManager.RunningServiceInfo; import android.app.Application; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.*; import android.content.SharedPreferences.Editor; import android.content.pm.PackageInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.location.LocationManager; import android.net.ConnectivityManager; import android.os.Handler; import android.os.Looper; import android.preference.PreferenceManager; //import android.util.Log; import android.util.Pair; import android.widget.Toast; import com.google.bitcoin.core.*; import com.google.bitcoin.params.MainNetParams; //import com.google.bitcoin.core.Wallet.AutosaveEventListener; //import com.google.bitcoin.store.WalletExtensionSerializer; import com.google.bitcoin.store.WalletProtobufSerializer; import org.apache.commons.io.IOUtils; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.spongycastle.util.encoders.Hex; import piuk.blockchain.android.EventListeners; import piuk.blockchain.android.MyRemoteWallet; import piuk.blockchain.android.MyRemoteWallet.NotModfiedException; import piuk.blockchain.android.MyWallet; import piuk.blockchain.android.R; //import piuk.blockchain.android.service.BlockchainServiceImpl; import piuk.blockchain.android.service.WebsocketService; import piuk.blockchain.android.ui.AbstractWalletActivity; import piuk.blockchain.android.ui.PinEntryActivity; import piuk.blockchain.android.SuccessCallback; //import piuk.blockchain.android.ui.dialogs.RekeyWalletDialog; //import piuk.blockchain.android.util.ErrorReporter; import piuk.blockchain.android.util.RandomOrgGenerator; import piuk.blockchain.android.util.WalletUtils; import info.blockchain.wallet.ui.ObjectSuccessCallback; import info.blockchain.wallet.ui.WalletUtil; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.math.BigInteger; import java.net.CookieHandler; import java.net.CookieManager; import java.security.MessageDigest; import java.security.SecureRandom; import java.security.Security; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.atomic.AtomicBoolean; @SuppressLint("SimpleDateFormat") public class WalletApplication extends Application { private MyRemoteWallet blockchainWallet; private SharedCoin sharedCoin; private final Handler handler = new Handler(); private Timer timer; public int decryptionErrors = 0; //private Intent blockchainServiceIntent; private Intent websocketServiceIntent; public boolean didEncounterFatalPINServerError = false; public Wallet bitcoinjWallet; public Pair<Block, Integer> blockExplorerBlockPair; public long earliestKeyTime; private volatile boolean checkWalletStatusScheduled = false; private boolean isPassedPinScreen = false; private boolean isScanning = false; private String temporyPIN = null; public void setTemporyPIN(String PIN) { this.temporyPIN = PIN; } public String getTemporyPIN() { return temporyPIN; } public boolean getIsScanning(){ return isScanning; } public void setIsScanning(boolean isScanning){ this.isScanning = isScanning; } public boolean getIsPassedPinScreen(){ return isPassedPinScreen; } public void setIsPassedPinScreen(boolean isPassPinScreen){ this.isPassedPinScreen = isPassPinScreen; } public List<String> getSharedPrefsActiveAddresses() { Set<String> activeAddressesSet = PreferenceManager.getDefaultSharedPreferences(this).getStringSet("activeAddresses", null); if (activeAddressesSet != null) return new ArrayList<String>(activeAddressesSet); else return null; } public boolean setSharedPrefsActiveAddresses(List<String> activeAddresses) { Set<String> activeAddressesSet = new HashSet<String>(); activeAddressesSet.addAll(activeAddresses); Editor edit = PreferenceManager.getDefaultSharedPreferences(this).edit(); edit.putStringSet("activeAddresses", activeAddressesSet); return edit.commit(); } private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { final String action = intent.getAction(); handler.post(new Runnable() { public void run() { if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) { boolean hasConnectivity = !intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); if (hasConnectivity) { checkWalletStatus(null); } } } }); } }; public Handler getHandler() { return handler; } public boolean isGeoEnabled() { LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE); return lm.isProviderEnabled(LocationManager.GPS_PROVIDER); } public void clearWallet() { if (this.isInP2PFallbackMode()) this.leaveP2PMode(); Editor edit = PreferenceManager.getDefaultSharedPreferences( this).edit(); edit.remove("guid"); edit.remove("sharedKey"); edit.commit(); this.blockchainWallet = null; this.didEncounterFatalPINServerError = false; this.decryptionErrors = 0; this.deleteLocalWallet(); } public void connect() { if (timer != null) { try { timer.cancel(); timer.purge(); timer = null; } catch (Exception e) { e.printStackTrace(); } } final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); registerReceiver(broadcastReceiver, intentFilter); if (!WebsocketService.isRunning) startService(websocketServiceIntent); } public Integer getLatestHeightFromBlockExplorer() throws Exception { return Integer.valueOf(WalletUtils.getURL("http://blockexplorer.com/q/getblockcount")); } /* public Pair<Block, Integer> getLatestBlockHeaderFromBlockExplorer(Integer blockHeight) throws Exception { String hash = WalletUtils.getURL("http://blockexplorer.com/q/getblockhash/"+blockHeight); JSONObject obj = (JSONObject) new JSONParser().parse(WalletUtils.getURL("http://blockexplorer.com/rawblock/"+hash)); Block block = new Block(Constants.NETWORK_PARAMETERS); block.version = ((Number)obj.get("ver")).longValue(); block.prevBlockHash = new Sha256Hash((String)obj.get("prev_block")); block.merkleRoot = new Sha256Hash((String)obj.get("mrkl_root")); block.time = ((Number)obj.get("time")).longValue(); block.difficultyTarget = ((Number)obj.get("bits")).longValue(); block.nonce = ((Number)obj.get("nonce")).longValue(); block.hash = new Sha256Hash((String)obj.get("hash")); block.headerParsed = true; block.transactionsParsed = true; block.headerBytesValid = true; block.transactionBytesValid = true; return new Pair<Block, Integer>(block, blockHeight); } */ public long estimateFirstSeenFromBlockExplorer() throws Exception { SimpleDateFormat format = new SimpleDateFormat("yyyy-mm-dd hh:mm:ss"); long earliest = 0; for (ECKey key : bitcoinjWallet.getKeys()) { try { String url = "http://blockexplorer.com/q/addressfirstseen/" + key.toAddress(Constants.NETWORK_PARAMETERS).toString(); String response = WalletUtils.getURL(url); Date date = null; if (response.contains("Never")) { date = new Date(); } else { date = format.parse(response); } if (earliest == 0) { earliest = date.getTime(); } else if (date.getTime() < earliest) { earliest = date.getTime(); } } catch (Exception e) { e.printStackTrace(); } } return earliest; } //BitcoinJ Temp Wallet public void saveBitcoinJWallet() { if (bitcoinjWallet == null) return; try { File walletFile = getFileStreamPath(Constants.WALLET_FILENAME_PROTOBUF); bitcoinjWallet.saveToFile(walletFile); } catch (final IOException x) { throw new RuntimeException(x); } } /* private static final class WalletAutosaveEventListener implements AutosaveEventListener { public boolean caughtException(final Throwable throwable) { throwable.printStackTrace(); return true; } public void onBeforeAutoSave(final File file) { } public void onAfterAutoSave(final File file) { } } //BitcoinJ Temp Wallet private void loadBitcoinJWallet() { File walletFile = getFileStreamPath(Constants.WALLET_FILENAME_PROTOBUF); if (walletFile.exists()) { FileInputStream walletStream = null; try { walletStream = new FileInputStream(walletFile); WalletProtobufSerializer serializer = new WalletProtobufSerializer(); serializer.setWalletExtensionSerializer(new WalletExtensionSerializer() { public Wallet newWallet(NetworkParameters params) { return new MyWallet.WalletOverride(params); } }); Wallet wallet = serializer.readWallet(walletStream); if (wallet.getKeychainSize() > 0) { bitcoinjWallet = wallet; bitcoinjWallet.autosaveToFile(walletFile, 1, TimeUnit.SECONDS, new WalletAutosaveEventListener()); } } catch (final Exception e) { e.printStackTrace(); } finally { if (walletStream != null) { try { walletStream.close(); } catch (final IOException x) { x.printStackTrace(); } } } } } */ public void deleteBitcoinJLocalData() { try { //Delete the wallet file File bitcoinJFile = getFileStreamPath(Constants.WALLET_FILENAME_PROTOBUF); if (bitcoinJFile.exists()) { bitcoinJFile.delete(); } } catch (Exception e) { e.printStackTrace(); } try { //Clear the blockchain file (we need to rescan) File blockChainFile = new File(getDir("blockstore", Context.MODE_WORLD_READABLE | Context.MODE_WORLD_WRITEABLE), Constants.BLOCKCHAIN_FILENAME); if (blockChainFile.exists()) blockChainFile.delete(); } catch (Exception e) { e.printStackTrace(); } } public void startBlockchainService() { if (blockchainWallet == null) return; try { if (bitcoinjWallet == null) { deleteBitcoinJLocalData(); this.bitcoinjWallet = blockchainWallet.getBitcoinJWallet(); } new Thread(new Runnable() { @Override public void run() { earliestKeyTime = 0; blockExplorerBlockPair = null; try { earliestKeyTime = estimateFirstSeenFromBlockExplorer(); } catch (Exception e) { e.printStackTrace(); } /*if (earliestKeyTime > 0) { try { Integer height = getLatestHeightFromBlockExplorer(); long elapsedSeconds = ((System.currentTimeMillis() - earliestKeyTime) / 1000); for (int ii = 0; ii < 10; ++ii) { //One block every 10 minutes (600 seconds) int estimatedHeight = height - (ii*10000) - (int)(elapsedSeconds / 600); Pair<Block, Integer> pair = getLatestBlockHeaderFromBlockExplorer(estimatedHeight); if (pair.first.getTime().getTime() < earliestKeyTime) { blockExplorerBlockPair = pair; break; } } } catch (Exception e1) { e1.printStackTrace(); } }*/ //if (!isBitcoinJServiceRunning()) // startService(blockchainServiceIntent); EventListeners.invokeWalletDidChange(); } }).start(); } catch (Exception e) { e.printStackTrace(); } } public void leaveP2PMode() { this.bitcoinjWallet = null; //stopService(blockchainServiceIntent); deleteBitcoinJLocalData(); EventListeners.invokeWalletDidChange(); } public void disconnectSoon() { try { if (timer == null) { timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { handler.post(new Runnable() { public void run() { try { if (WebsocketService.isRunning) stopService(websocketServiceIntent); AbstractWalletActivity.lastDisplayedNetworkError = 0; if (isInP2PFallbackMode()) saveBitcoinJWallet(); unregisterReceiver(broadcastReceiver); } catch (Exception e) { e.printStackTrace(); } } }); } }, 5000); timer.schedule(new TimerTask() { @Override public void run() { handler.post(new Runnable() { public void run() { try { blockchainWallet = null; didEncounterFatalPINServerError = false; decryptionErrors = 0; } catch (Exception e) { e.printStackTrace(); } } }); } }, 120000); } } catch (Exception e) { e.printStackTrace(); } } public void generateNewWallet() throws Exception { this.blockchainWallet = new MyRemoteWallet(); this.decryptionErrors = 0; this.didEncounterFatalPINServerError = false; } public void checkWalletStatus(final AbstractWalletActivity activity) { boolean passwordSaved = PreferenceManager.getDefaultSharedPreferences(this).contains("encrypted_password"); if (this.isInP2PFallbackMode()) { if (!this.isBitcoinJServiceRunning()) { //startService(blockchainServiceIntent); } } if (blockchainWallet != null && decryptionErrors == 0 && (passwordSaved || didEncounterFatalPINServerError)) { if (!blockchainWallet.isUptoDate(Constants.MultiAddrTimeThreshold)) { if (checkWalletStatusScheduled) { return; } checkWalletStatusScheduled = true; handler.postDelayed(new Runnable() { @Override public void run() { if (blockchainWallet != null) { checkIfWalletHasUpdatedAndFetchTransactions(blockchainWallet.getTemporyPassword()); } checkWalletStatusScheduled = false; } }, 2500); } } else if (blockchainWallet == null || decryptionErrors > 0 || !passwordSaved) { if (activity == null || PinEntryActivity.active) return; //Remove old password String old_password = PreferenceManager.getDefaultSharedPreferences(this).getString("password", null); if (old_password != null) { decryptLocalWallet(readLocalWallet(), old_password); PreferenceManager.getDefaultSharedPreferences(this).edit().remove("password").commit(); } handler.post(new Runnable() { @Override public void run() { if (!PinEntryActivity.active) { Intent intent = new Intent(activity, PinEntryActivity.class); activity.startActivity(intent); } } }); } } @Override public void onCreate() { super.onCreate(); // ErrorReporter.getInstance().init(this); //blockchainServiceIntent = new Intent(this, BlockchainServiceImpl.class); websocketServiceIntent = new Intent(this, WebsocketService.class); System.setProperty("device_name", "android"); try { PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0); System.setProperty("device_version", pInfo.versionName); } catch (NameNotFoundException e1) { e1.printStackTrace(); } try { // Need to save session cookie for kaptcha CookieHandler.setDefault(new CookieManager()); Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider()); } catch (Throwable e) { e.printStackTrace(); } seedFromRandomOrg(); //loadBitcoinJWallet(); connect(); } public MyRemoteWallet getRemoteWallet() { return blockchainWallet; } public SharedCoin getSharedCoin() { return sharedCoin; } public void setSharedCoin(SharedCoin sharedCoin) { this.sharedCoin = sharedCoin; } public String getGUID() { return PreferenceManager.getDefaultSharedPreferences(this).getString("guid", null); } public long getLastTriedToRegisterForNotifications() { return PreferenceManager.getDefaultSharedPreferences(this).getLong("last_notification_register", 0); } /* public boolean needsWalletRekey() { MyRemoteWallet wallet = getRemoteWallet(); if (wallet == null || wallet.isNew()) return false; List<String> insecure_addresses = RekeyWalletDialog.getPossiblyInsecureAddresses(wallet); return !getHasAskedToRekeyWallet() && insecure_addresses.size() > 0 && !RekeyWalletDialog.hasKnownAndroidAddresses(wallet); } */ public boolean getHasAskedToRekeyWallet() { return PreferenceManager.getDefaultSharedPreferences(this).getBoolean("has_asked_rekeyed_wallet4", false); } public boolean setHasAskedRekeyedWallet(boolean value) { return PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean("has_asked_rekeyed_wallet4", value).commit(); } public boolean hasRegisteredForNotifications(String guid) { String registered_guid = PreferenceManager.getDefaultSharedPreferences(this).getString("registered_guid", null); return registered_guid != null && registered_guid.equals(guid); } public boolean setLastRegisteredForNotifications(long time) { Editor edit = PreferenceManager .getDefaultSharedPreferences( this .getApplicationContext()) .edit(); edit.putLong("last_notification_register", time); return edit.commit(); } public boolean setRegisteredForNotifications(String guid) { Editor edit = PreferenceManager .getDefaultSharedPreferences( this .getApplicationContext()) .edit(); edit.putString("registered_guid", guid); return edit.commit(); } public void registerForNotificationsIfNeeded(final String registration_id) { if (blockchainWallet == null) return; if (!blockchainWallet.isNew() && !hasRegisteredForNotifications(getGUID())) { if (getLastTriedToRegisterForNotifications() > System.currentTimeMillis()-30000) { // System.out.println("Registered Recently"); return; } setLastRegisteredForNotifications(System.currentTimeMillis()); new Thread(new Runnable() { @Override public void run() { try { if (blockchainWallet.registerNotifications(registration_id)) { setRegisteredForNotifications(getGUID()); } } catch (Exception e) { e.printStackTrace(); } } }).start(); } else { // System.out.println("New wallet or already Registered"); } } private boolean isBitcoinJServiceRunning() { ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { // if (BlockchainServiceImpl.class.getName().equals(service.service.getClassName())) { // return true; // } } return false; } public boolean isInP2PFallbackMode() { return bitcoinjWallet != null; } public void unRegisterForNotifications(final String registration_id) { new Thread(new Runnable() { @Override public void run() { try { if (blockchainWallet.unregisterNotifications(registration_id)) { setRegisteredForNotifications(null); } } catch (Exception e) { e.printStackTrace(); } } }).start(); } public String getSharedKey() { return PreferenceManager.getDefaultSharedPreferences(this).getString( "sharedKey", null); } public void notifyWidgets() { final Context context = getApplicationContext(); // notify widgets final AppWidgetManager appWidgetManager = AppWidgetManager .getInstance(context); for (final AppWidgetProviderInfo providerInfo : appWidgetManager .getInstalledProviders()) { // limit to own widgets if (providerInfo.provider.getPackageName().equals( context.getPackageName())) { final Intent intent = new Intent( AppWidgetManager.ACTION_APPWIDGET_UPDATE); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetManager.getAppWidgetIds(providerInfo.provider)); context.sendBroadcast(intent); } } } public synchronized String readExceptionLog() { try { FileInputStream multiaddrCacheFile = openFileInput(Constants.EXCEPTION_LOG); return IOUtils.toString(multiaddrCacheFile); } catch (IOException e1) { e1.printStackTrace(); return null; } } public synchronized void writeException(Exception e) { try { FileOutputStream file = openFileOutput(Constants.EXCEPTION_LOG, MODE_APPEND); PrintStream stream = new PrintStream(file); e.printStackTrace(stream); stream.close(); file.close(); } catch (IOException e1) { e1.printStackTrace(); } } public synchronized void writeMultiAddrCache(String repsonse) { if (blockchainWallet == null) return; try { FileOutputStream file = openFileOutput(blockchainWallet.getGUID() + Constants.MULTIADDR_FILENAME, Constants.WALLET_MODE); file.write(repsonse.getBytes()); file.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public synchronized void checkIfWalletHasUpdatedAndFetchTransactions(final String password) { checkIfWalletHasUpdatedAndFetchTransactions(password, null); } public synchronized void checkIfWalletHasUpdatedAndFetchTransactions(final String password, final SuccessCallback callbackFinal) { checkIfWalletHasUpdated(password, true, callbackFinal); } public synchronized void checkIfWalletHasUpdated(final String password, boolean fetchTransactions, final SuccessCallback callbackFinal) { if (getGUID() == null || getSharedKey() == null) { if (callbackFinal != null) callbackFinal.onFail(); return; } checkIfWalletHasUpdated(password, getGUID(), getSharedKey(), fetchTransactions, callbackFinal); } public void seedFromRandomOrg() { new Thread(new Runnable() { public void run() { try { MyWallet.extra_seed = RandomOrgGenerator.getRandomBytes(32); } catch (Exception e) { e.printStackTrace(); } } }).start(); } public synchronized void checkIfWalletHasUpdated(final String password, final String guid, final String sharedKey, final boolean fetchTransactions, final SuccessCallback callbackFinal) { final boolean fetchTx = true; // System.out.println("checkIfWalletHasUpdated: password"); final WalletApplication application = this; new Thread(new Runnable() { public void run() { JSONObject walletPayloadObj = null; SuccessCallback callback = callbackFinal; try { walletPayloadObj = MyRemoteWallet.getWalletPayload(guid, sharedKey); } catch(Exception e) { /* handler.post(new Runnable() { public void run() { if (callbackFinal != null) callbackFinal.onFail(); }; }); */ ; } /* * * if(walletPayloadObj == null) { handler.post(new Runnable() { public void run() { if (callbackFinal != null) callbackFinal.onFail(); return; }; }); return; } * * */ // // // if(walletPayloadObj == null) { // System.out.println("walletPayloadObj == null, going local"); String localWallet = readLocalWallet(); if(decryptLocalWallet(localWallet, password)) { // System.out.println("local decrypted ok"); try { walletPayloadObj = MyRemoteWallet.getWalletPayload(guid, sharedKey); } catch(Exception e) { // System.out.println("error fetching payload after local decrypt"); handler.post(new Runnable() { public void run() { if (callbackFinal != null) callbackFinal.onFail(); return; }; }); } } else { // System.out.println("local decrypted ko"); handler.post(new Runnable() { public void run() { if (callbackFinal != null) callbackFinal.onFail(); return; }; }); return; } } // // // // walletPayloadObj != null try { if (blockchainWallet == null) { // System.out.println("blockchainWallet == null, using payload + password"); blockchainWallet = new MyRemoteWallet(walletPayloadObj, password); doMultiAddr(false, null); } else { // System.out.println("blockchainWallet != null"); blockchainWallet.setPayload(walletPayloadObj); } // blockchainWallet.setTemporyPassword(password); decryptionErrors = 0; if (callback != null) { if(blockchainWallet == null) { handler.post(new Runnable() { public void run() { if (callbackFinal != null) callbackFinal.onFail(); }; }); } else { // assign blockchainWallet as the remoteWallet to be returned WalletUtil.putRemoteWallet(blockchainWallet); handler.post(new Runnable() { public void run() { if (callbackFinal != null) callbackFinal.onSuccess(); }; }); } callback = null; } EventListeners.invokeWalletDidChange(); } catch (Exception e) { e.printStackTrace(); decryptionErrors++; blockchainWallet = null; if (callback != null) { handler.post(new Runnable() { public void run() { Toast.makeText(WalletApplication.this, R.string.toast_wallet_decryption_failed, Toast.LENGTH_LONG).show(); if (callbackFinal != null) callbackFinal.onFail(); }; }); callback = null; } EventListeners.invokeWalletDidChange(); writeException(e); return; } if (decryptionErrors > 0) return; localSaveWallet(); try { // Get the balance and transaction if (fetchTx) doMultiAddr(true); } catch (Exception e) { e.printStackTrace(); writeException(e); handler.post(new Runnable() { public void run() { Toast.makeText(WalletApplication.this, R.string.toast_error_syncing_wallet, Toast.LENGTH_LONG).show(); } }); } } }).start(); } private AtomicBoolean isRunningMultiAddr = new AtomicBoolean(false); public void doMultiAddr(final boolean notifications) { doMultiAddr(notifications, null); } public void doMultiAddr(final boolean notifications, final SuccessCallback callback) { final MyRemoteWallet blockchainWallet = this.blockchainWallet; if (blockchainWallet == null) { if (callback != null) callback.onFail(); return; } if (!isRunningMultiAddr.compareAndSet(false, true)) { if (callback != null) callback.onFail(); return; } new Thread(new Runnable() { public void run() { try { String multiAddr = null; try { multiAddr = blockchainWallet.doMultiAddr(notifications); } catch (Exception e) { e.printStackTrace(); try { //Sleep for a bit and retry Thread.sleep(5000); } catch (InterruptedException e1) { e1.printStackTrace(); } try { multiAddr = blockchainWallet.doMultiAddr(notifications); } catch (Exception e1) { e1.printStackTrace(); EventListeners.invokeOnMultiAddrError(); if (callback != null) callback.onFail(); return; } } if (callback != null) callback.onSuccess(); try { writeMultiAddrCache(multiAddr); } catch (Exception e) { e.printStackTrace(); } //After multi addr the currency is set if (blockchainWallet.getLocalCurrencyCode() != null) setCurrency(blockchainWallet.getLocalCurrencyCode()); handler.post(new Runnable() { public void run() { notifyWidgets(); } }); } finally { isRunningMultiAddr.set(false); } } }).start(); } public void getBalances(final String[] addresses, final boolean notifications, final ObjectSuccessCallback callback) { new Thread(new Runnable() { public void run() { try { String response = null; try { response = MyRemoteWallet.getBalances(addresses, notifications); } catch (Exception e) { e.printStackTrace(); try { //Sleep for a bit and retry Thread.sleep(5000); } catch (InterruptedException e1) { e1.printStackTrace(); } try { response = MyRemoteWallet.getBalances(addresses, notifications); } catch (Exception e1) { e1.printStackTrace(); EventListeners.invokeOnMultiAddrError(); if (callback != null) callback.onFail(e1.getLocalizedMessage()); return; } } if (callback != null) { try { callback.onSuccess((JSONObject) new JSONParser().parse(response)); } catch (ParseException e) { callback.onFail(e.getLocalizedMessage()); } } handler.post(new Runnable() { public void run() { notifyWidgets(); } }); } finally { } } }).start(); } public void getAccountInformation(final boolean notifications, final SuccessCallback callback) { final MyRemoteWallet blockchainWallet = this.blockchainWallet; if (blockchainWallet == null) { if (callback != null) callback.onFail(); return; } new Thread(new Runnable() { public void run() { try { try { blockchainWallet.getAccountInformation(); } catch (Exception e) { e.printStackTrace(); try { //Sleep for a bit and retry Thread.sleep(5000); } catch (InterruptedException e1) { e1.printStackTrace(); } try { blockchainWallet.getAccountInformation(); } catch (Exception e1) { e1.printStackTrace(); if (callback != null) callback.onFail(); return; } } if (callback != null) callback.onSuccess(); handler.post(new Runnable() { public void run() { } }); } finally { } } }).start(); } public static interface AddAddressCallback { public void onSavedAddress(String address); public void onError(String reason); } public void saveWallet(final SuccessCallback callback) { if (this.isInP2PFallbackMode()) { callback.onFail(); return; }; new Thread() { @Override public void run() { try { if (blockchainWallet.remoteSave()) { handler.post(new Runnable() { public void run() { callback.onSuccess(); notifyWidgets(); } }); } else { handler.post(new Runnable() { public void run() { callback.onFail(); } }); } } catch (Exception e) { e.printStackTrace(); writeException(e); handler.post(new Runnable() { public void run() { callback.onFail(); Toast.makeText(WalletApplication.this, R.string.toast_error_syncing_wallet, Toast.LENGTH_LONG).show(); } }); } } }.start(); } public void addKeyToWallet(final ECKey key, final String address, final String label, final int tag, final AddAddressCallback callback) { if (blockchainWallet == null) { callback.onError("Wallet null."); return; } if (isInP2PFallbackMode()) { callback.onError("Error saving wallet."); return; } try { final boolean success = blockchainWallet.addKey(key, address, label); if (success) { if (tag != 0) { blockchainWallet.setTag(address, tag); } localSaveWallet(); saveWallet(new SuccessCallback() { @Override public void onSuccess() { checkIfWalletHasUpdated(blockchainWallet.getTemporyPassword(), false, new SuccessCallback() { @Override public void onSuccess() { try { ECKey key = blockchainWallet.getECKey(address); if (key != null && (key.toAddressCompressed(MainNetParams.get()).toString().equals(address) || key.toAddressUnCompressed(MainNetParams.get()).toString().equals(address))) { callback.onSavedAddress(address); } else { blockchainWallet.removeKey(key); callback.onError("WARNING! Wallet saved but address doesn't seem to exist after re-read."); } } catch (Exception e) { blockchainWallet.removeKey(key); callback.onError("WARNING! Error checking if ECKey is valid on re-read."); } } @Override public void onFail() { blockchainWallet.removeKey(key); callback.onError("WARNING! Error checking if address was correctly saved."); } }); } @Override public void onFail() { blockchainWallet.removeKey(key); callback.onError("Error saving wallet"); } }); } else { callback.onError("addKey returned false"); } } catch (Exception e) { e.printStackTrace(); writeException(e); callback.onError(e.getLocalizedMessage()); } } public void updateEmail(final String email, final SuccessCallback callback) { final MyRemoteWallet blockchainWallet = this.blockchainWallet; if (blockchainWallet == null) { if (callback != null) callback.onFail(); return; } new Thread(new Runnable() { public void run() { try { try { blockchainWallet.updateEmail(email); blockchainWallet.setEmail(email); } catch (Exception e) { e.printStackTrace(); try { //Sleep for a bit and retry Thread.sleep(5000); } catch (InterruptedException e1) { e1.printStackTrace(); } try { blockchainWallet.updateEmail(email); blockchainWallet.setEmail(email); } catch (Exception e1) { e1.printStackTrace(); EventListeners.invokeOnMultiAddrError(); if (callback != null) callback.onFail(); return; } } if (callback != null) callback.onSuccess(); handler.post(new Runnable() { public void run() { } }); } finally { } } }).start(); } public void updateSMS(final String smsNumber, final SuccessCallback callback) { final MyRemoteWallet blockchainWallet = this.blockchainWallet; if (blockchainWallet == null) { if (callback != null) callback.onFail(); return; } new Thread(new Runnable() { public void run() { try { try { blockchainWallet.updateSMS(smsNumber); blockchainWallet.setSmsNumber(smsNumber); } catch (Exception e) { e.printStackTrace(); try { //Sleep for a bit and retry Thread.sleep(5000); } catch (InterruptedException e1) { e1.printStackTrace(); } try { blockchainWallet.updateSMS(smsNumber); blockchainWallet.setSmsNumber(smsNumber); } catch (Exception e1) { e1.printStackTrace(); EventListeners.invokeOnMultiAddrError(); if (callback != null) callback.onFail(); return; } } if (callback != null) callback.onSuccess(); handler.post(new Runnable() { public void run() { } }); } finally { } } }).start(); } public void updateNotificationsType(final boolean enableEmailNotification, final boolean enableSMSNotification, final SuccessCallback callback) { final MyRemoteWallet blockchainWallet = this.blockchainWallet; if (blockchainWallet == null) { if (callback != null) callback.onFail(); return; } new Thread(new Runnable() { public void run() { try { try { blockchainWallet.updateNotificationsType(enableEmailNotification, enableSMSNotification); } catch (Exception e) { e.printStackTrace(); try { //Sleep for a bit and retry Thread.sleep(5000); } catch (InterruptedException e1) { e1.printStackTrace(); } try { blockchainWallet.updateNotificationsType(enableEmailNotification, enableSMSNotification); } catch (Exception e1) { e1.printStackTrace(); EventListeners.invokeOnMultiAddrError(); if (callback != null) callback.onFail(); return; } } if (callback != null) callback.onSuccess(); handler.post(new Runnable() { public void run() { } }); } finally { } } }).start(); } public void apiStoreKey(final String pin, final SuccessCallback callback) { final MyRemoteWallet blockchainWallet = this.blockchainWallet; if (blockchainWallet == null) { if (callback != null) callback.onFail(); return; } final WalletApplication application = this; new Thread(new Runnable(){ @Override public void run() { Looper.prepare(); Editor edit = PreferenceManager.getDefaultSharedPreferences(application).edit(); // // Save PIN // try { byte[] bytes = new byte[16]; SecureRandom random = new SecureRandom(); random.nextBytes(bytes); final String key = new String(Hex.encode(bytes), "UTF-8"); random.nextBytes(bytes); final String value = new String(Hex.encode(bytes), "UTF-8"); final JSONObject response = piuk.blockchain.android.ui.PinEntryActivity.apiStoreKey(key, value, pin); if (response.get("success") != null) { callback.onSuccess(); edit.putString("pin_kookup_key", key); edit.putString("encrypted_password", MyWallet.encrypt(application.getRemoteWallet().getTemporyPassword(), value, piuk.blockchain.android.ui.PinEntryActivity.PBKDF2Iterations)); if (!edit.commit()) { throw new Exception("Error Saving Preferences"); } else { } } else { Toast.makeText(application, response.toString(), Toast.LENGTH_LONG).show(); } } catch (Exception e) { Toast.makeText(application, e.toString(), Toast.LENGTH_LONG).show(); e.printStackTrace(); } // // // Looper.loop(); } }).start(); } public boolean setCurrency(String currency) { return PreferenceManager.getDefaultSharedPreferences(this).edit().putString(Constants.PREFS_KEY_EXCHANGE_CURRENCY, currency).commit(); } public boolean setShouldDisplayLocalCurrency(boolean value) { return PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean("should_display_local_currency", value).commit(); } public boolean getShouldDisplayLocalCurrency() { return PreferenceManager.getDefaultSharedPreferences(this).getBoolean("should_display_local_currency", false); } public boolean readLocalMultiAddr() { if (blockchainWallet == null) return false; try { // Restore the multi address cache FileInputStream multiaddrCacheFile = openFileInput(blockchainWallet.getGUID() + Constants.MULTIADDR_FILENAME); String multiAddr = IOUtils.toString(multiaddrCacheFile); blockchainWallet.parseMultiAddr(multiAddr, false); if (blockchainWallet.getLocalCurrencyCode() != null) setCurrency(blockchainWallet.getLocalCurrencyCode()); return true; } catch (Exception e) { writeException(e); e.printStackTrace(); return false; } } public String makeWalletChecksum(String payload) { try { return new String(Hex.encode(MessageDigest.getInstance("SHA-256").digest(payload.getBytes("UTF-8")))); } catch (Exception e) {} return null; } public String readLocalWallet() { try { // Read the wallet from local file FileInputStream file = openFileInput(Constants.WALLET_FILENAME); return IOUtils.toString(file, "UTF-8"); } catch (Exception e) {} return null; } public boolean deleteLocalWallet() { try { if (deleteFile(Constants.WALLET_FILENAME)) { // System.out.println("Removed Local Wallet"); } else { // System.out.println("Error Removing Local Wallet"); } } catch (Exception e) { writeException(e); e.printStackTrace(); } return false; } public boolean decryptLocalWallet(String payload, String password) { try { MyRemoteWallet wallet = new MyRemoteWallet(payload, password); if (wallet.getGUID().equals(getGUID())) { this.blockchainWallet = wallet; this.decryptionErrors = 0; EventListeners.invokeWalletDidChange(); return true; } else { return false; } } catch (Exception e) { writeException(e); e.printStackTrace(); } return false; } public void localSaveWallet() { if (blockchainWallet == null) return; try { if (blockchainWallet.isNew()) return; FileOutputStream file = openFileOutput( Constants.WALLET_FILENAME, Constants.WALLET_MODE); file.write(blockchainWallet.getPayload().getBytes()); file.close(); } catch (Exception e) { e.printStackTrace(); } } public Address determineSelectedAddress() { if (blockchainWallet == null) return null; final String[] addresses = blockchainWallet.getActiveAddresses(); if (addresses.length == 0) return null; final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); final String selectedAddress = prefs.getString(Constants.PREFS_KEY_SELECTED_ADDRESS, null); if (selectedAddress != null) { for (final String address : addresses) { if (address.equals(selectedAddress)) { try { return new Address(Constants.NETWORK_PARAMETERS, address); } catch (WrongNetworkException e) { e.printStackTrace(); } catch (AddressFormatException e) { e.printStackTrace(); } } } } try { return new Address(Constants.NETWORK_PARAMETERS, addresses[0]); } catch (WrongNetworkException e) { e.printStackTrace(); } catch (AddressFormatException e) { e.printStackTrace(); } return null; } public final int applicationVersionCode() { try { return getPackageManager().getPackageInfo(getPackageName(), 0).versionCode; } catch (NameNotFoundException x) { return 0; } } public final String applicationVersionName() { try { return getPackageManager().getPackageInfo(getPackageName(), 0).versionName; } catch (NameNotFoundException x) { return "unknown"; } } public void sharedCoinRecoverSeeds(List<String> shared_coin_seeds) { sharedCoin.recoverSeeds(shared_coin_seeds, new SuccessCallback() { @Override public void onSuccess() { // Log.d("SharedCoin", "SharedCoin recoverSeeds onSuccess"); handler.post(new Runnable() { public void run() { Toast.makeText(WalletApplication.this, "SharedCoin recoverSeeds Success", Toast.LENGTH_LONG).show(); } }); } @Override public void onFail() { // Log.d("SharedCoin", "SharedCoin recoverSeeds onFail"); handler.post(new Runnable() { public void run() { Toast.makeText(WalletApplication.this, "SharedCoin recoverSeeds fail", Toast.LENGTH_LONG).show(); } }); } }); } public void sendSharedCoin(List<String> fromAddresses, String toAddress, BigInteger amount) { if (SharedCoin.VERSION > sharedCoin.getMinSupportedVersion()) { try { sharedCoin.sendSharedCoin(4, fromAddresses, amount, toAddress, new ObjectSuccessCallback() { @Override public void onSuccess(final Object obj) { // Log.d("SharedCoin", "SharedCoin sendSharedCoin onSuccess"); handler.post(new Runnable() { public void run() { Toast.makeText(WalletApplication.this, (String) obj, Toast.LENGTH_LONG).show(); } }); } @Override public void onFail(final String error) { // Log.d("SharedCoin", "SharedCoin sendSharedCoin onFail " + error); handler.post(new Runnable() { public void run() { Toast.makeText(WalletApplication.this, error, Toast.LENGTH_LONG).show(); } }); } }); } catch (Exception e) { // Log.d("SharedCoin", "SharedCoin sendSharedCoin Exception " + e.getLocalizedMessage()); Toast.makeText(WalletApplication.this, e.getLocalizedMessage(), Toast.LENGTH_LONG).show(); e.printStackTrace(); } } } public void sharedCoinGetInfo(final SuccessCallback callback) { final MyRemoteWallet blockchainWallet = this.blockchainWallet; if (blockchainWallet == null) { if (callback != null) callback.onFail(); return; } sharedCoin = SharedCoin.getInstance(this, blockchainWallet); new Thread(new Runnable() { public void run() { try { try { sharedCoin.getInfo(); } catch (Exception e) { e.printStackTrace(); try { //Sleep for a bit and retry Thread.sleep(5000); } catch (InterruptedException e1) { e1.printStackTrace(); } try { sharedCoin.getInfo(); } catch (Exception e1) { e1.printStackTrace(); EventListeners.invokeOnMultiAddrError(); if (callback != null) callback.onFail(); return; } } if (callback != null) callback.onSuccess(); handler.post(new Runnable() { public void run() { } }); } finally { } } }).start(); } }