Some Bugfixes and some Kotlin that helped fixing the bugs
This commit is contained in:
@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Christian Basler
|
||||
*
|
||||
* 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 ch.dissem.apps.abit.synchronization;
|
||||
|
||||
import android.accounts.AbstractAccountAuthenticator;
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountAuthenticatorResponse;
|
||||
import android.accounts.NetworkErrorException;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
/*
|
||||
* Implement AbstractAccountAuthenticator and stub out all
|
||||
* of its methods
|
||||
*/
|
||||
public class Authenticator extends AbstractAccountAuthenticator {
|
||||
public static final Account ACCOUNT_SYNC = new Account("Bitmessage", "ch.dissem.bitmessage");
|
||||
public static final Account ACCOUNT_POW = new Account("Proof of Work ", "ch.dissem.bitmessage");
|
||||
|
||||
// Simple constructor
|
||||
public Authenticator(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
// Editing properties is not supported
|
||||
@Override
|
||||
public Bundle editProperties(
|
||||
AccountAuthenticatorResponse r, String s) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// Don't add additional accounts
|
||||
@Override
|
||||
public Bundle addAccount(
|
||||
AccountAuthenticatorResponse r,
|
||||
String s,
|
||||
String s2,
|
||||
String[] strings,
|
||||
Bundle bundle) throws NetworkErrorException {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Ignore attempts to confirm credentials
|
||||
@Override
|
||||
public Bundle confirmCredentials(
|
||||
AccountAuthenticatorResponse r,
|
||||
Account account,
|
||||
Bundle bundle) throws NetworkErrorException {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Getting an authentication token is not supported
|
||||
@Override
|
||||
public Bundle getAuthToken(
|
||||
AccountAuthenticatorResponse r,
|
||||
Account account,
|
||||
String s,
|
||||
Bundle bundle) throws NetworkErrorException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// Getting a label for the auth token is not supported
|
||||
@Override
|
||||
public String getAuthTokenLabel(String s) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// Updating user credentials is not supported
|
||||
@Override
|
||||
public Bundle updateCredentials(
|
||||
AccountAuthenticatorResponse r,
|
||||
Account account,
|
||||
String s, Bundle bundle) throws NetworkErrorException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// Checking features for the account is not supported
|
||||
@Override
|
||||
public Bundle hasFeatures(
|
||||
AccountAuthenticatorResponse r,
|
||||
Account account, String[] strings) throws NetworkErrorException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2016 Christian Basler
|
||||
*
|
||||
* 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 ch.dissem.apps.abit.synchronization
|
||||
|
||||
import android.accounts.AbstractAccountAuthenticator
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountAuthenticatorResponse
|
||||
import android.accounts.NetworkErrorException
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
|
||||
/**
|
||||
* Implement AbstractAccountAuthenticator and stub out all
|
||||
* of its methods
|
||||
*/
|
||||
class Authenticator(context: Context) : AbstractAccountAuthenticator(context) {
|
||||
|
||||
// Editing properties is not supported
|
||||
override fun editProperties(r: AccountAuthenticatorResponse, s: String) = throw UnsupportedOperationException()
|
||||
|
||||
// Don't add additional accounts
|
||||
@Throws(NetworkErrorException::class)
|
||||
override fun addAccount(
|
||||
r: AccountAuthenticatorResponse,
|
||||
s: String,
|
||||
s2: String,
|
||||
strings: Array<String>,
|
||||
bundle: Bundle) = null
|
||||
|
||||
// Ignore attempts to confirm credentials
|
||||
@Throws(NetworkErrorException::class)
|
||||
override fun confirmCredentials(
|
||||
r: AccountAuthenticatorResponse,
|
||||
account: Account,
|
||||
bundle: Bundle) = null
|
||||
|
||||
// Getting an authentication token is not supported
|
||||
@Throws(NetworkErrorException::class)
|
||||
override fun getAuthToken(
|
||||
r: AccountAuthenticatorResponse,
|
||||
account: Account,
|
||||
s: String,
|
||||
bundle: Bundle) = throw UnsupportedOperationException()
|
||||
|
||||
// Getting a label for the auth token is not supported
|
||||
override fun getAuthTokenLabel(s: String) = throw UnsupportedOperationException()
|
||||
|
||||
// Updating user credentials is not supported
|
||||
@Throws(NetworkErrorException::class)
|
||||
override fun updateCredentials(
|
||||
r: AccountAuthenticatorResponse,
|
||||
account: Account,
|
||||
s: String, bundle: Bundle) = throw UnsupportedOperationException()
|
||||
|
||||
// Checking features for the account is not supported
|
||||
@Throws(NetworkErrorException::class)
|
||||
override fun hasFeatures(
|
||||
r: AccountAuthenticatorResponse,
|
||||
account: Account, strings: Array<String>) = throw UnsupportedOperationException()
|
||||
|
||||
companion object {
|
||||
@JvmField val ACCOUNT_SYNC = Account("Bitmessage", "ch.dissem.bitmessage")
|
||||
@JvmField val ACCOUNT_POW = Account("Proof of Work ", "ch.dissem.bitmessage")
|
||||
}
|
||||
}
|
@ -14,34 +14,30 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package ch.dissem.apps.abit.synchronization;
|
||||
package ch.dissem.apps.abit.synchronization
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.os.IBinder
|
||||
|
||||
/**
|
||||
* A bound Service that instantiates the authenticator
|
||||
* when started.
|
||||
*/
|
||||
public class AuthenticatorService extends Service {
|
||||
class AuthenticatorService : Service() {
|
||||
/**
|
||||
* Instance field that stores the authenticator object
|
||||
*/
|
||||
private Authenticator authenticator;
|
||||
private var authenticator: Authenticator? = null
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
override fun onCreate() {
|
||||
// Create a new authenticator object
|
||||
authenticator = new Authenticator(this);
|
||||
authenticator = Authenticator(this)
|
||||
}
|
||||
|
||||
/*
|
||||
* When the system binds to this Service to make the RPC call
|
||||
* return the authenticator's IBinder.
|
||||
*/
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return authenticator.getIBinder();
|
||||
}
|
||||
}
|
||||
override fun onBind(intent: Intent) = authenticator?.iBinder
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Christian Basler
|
||||
*
|
||||
* 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 ch.dissem.apps.abit.synchronization;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
/*
|
||||
* Define an implementation of ContentProvider that stubs out
|
||||
* all methods
|
||||
*/
|
||||
public class StubProvider extends ContentProvider {
|
||||
public static final String AUTHORITY = "ch.dissem.apps.abit.provider";
|
||||
|
||||
/*
|
||||
* Always return true, indicating that the
|
||||
* provider loaded correctly.
|
||||
*/
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return no type for MIME type
|
||||
*/
|
||||
@Override
|
||||
public String getType(@NonNull Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* query() always returns no results
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public Cursor query(
|
||||
@NonNull Uri uri,
|
||||
String[] projection,
|
||||
String selection,
|
||||
String[] selectionArgs,
|
||||
String sortOrder) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* insert() always returns null (no URI)
|
||||
*/
|
||||
@Override
|
||||
public Uri insert(@NonNull Uri uri, ContentValues values) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* delete() always returns "no rows affected" (0)
|
||||
*/
|
||||
@Override
|
||||
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* update() always returns "no rows affected" (0)
|
||||
*/
|
||||
public int update(
|
||||
@NonNull Uri uri,
|
||||
ContentValues values,
|
||||
String selection,
|
||||
String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2016 Christian Basler
|
||||
*
|
||||
* 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 ch.dissem.apps.abit.synchronization
|
||||
|
||||
import android.content.ContentProvider
|
||||
import android.content.ContentValues
|
||||
import android.net.Uri
|
||||
|
||||
/*
|
||||
* Define an implementation of ContentProvider that stubs out
|
||||
* all methods
|
||||
*/
|
||||
class StubProvider : ContentProvider() {
|
||||
|
||||
/**
|
||||
* Always return true, indicating that the
|
||||
* provider loaded correctly.
|
||||
*/
|
||||
override fun onCreate() = true
|
||||
|
||||
/**
|
||||
* Return no type for MIME type
|
||||
*/
|
||||
override fun getType(uri: Uri) = null
|
||||
|
||||
/**
|
||||
* query() always returns no results
|
||||
*/
|
||||
override fun query(
|
||||
uri: Uri,
|
||||
projection: Array<String>?,
|
||||
selection: String?,
|
||||
selectionArgs: Array<String>?,
|
||||
sortOrder: String?) = null
|
||||
|
||||
/**
|
||||
* insert() always returns null (no URI)
|
||||
*/
|
||||
override fun insert(uri: Uri, values: ContentValues?) = null
|
||||
|
||||
/**
|
||||
* delete() always returns "no rows affected" (0)
|
||||
*/
|
||||
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?) = 0
|
||||
|
||||
/**
|
||||
* update() always returns "no rows affected" (0)
|
||||
*/
|
||||
override fun update(
|
||||
uri: Uri,
|
||||
values: ContentValues?,
|
||||
selection: String?,
|
||||
selectionArgs: Array<String>?) = 0
|
||||
|
||||
companion object {
|
||||
const val AUTHORITY = "ch.dissem.apps.abit.provider"
|
||||
}
|
||||
}
|
@ -1,190 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Christian Basler
|
||||
*
|
||||
* 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 ch.dissem.apps.abit.synchronization;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.content.AbstractThreadedSyncAdapter;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.SyncResult;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import ch.dissem.apps.abit.service.Singleton;
|
||||
import ch.dissem.apps.abit.util.Preferences;
|
||||
import ch.dissem.bitmessage.BitmessageContext;
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||
import ch.dissem.bitmessage.entity.CustomMessage;
|
||||
import ch.dissem.bitmessage.exception.DecryptionFailedException;
|
||||
import ch.dissem.bitmessage.extensions.CryptoCustomMessage;
|
||||
import ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest;
|
||||
import ch.dissem.bitmessage.ports.ProofOfWorkRepository;
|
||||
|
||||
import static ch.dissem.apps.abit.synchronization.Authenticator.ACCOUNT_POW;
|
||||
import static ch.dissem.apps.abit.synchronization.Authenticator.ACCOUNT_SYNC;
|
||||
import static ch.dissem.apps.abit.synchronization.StubProvider.AUTHORITY;
|
||||
import static ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest.Request.CALCULATE;
|
||||
import static ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest.Request.COMPLETE;
|
||||
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||
|
||||
/**
|
||||
* Sync Adapter to synchronize with the Bitmessage network - fetches
|
||||
* new objects and then disconnects.
|
||||
*/
|
||||
public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(SyncAdapter.class);
|
||||
|
||||
private static final long SYNC_FREQUENCY = 15 * 60; // seconds
|
||||
|
||||
private final BitmessageContext bmc;
|
||||
|
||||
/**
|
||||
* Set up the sync adapter
|
||||
*/
|
||||
public SyncAdapter(Context context, boolean autoInitialize) {
|
||||
super(context, autoInitialize);
|
||||
bmc = Singleton.getBitmessageContext(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPerformSync(Account account, Bundle extras, String authority,
|
||||
ContentProviderClient provider, SyncResult syncResult) {
|
||||
try {
|
||||
if (account.equals(ACCOUNT_SYNC)) {
|
||||
if (Preferences.isConnectionAllowed(getContext())) {
|
||||
syncData();
|
||||
}
|
||||
} else if (account.equals(ACCOUNT_POW)) {
|
||||
syncPOW();
|
||||
} else {
|
||||
syncResult.stats.numAuthExceptions++;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
syncResult.stats.numIoExceptions++;
|
||||
} catch (DecryptionFailedException e) {
|
||||
syncResult.stats.numAuthExceptions++;
|
||||
}
|
||||
}
|
||||
|
||||
private void syncData() throws IOException {
|
||||
// If the Bitmessage context acts as a full node, synchronization isn't necessary
|
||||
if (bmc.isRunning()) {
|
||||
LOG.info("Synchronization skipped, Abit is acting as a full node");
|
||||
return;
|
||||
}
|
||||
LOG.info("Synchronizing Bitmessage");
|
||||
|
||||
LOG.info("Synchronization started");
|
||||
bmc.synchronize(
|
||||
Preferences.getTrustedNode(getContext()),
|
||||
Preferences.getTrustedNodePort(getContext()),
|
||||
Preferences.getTimeoutInSeconds(getContext()),
|
||||
true
|
||||
);
|
||||
LOG.info("Synchronization finished");
|
||||
}
|
||||
|
||||
private void syncPOW() throws IOException, DecryptionFailedException {
|
||||
// If the Bitmessage context acts as a full node, synchronization isn't necessary
|
||||
LOG.info("Looking for completed POW");
|
||||
|
||||
BitmessageAddress identity = Singleton.getIdentity(getContext());
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
byte[] privateKey = identity.getPrivateKey().getPrivateEncryptionKey();
|
||||
byte[] signingKey = cryptography().createPublicKey(identity.getPublicDecryptionKey());
|
||||
ProofOfWorkRequest.Reader reader = new ProofOfWorkRequest.Reader(identity);
|
||||
ProofOfWorkRepository powRepo = Singleton.getProofOfWorkRepository(getContext());
|
||||
List<byte[]> items = powRepo.getItems();
|
||||
for (byte[] initialHash : items) {
|
||||
ProofOfWorkRepository.Item item = powRepo.getItem(initialHash);
|
||||
byte[] target = cryptography().getProofOfWorkTarget(item.getObjectMessage(), item.getNonceTrialsPerByte(), item.getExtraBytes());
|
||||
CryptoCustomMessage<ProofOfWorkRequest> cryptoMsg = new CryptoCustomMessage<>(
|
||||
new ProofOfWorkRequest(identity, initialHash, CALCULATE, target));
|
||||
cryptoMsg.signAndEncrypt(identity, signingKey);
|
||||
CustomMessage response = bmc.send(
|
||||
Preferences.getTrustedNode(getContext()),
|
||||
Preferences.getTrustedNodePort(getContext()),
|
||||
cryptoMsg
|
||||
);
|
||||
if (response.isError()) {
|
||||
LOG.error("Server responded with error: " + new String(response.getData(),
|
||||
"UTF-8"));
|
||||
} else {
|
||||
ProofOfWorkRequest decryptedResponse = CryptoCustomMessage.read(
|
||||
response, reader).decrypt(privateKey);
|
||||
if (decryptedResponse.getRequest() == COMPLETE) {
|
||||
bmc.internals().getProofOfWorkService().onNonceCalculated(
|
||||
initialHash, decryptedResponse.getData());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (items.size() == 0) {
|
||||
stopPowSync(getContext());
|
||||
}
|
||||
LOG.info("Synchronization finished");
|
||||
}
|
||||
|
||||
public static void startSync(Context ctx) {
|
||||
// Create account, if it's missing. (Either first run, or user has deleted account.)
|
||||
Account account = addAccount(ctx, ACCOUNT_SYNC);
|
||||
|
||||
// Recommend a schedule for automatic synchronization. The system may modify this based
|
||||
// on other scheduled syncs and network utilization.
|
||||
ContentResolver.addPeriodicSync(account, AUTHORITY, new Bundle(), SYNC_FREQUENCY);
|
||||
}
|
||||
|
||||
public static void stopSync(Context ctx) {
|
||||
// Create account, if it's missing. (Either first run, or user has deleted account.)
|
||||
Account account = addAccount(ctx, ACCOUNT_SYNC);
|
||||
|
||||
ContentResolver.removePeriodicSync(account, AUTHORITY, new Bundle());
|
||||
}
|
||||
|
||||
|
||||
public static void startPowSync(Context ctx) {
|
||||
// Create account, if it's missing. (Either first run, or user has deleted account.)
|
||||
Account account = addAccount(ctx, ACCOUNT_POW);
|
||||
|
||||
// Recommend a schedule for automatic synchronization. The system may modify this based
|
||||
// on other scheduled syncs and network utilization.
|
||||
ContentResolver.addPeriodicSync(account, AUTHORITY, new Bundle(), SYNC_FREQUENCY);
|
||||
}
|
||||
|
||||
public static void stopPowSync(Context ctx) {
|
||||
// Create account, if it's missing. (Either first run, or user has deleted account.)
|
||||
Account account = addAccount(ctx, ACCOUNT_POW);
|
||||
|
||||
ContentResolver.removePeriodicSync(account, AUTHORITY, new Bundle());
|
||||
}
|
||||
|
||||
private static Account addAccount(Context ctx, Account account) {
|
||||
if (AccountManager.get(ctx).addAccountExplicitly(account, null, null)) {
|
||||
// Inform the system that this account supports sync
|
||||
ContentResolver.setIsSyncable(account, AUTHORITY, 1);
|
||||
// Inform the system that this account is eligible for auto sync when the network is up
|
||||
ContentResolver.setSyncAutomatically(account, AUTHORITY, true);
|
||||
}
|
||||
return account;
|
||||
}
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright 2016 Christian Basler
|
||||
*
|
||||
* 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 ch.dissem.apps.abit.synchronization
|
||||
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountManager
|
||||
import android.content.*
|
||||
import android.os.Bundle
|
||||
import ch.dissem.apps.abit.service.Singleton
|
||||
import ch.dissem.apps.abit.synchronization.Authenticator.Companion.ACCOUNT_POW
|
||||
import ch.dissem.apps.abit.synchronization.Authenticator.Companion.ACCOUNT_SYNC
|
||||
import ch.dissem.apps.abit.synchronization.StubProvider.Companion.AUTHORITY
|
||||
import ch.dissem.apps.abit.util.Preferences
|
||||
import ch.dissem.bitmessage.exception.DecryptionFailedException
|
||||
import ch.dissem.bitmessage.extensions.CryptoCustomMessage
|
||||
import ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest
|
||||
import ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest.Request.CALCULATE
|
||||
import ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest.Request.COMPLETE
|
||||
import ch.dissem.bitmessage.utils.Singleton.cryptography
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Sync Adapter to synchronize with the Bitmessage network - fetches
|
||||
* new objects and then disconnects.
|
||||
*/
|
||||
class SyncAdapter(context: Context, autoInitialize: Boolean) : AbstractThreadedSyncAdapter(context, autoInitialize) {
|
||||
|
||||
private val bmc = Singleton.getBitmessageContext(context)
|
||||
|
||||
override fun onPerformSync(
|
||||
account: Account,
|
||||
extras: Bundle,
|
||||
authority: String,
|
||||
provider: ContentProviderClient,
|
||||
syncResult: SyncResult
|
||||
) {
|
||||
try {
|
||||
if (account == ACCOUNT_SYNC) {
|
||||
if (Preferences.isConnectionAllowed(context)) {
|
||||
syncData()
|
||||
}
|
||||
} else if (account == ACCOUNT_POW) {
|
||||
syncPOW()
|
||||
} else {
|
||||
syncResult.stats.numAuthExceptions++
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
syncResult.stats.numIoExceptions++
|
||||
} catch (e: DecryptionFailedException) {
|
||||
syncResult.stats.numAuthExceptions++
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun syncData() {
|
||||
// If the Bitmessage context acts as a full node, synchronization isn't necessary
|
||||
if (bmc.isRunning()) {
|
||||
LOG.info("Synchronization skipped, Abit is acting as a full node")
|
||||
return
|
||||
}
|
||||
val trustedNode = Preferences.getTrustedNode(context)
|
||||
if (trustedNode == null) {
|
||||
LOG.info("Trusted node not available, disabling synchronization")
|
||||
stopSync(context)
|
||||
return
|
||||
}
|
||||
LOG.info("Synchronization started")
|
||||
bmc.synchronize(
|
||||
trustedNode,
|
||||
Preferences.getTrustedNodePort(context),
|
||||
Preferences.getTimeoutInSeconds(context),
|
||||
true
|
||||
)
|
||||
LOG.info("Synchronization finished")
|
||||
}
|
||||
|
||||
private fun syncPOW() {
|
||||
val identity = Singleton.getIdentity(context)
|
||||
if (identity == null) {
|
||||
LOG.info("No identity available - skipping POW synchronization")
|
||||
return
|
||||
}
|
||||
val trustedNode = Preferences.getTrustedNode(context)
|
||||
if (trustedNode == null) {
|
||||
LOG.info("Trusted node not available, disabling POW synchronization")
|
||||
stopPowSync(context)
|
||||
return
|
||||
}
|
||||
// If the Bitmessage context acts as a full node, synchronization isn't necessary
|
||||
LOG.info("Looking for completed POW")
|
||||
|
||||
val privateKey = identity.privateKey!!.privateEncryptionKey
|
||||
val signingKey = cryptography().createPublicKey(identity.publicDecryptionKey)
|
||||
val reader = ProofOfWorkRequest.Reader(identity)
|
||||
val powRepo = Singleton.getProofOfWorkRepository(context)
|
||||
val items = powRepo.getItems()
|
||||
for (initialHash in items) {
|
||||
val (objectMessage, nonceTrialsPerByte, extraBytes) = powRepo.getItem(initialHash)
|
||||
val target = cryptography().getProofOfWorkTarget(objectMessage, nonceTrialsPerByte, extraBytes)
|
||||
val cryptoMsg = CryptoCustomMessage(
|
||||
ProofOfWorkRequest(identity, initialHash, CALCULATE, target))
|
||||
cryptoMsg.signAndEncrypt(identity, signingKey)
|
||||
val response = bmc.send(
|
||||
trustedNode,
|
||||
Preferences.getTrustedNodePort(context),
|
||||
cryptoMsg
|
||||
)
|
||||
if (response.isError) {
|
||||
LOG.error("Server responded with error: ${String(response.getData())}")
|
||||
} else {
|
||||
val (_, _, request, data) = CryptoCustomMessage.read(response, reader).decrypt(privateKey)
|
||||
if (request == COMPLETE) {
|
||||
bmc.internals.proofOfWorkService.onNonceCalculated(initialHash, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (items.isEmpty()) {
|
||||
stopPowSync(context)
|
||||
}
|
||||
LOG.info("Synchronization finished")
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = LoggerFactory.getLogger(SyncAdapter::class.java)
|
||||
|
||||
private const val SYNC_FREQUENCY = 15 * 60L // seconds
|
||||
|
||||
@JvmStatic
|
||||
fun startSync(ctx: Context) {
|
||||
// Create account, if it's missing. (Either first run, or user has deleted account.)
|
||||
val account = addAccount(ctx, ACCOUNT_SYNC)
|
||||
|
||||
// Recommend a schedule for automatic synchronization. The system may modify this based
|
||||
// on other scheduled syncs and network utilization.
|
||||
ContentResolver.addPeriodicSync(account, AUTHORITY, Bundle(), SYNC_FREQUENCY)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun stopSync(ctx: Context) {
|
||||
// Create account, if it's missing. (Either first run, or user has deleted account.)
|
||||
val account = addAccount(ctx, ACCOUNT_SYNC)
|
||||
|
||||
ContentResolver.removePeriodicSync(account, AUTHORITY, Bundle())
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun startPowSync(ctx: Context) {
|
||||
// Create account, if it's missing. (Either first run, or user has deleted account.)
|
||||
val account = addAccount(ctx, ACCOUNT_POW)
|
||||
|
||||
// Recommend a schedule for automatic synchronization. The system may modify this based
|
||||
// on other scheduled syncs and network utilization.
|
||||
ContentResolver.addPeriodicSync(account, AUTHORITY, Bundle(), SYNC_FREQUENCY)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun stopPowSync(ctx: Context) {
|
||||
// Create account, if it's missing. (Either first run, or user has deleted account.)
|
||||
val account = addAccount(ctx, ACCOUNT_POW)
|
||||
|
||||
ContentResolver.removePeriodicSync(account, AUTHORITY, Bundle())
|
||||
}
|
||||
|
||||
private fun addAccount(ctx: Context, account: Account): Account {
|
||||
if (AccountManager.get(ctx).addAccountExplicitly(account, null, null)) {
|
||||
// Inform the system that this account supports sync
|
||||
ContentResolver.setIsSyncable(account, AUTHORITY, 1)
|
||||
// Inform the system that this account is eligible for auto sync when the network is up
|
||||
ContentResolver.setSyncAutomatically(account, AUTHORITY, true)
|
||||
}
|
||||
return account
|
||||
}
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Christian Basler
|
||||
*
|
||||
* 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 ch.dissem.apps.abit.synchronization;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
/**
|
||||
* Define a Service that returns an IBinder for the
|
||||
* sync adapter class, allowing the sync adapter framework to call
|
||||
* onPerformSync().
|
||||
*/
|
||||
public class SyncService extends Service {
|
||||
// Storage for an instance of the sync adapter
|
||||
private static SyncAdapter syncAdapter = null;
|
||||
// Object to use as a thread-safe lock
|
||||
private static final Object syncAdapterLock = new Object();
|
||||
|
||||
/**
|
||||
* Instantiate the sync adapter object.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate() {
|
||||
/*
|
||||
* Create the sync adapter as a singleton.
|
||||
* Set the sync adapter as syncable
|
||||
* Disallow parallel syncs
|
||||
*/
|
||||
synchronized (syncAdapterLock) {
|
||||
if (syncAdapter == null) {
|
||||
syncAdapter = new SyncAdapter(this, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an object that allows the system to invoke
|
||||
* the sync adapter.
|
||||
*/
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
/*
|
||||
* Get the object that allows external processes
|
||||
* to call onPerformSync(). The object is created
|
||||
* in the base class code when the SyncAdapter
|
||||
* constructors call super()
|
||||
*/
|
||||
return syncAdapter.getSyncAdapterBinder();
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2016 Christian Basler
|
||||
*
|
||||
* 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 ch.dissem.apps.abit.synchronization
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.os.IBinder
|
||||
|
||||
/**
|
||||
* Define a Service that returns an IBinder for the
|
||||
* sync adapter class, allowing the sync adapter framework to call
|
||||
* onPerformSync().
|
||||
*/
|
||||
class SyncService : Service() {
|
||||
|
||||
/**
|
||||
* Instantiate the sync adapter object.
|
||||
*/
|
||||
override fun onCreate() {
|
||||
// Create the sync adapter as a singleton.
|
||||
// Set the sync adapter as syncable
|
||||
// Disallow parallel syncs
|
||||
synchronized(syncAdapterLock) {
|
||||
if (syncAdapter == null) {
|
||||
syncAdapter = SyncAdapter(this, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an object that allows the system to invoke
|
||||
* the sync adapter.
|
||||
*/
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
// Get the object that allows external processes
|
||||
// to call onPerformSync(). The object is created
|
||||
// in the base class code when the SyncAdapter
|
||||
// constructors call super()
|
||||
return syncAdapter?.syncAdapterBinder
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Storage for an instance of the sync adapter
|
||||
private var syncAdapter: SyncAdapter? = null
|
||||
// Object to use as a thread-safe lock
|
||||
private val syncAdapterLock = Any()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user