You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
353 lines
12 KiB
353 lines
12 KiB
/**
|
|
* Copyright (c) 2017 m2049r
|
|
* <p>
|
|
* 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
|
|
* <p>
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
* <p>
|
|
* 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.service;
|
|
|
|
import android.app.ProgressDialog;
|
|
import android.app.Service;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.os.Binder;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.os.HandlerThread;
|
|
import android.os.IBinder;
|
|
import android.os.Looper;
|
|
import android.os.Message;
|
|
import android.os.PowerManager;
|
|
import android.os.Process;
|
|
import android.util.Log;
|
|
import android.widget.Toast;
|
|
|
|
import com.m2049r.xmrwallet.R;
|
|
import com.m2049r.xmrwallet.model.Wallet;
|
|
import com.m2049r.xmrwallet.model.WalletListener;
|
|
import com.m2049r.xmrwallet.model.WalletManager;
|
|
import com.m2049r.xmrwallet.util.Helper;
|
|
|
|
// Bind / Unbind
|
|
// Activity onCreate() / onDestroy()
|
|
// or
|
|
// Activity onStart() / onStop()
|
|
|
|
|
|
public class WalletService extends Service {
|
|
final static String TAG = "WalletService";
|
|
|
|
public static final String REQUEST = "request";
|
|
public static final String REQUEST_WALLET = "wallet";
|
|
public static final String REQUEST_CMD_LOAD = "load";
|
|
public static final String REQUEST_CMD_LOAD_PW = "walletPassword";
|
|
|
|
public static final int START_SERVICE = 1;
|
|
public static final int STOP_SERVICE = 2;
|
|
|
|
private MyWalletListener listener = null;
|
|
|
|
private class MyWalletListener implements WalletListener {
|
|
private Wallet wallet;
|
|
boolean updated = true;
|
|
|
|
Wallet getWallet() {
|
|
return wallet;
|
|
}
|
|
|
|
MyWalletListener(Wallet aWallet) {
|
|
if (aWallet == null) throw new IllegalArgumentException("Cannot open wallet!");
|
|
this.wallet = aWallet;
|
|
}
|
|
|
|
public void start() {
|
|
Log.d(TAG, "MyWalletListener.start()");
|
|
if (wallet == null) throw new IllegalStateException("No wallet!");
|
|
//acquireWakeLock();
|
|
wallet.setListener(this);
|
|
wallet.startRefresh();
|
|
}
|
|
|
|
public void stop() {
|
|
Log.d(TAG, "MyWalletListener.stop()");
|
|
if (wallet == null) throw new IllegalStateException("No wallet!");
|
|
wallet.pauseRefresh();
|
|
wallet.setListener(null);
|
|
//releaseWakeLock();
|
|
}
|
|
|
|
// WalletListener callbacks
|
|
public void moneySpent(String txId, long amount) {
|
|
Log.d(TAG, "moneySpent() " + amount + " @ " + txId);
|
|
}
|
|
|
|
public void moneyReceived(String txId, long amount) {
|
|
Log.d(TAG, "moneyReceived() " + amount + " @ " + txId);
|
|
}
|
|
|
|
public void unconfirmedMoneyReceived(String txId, long amount) {
|
|
Log.d(TAG, "unconfirmedMoneyReceived() " + amount + " @ " + txId);
|
|
}
|
|
|
|
long lastBlockTime = 0;
|
|
|
|
public void newBlock(long height) {
|
|
if (wallet == null) throw new IllegalStateException("No wallet!");
|
|
// don't flood with an update for every block ...
|
|
if (lastBlockTime < System.currentTimeMillis() - 2000) {
|
|
Log.d(TAG, "newBlock() @" + height + "with observer " + observer);
|
|
lastBlockTime = System.currentTimeMillis();
|
|
if (observer != null) {
|
|
observer.onRefreshed(wallet, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void updated() {
|
|
Log.d(TAG, "updated() " + wallet.getBalance());
|
|
if (wallet == null) throw new IllegalStateException("No wallet!");
|
|
updated = true;
|
|
}
|
|
|
|
public void refreshed() {
|
|
if (wallet == null) throw new IllegalStateException("No wallet!");
|
|
Log.d(TAG, "refreshed() " + wallet.getBalance() + " sync=" + wallet.isSynchronized() + "with observer " + observer);
|
|
if (updated) {
|
|
if (observer != null) {
|
|
wallet.getHistory().refresh();
|
|
observer.onRefreshed(wallet, true);
|
|
updated = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////
|
|
// communication back to client (activity) //
|
|
/////////////////////////////////////////////
|
|
// NB: This allows for only one observer, i.e. only a single activity bound here
|
|
|
|
private Observer observer = null;
|
|
|
|
public void setObserver(Observer anObserver) {
|
|
observer = anObserver;
|
|
Log.d(TAG, "setObserver " + observer);
|
|
}
|
|
|
|
public interface Observer {
|
|
void onRefreshed(Wallet wallet, boolean full);
|
|
|
|
void onProgress(String text);
|
|
|
|
void onProgress(int n);
|
|
}
|
|
|
|
private void showProgress(String text) {
|
|
if (observer != null) {
|
|
observer.onProgress(text);
|
|
}
|
|
}
|
|
|
|
private void showProgress(int n) {
|
|
if (observer != null) {
|
|
observer.onProgress(n);
|
|
}
|
|
}
|
|
|
|
//
|
|
public Wallet getWallet() {
|
|
if (listener == null) throw new IllegalStateException("no listener");
|
|
return listener.getWallet();
|
|
}
|
|
|
|
/////////////////////////////////////////////
|
|
/////////////////////////////////////////////
|
|
|
|
private Looper mServiceLooper;
|
|
private WalletService.ServiceHandler mServiceHandler;
|
|
|
|
// Handler that receives messages from the thread
|
|
private final class ServiceHandler extends Handler {
|
|
public ServiceHandler(Looper looper) {
|
|
super(looper);
|
|
}
|
|
|
|
@Override
|
|
public void handleMessage(Message msg) {
|
|
Log.d(TAG, "Handling " + msg.arg2);
|
|
switch (msg.arg2) {
|
|
case START_SERVICE: {
|
|
Bundle extras = msg.getData();
|
|
String walletId = extras.getString(REQUEST_WALLET, null);
|
|
String walletPw = extras.getString(REQUEST_CMD_LOAD_PW, null);
|
|
Log.d(TAG, "LOAD wallet " + walletId);// + ":" + walletPw);
|
|
if (walletId != null) {
|
|
start(walletId, walletPw); // TODO What if this fails?
|
|
}
|
|
}
|
|
break;
|
|
case STOP_SERVICE:
|
|
stop();
|
|
break;
|
|
default:
|
|
Log.e(TAG, "UNKNOWN " + msg.arg2);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCreate() {
|
|
//mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
|
//showNotification();
|
|
|
|
// We are using a HandlerThread and a Looper to avoid loading and closing
|
|
// concurrency
|
|
HandlerThread thread = new HandlerThread("WalletService",
|
|
Process.THREAD_PRIORITY_BACKGROUND);
|
|
thread.start();
|
|
|
|
// Get the HandlerThread's Looper and use it for our Handler
|
|
mServiceLooper = thread.getLooper();
|
|
mServiceHandler = new WalletService.ServiceHandler(mServiceLooper);
|
|
|
|
Log.d(TAG, "Service created");
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
Log.d(TAG, "onDestroy()");
|
|
// Cancel the persistent notification.
|
|
//mNM.cancel(NOTIFICATION);
|
|
if (this.listener != null) {
|
|
Log.w(TAG, "onDestroy() with active listener");
|
|
// no need to stop() here because the wallet closing should have been triggered
|
|
// through onUnbind() already
|
|
}
|
|
}
|
|
|
|
public class WalletServiceBinder extends Binder {
|
|
public WalletService getService() {
|
|
return WalletService.this;
|
|
}
|
|
}
|
|
|
|
private final IBinder mBinder = new WalletServiceBinder();
|
|
|
|
@Override
|
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
// when the activity satrts the service, it expects to start it for a new wallet
|
|
// the service is possibly still occupied with saving the last opened wallet
|
|
// so we queue the open request
|
|
// this should not matter since the old activity is not getting updates
|
|
// and the new one is not listening yet (although it will be bound)
|
|
Log.d(TAG, "onStartCommand()");
|
|
//acquireWakeLock(); // we want to be awake for the fun stuff
|
|
// For each start request, send a message to start a job and deliver the
|
|
// start ID so we know which request we're stopping when we finish the job
|
|
Message msg = mServiceHandler.obtainMessage();
|
|
msg.arg2 = START_SERVICE;
|
|
msg.setData(intent.getExtras());
|
|
mServiceHandler.sendMessage(msg);
|
|
//Log.d(TAG, "onStartCommand() message sent");
|
|
return START_NOT_STICKY;
|
|
}
|
|
|
|
@Override
|
|
public IBinder onBind(Intent intent) {
|
|
// Very first client binds
|
|
Log.d(TAG, "onBind()");
|
|
return mBinder;
|
|
}
|
|
|
|
@Override
|
|
public boolean onUnbind(Intent intent) {
|
|
Log.d(TAG, "onUnbind()");
|
|
// All clients have unbound with unbindService()
|
|
Message msg = mServiceHandler.obtainMessage();
|
|
msg.arg2 = STOP_SERVICE;
|
|
mServiceHandler.sendMessage(msg);
|
|
Log.d(TAG, "onUnbind() message sent");
|
|
return true; // true is important so that onUnbind is also called next time
|
|
}
|
|
|
|
private void start(String walletName, String walletPassword) {
|
|
// if there is an listener it is always started / syncing
|
|
Log.d(TAG, "start()");
|
|
showProgress(getString(R.string.status_wallet_loading));
|
|
showProgress(10);
|
|
if (listener == null) {
|
|
Log.d(TAG, "start() loadWallet");
|
|
Wallet aWallet = loadWallet(walletName, walletPassword);
|
|
listener = new MyWalletListener(aWallet);
|
|
listener.start();
|
|
showProgress(95);
|
|
}
|
|
Log.d(TAG, "start() done");
|
|
}
|
|
|
|
public void stop() {
|
|
Log.d(TAG, "stop()");
|
|
setObserver(null); // in case it was not reset already
|
|
if (listener != null) {
|
|
listener.stop();
|
|
Log.d(TAG, "stop() closing");
|
|
listener.getWallet().close();
|
|
Log.d(TAG, "stop() closed");
|
|
listener = null;
|
|
}
|
|
stopSelf();
|
|
// TODO ensure the Looper & thread actually stop and go away?
|
|
}
|
|
|
|
private Wallet loadWallet(String walletName, String walletPassword) {
|
|
String path = Helper.getWalletPath(getApplicationContext(), walletName);
|
|
//Log.d(TAG, "open wallet " + path);
|
|
Wallet wallet = openWallet(walletName, walletPassword);
|
|
//Log.d(TAG, "wallet opened: " + wallet);
|
|
if (wallet != null) {
|
|
//Log.d(TAG, wallet.getStatus().toString());
|
|
Log.d(TAG, "Using daemon " + WalletManager.getInstance().getDaemonAddress());
|
|
showProgress(55);
|
|
wallet.init(0);
|
|
showProgress(90);
|
|
Log.d(TAG, wallet.getConnectionStatus().toString());
|
|
}
|
|
return wallet;
|
|
}
|
|
|
|
private Wallet openWallet(String walletName, String walletPassword) {
|
|
String path = Helper.getWalletPath(getApplicationContext(), walletName);
|
|
showProgress(20);
|
|
Wallet wallet = null;
|
|
WalletManager walletMgr = WalletManager.getInstance();
|
|
Log.d(TAG, "WalletManager testnet=" + walletMgr.isTestNet());
|
|
showProgress(30);
|
|
if (walletMgr.walletExists(path)) {
|
|
Log.d(TAG, "open wallet " + path);
|
|
wallet = walletMgr.openWallet(path, walletPassword);
|
|
showProgress(60);
|
|
Log.d(TAG, "wallet opened");
|
|
Wallet.Status status = wallet.getStatus();
|
|
Log.d(TAG, "wallet status is " + status);
|
|
if (status != Wallet.Status.Status_Ok) {
|
|
Log.d(TAG, "wallet status is " + status);
|
|
WalletManager.getInstance().close(wallet); // TODO close() failed?
|
|
wallet = null;
|
|
// TODO what do we do with the progress??
|
|
}
|
|
}
|
|
return wallet;
|
|
}
|
|
}
|
|
|