Implemented remote service using Messenger.

This commit is contained in:
Adrian Tiberius 2015-06-26 12:21:17 +02:00
parent 33be741881
commit 961fb203df
31 changed files with 1915 additions and 145 deletions

View File

@ -14,7 +14,7 @@
android:largeHeap="true"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:name=".RemoteMainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -28,11 +28,22 @@
</activity>
<service
android:name="org.ethereum.android_app.EthereumService"
android:name=".EthereumService"
android:enabled="true"
android:exported="true"
android:process=":ethereum_process" >
</service>
<service
android:name=".EthereumRemoteService"
android:enabled="true"
android:exported="true"
android:process=":ethereum_remote_process" >
</service>
<activity
android:name=".AidlMainActivity"
android:label="@string/app_name" >
</activity>
</application>
</manifest>

View File

@ -1,8 +1,6 @@
package org.ethereum.android_app;
import android.support.v4.app.Fragment;
public interface ActivityInterface {
void registerFragment(FragmentInterface fragment);

View File

@ -4,11 +4,9 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewPager;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
@ -16,10 +14,8 @@ import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.os.Build;
import android.widget.Toast;
import org.ethereum.android.EthereumManager;
import org.ethereum.android.interop.IEthereumService;
import org.ethereum.android.interop.IListener;
import org.ethereum.config.SystemProperties;
@ -28,7 +24,7 @@ import java.io.File;
import java.util.ArrayList;
public class MainActivity extends ActionBarActivity implements ActivityInterface {
public class AidlMainActivity extends ActionBarActivity implements ActivityInterface {
private static final String TAG = "MyActivity";
@ -67,7 +63,7 @@ public class MainActivity extends ActionBarActivity implements ActivityInterface
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
ethereumService = IEthereumService.Stub.asInterface(service);
Toast.makeText(MainActivity.this, "service attached", Toast.LENGTH_SHORT).show();
Toast.makeText(AidlMainActivity.this, "service attached", Toast.LENGTH_SHORT).show();
try {
@ -93,7 +89,7 @@ public class MainActivity extends ActionBarActivity implements ActivityInterface
SystemProperties.CONFIG.activePeerPort(),
SystemProperties.CONFIG.activePeerNodeid());
}
Toast.makeText(MainActivity.this, "connected to service", Toast.LENGTH_SHORT).show();
Toast.makeText(AidlMainActivity.this, "connected to service", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
logMessage("Error adding listener: " + e.getMessage());
}
@ -104,7 +100,7 @@ public class MainActivity extends ActionBarActivity implements ActivityInterface
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
ethereumService = null;
Toast.makeText(MainActivity.this, "service disconnected", Toast.LENGTH_SHORT).show();
Toast.makeText(AidlMainActivity.this, "service disconnected", Toast.LENGTH_SHORT).show();
}
};
@ -125,7 +121,7 @@ public class MainActivity extends ActionBarActivity implements ActivityInterface
tabs.setDistributeEvenly(true);
tabs.setViewPager(viewPager);
ComponentName myService = startService(new Intent(MainActivity.this, EthereumService.class));
ComponentName myService = startService(new Intent(AidlMainActivity.this, EthereumService.class));
doBindService();
//StrictMode.enableDefaults();
@ -133,13 +129,13 @@ public class MainActivity extends ActionBarActivity implements ActivityInterface
protected void logMessage(String message) {
MainActivity.consoleLog += message + "\n";
int consoleLength = MainActivity.consoleLog.length();
AidlMainActivity.consoleLog += message + "\n";
int consoleLength = AidlMainActivity.consoleLog.length();
if (consoleLength > 5000) {
MainActivity.consoleLog = MainActivity.consoleLog.substring(4000);
AidlMainActivity.consoleLog = AidlMainActivity.consoleLog.substring(4000);
}
broadcastFragments(MainActivity.consoleLog);
broadcastFragments(AidlMainActivity.consoleLog);
}
@ -155,9 +151,9 @@ public class MainActivity extends ActionBarActivity implements ActivityInterface
// Establish a connection with the service. We use an explicit
// class name because there is no reason to be able to let other
// applications replace our component.
bindService(new Intent(MainActivity.this, EthereumService.class), serviceConnection, Context.BIND_AUTO_CREATE);
bindService(new Intent(AidlMainActivity.this, EthereumService.class), serviceConnection, Context.BIND_AUTO_CREATE);
isBound = true;
Toast.makeText(MainActivity.this, "binding to service", Toast.LENGTH_SHORT).show();
Toast.makeText(AidlMainActivity.this, "binding to service", Toast.LENGTH_SHORT).show();
}
void doUnbindService() {
@ -177,7 +173,7 @@ public class MainActivity extends ActionBarActivity implements ActivityInterface
// Detach our existing connection.
unbindService(serviceConnection);
isBound = false;
Toast.makeText(MainActivity.this, "unbinding from service", Toast.LENGTH_SHORT).show();
Toast.makeText(AidlMainActivity.this, "unbinding from service", Toast.LENGTH_SHORT).show();
}
}
@ -191,7 +187,6 @@ public class MainActivity extends ActionBarActivity implements ActivityInterface
@Override
public boolean onCreateOptionsMenu(Menu menu) {
//return super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

View File

@ -3,11 +3,23 @@ package org.ethereum.android_app;
import android.support.multidex.MultiDexApplication;
import org.ethereum.android.service.EthereumConnector;
public class EthereumApplication extends MultiDexApplication {
EthereumConnector ethereum = null;
@Override public void onCreate() {
super.onCreate();
ethereum = new EthereumConnector(this, EthereumRemoteService.class);
ethereum.bindService();
}
@Override
public void onTerminate() {
super.onTerminate();
ethereum.unbindService();
}
}

View File

@ -0,0 +1,10 @@
package org.ethereum.android_app;
public class EthereumRemoteService extends org.ethereum.android.service.EthereumRemoteService {
public EthereumRemoteService() {
super();
}
}

View File

@ -1,6 +1,6 @@
package org.ethereum.android_app;
import org.ethereum.android.EthereumAidlService;
import org.ethereum.android.service.EthereumAidlService;
import org.ethereum.android.interop.IListener;
public class EthereumService extends EthereumAidlService {

View File

@ -0,0 +1,69 @@
package org.ethereum.android_app;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import java.util.ArrayList;
public class RemoteMainActivity extends ActionBarActivity implements ActivityInterface {
private Toolbar toolbar;
private ViewPager viewPager;
private SlidingTabLayout tabs;
private TabsPagerAdapter adapter;
protected ArrayList<FragmentInterface> fragments = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.tool_bar);
setSupportActionBar(toolbar);
adapter = new TabsPagerAdapter(getSupportFragmentManager());
viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setAdapter(adapter);;
tabs = (SlidingTabLayout) findViewById(R.id.tabs);
tabs.setDistributeEvenly(true);
tabs.setViewPager(viewPager);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public void registerFragment(FragmentInterface fragment) {
if (!fragments.contains(fragment)) {
fragments.add(fragment);
}
}
}

View File

@ -10,12 +10,14 @@ public class TestActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_test, menu);
return true;
@ -23,6 +25,7 @@ public class TestActivity extends ActionBarActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.

View File

@ -1,22 +1,132 @@
package org.ethereum.android_app;
import android.os.Bundle;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import org.ethereum.android.EthereumManager;
import org.ethereum.android.interop.AdminInfo;
import org.ethereum.android.service.ConnectorHandler;
import org.ethereum.android.service.EthereumClientMessage;
import org.ethereum.config.SystemProperties;
public class TestsFragment extends Fragment {
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
EthereumManager ethereumManager;
import static org.ethereum.config.SystemProperties.CONFIG;
public class TestsFragment extends Fragment implements ConnectorHandler {
TextView connectionStatus;
TextView blockchainStatus;
TextView startupTime;
TextView isConsensus;
TextView blockExecTime;
Button connectButton;
Button getEthereumStatus;
Button getBlockchainStatus;
String identifier = UUID.randomUUID().toString();
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_tests, container, false);
connectionStatus = (TextView)view.findViewById(R.id.connectionStatus);
blockchainStatus = (TextView)view.findViewById(R.id.blockchainStatus);
startupTime = (TextView)view.findViewById(R.id.startupTime);
isConsensus = (TextView)view.findViewById(R.id.isConsensus);
blockExecTime = (TextView)view.findViewById(R.id.blockExecTime);
connectButton = (Button)view.findViewById(R.id.connectButton);
getEthereumStatus = (Button)view.findViewById(R.id.getEthereumStatus);
getBlockchainStatus = (Button)view.findViewById(R.id.getBlockchainStatus);
connectButton.setOnClickListener(onClickListener);
getEthereumStatus.setOnClickListener(onClickListener);
getBlockchainStatus.setOnClickListener(onClickListener);
EthereumApplication app = (EthereumApplication)getActivity().getApplication();
app.ethereum.registerHandler(this);
return view;
}
private View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(final View v) {
EthereumApplication app = (EthereumApplication)getActivity().getApplication();
switch(v.getId()){
case R.id.connectButton:
app.ethereum.connect(CONFIG.activePeerIP(), CONFIG.activePeerPort(), CONFIG.activePeerNodeid());
break;
case R.id.getEthereumStatus:
app.ethereum.getConnectionStatus(identifier);
app.ethereum.getAdminInfo(identifier);
break;
case R.id.getBlockchainStatus:
app.ethereum.getBlockchainStatus(identifier);
break;
}
}
};
protected void updateTextView(final TextView view, final String text) {
view.post(new Runnable() {
@Override
public void run() {
view.setText(text);
}
});
}
@Override
public boolean handleMessage(final Message message) {
boolean isClaimed = true;
switch(message.what) {
case EthereumClientMessage.MSG_CONNECTION_STATUS:
updateTextView(connectionStatus, message.getData().getString("status"));
break;
case EthereumClientMessage.MSG_BLOCKCHAIN_STATUS:
updateTextView(blockchainStatus, message.getData().getString("status"));
break;
case EthereumClientMessage.MSG_ADMIN_INFO:
Bundle data = message.getData();
data.setClassLoader(AdminInfo.class.getClassLoader());
AdminInfo adminInfo = data.getParcelable("adminInfo");
updateTextView(startupTime, new SimpleDateFormat("yyyy MM dd HH:mm:ss").format(new Date(adminInfo.getStartupTimeStamp())));
updateTextView(isConsensus, adminInfo.isConsensus() ? "true" : "false");
updateTextView(blockExecTime, adminInfo.getExecAvg().toString());
break;
case EthereumClientMessage.MSG_ONLINE_PEER:
break;
case EthereumClientMessage.MSG_PEERS:
break;
case EthereumClientMessage.MSG_PENDING_TRANSACTIONS:
break;
case EthereumClientMessage.MSG_SUBMIT_TRANSACTION_RESULT:
break;
default:
isClaimed = false;
}
return isClaimed;
}
@Override
public String getID() {
return identifier;
}
}

View File

@ -0,0 +1,12 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="org.ethereum.android_app.RemoteMainActivity">
<TextView android:text="@string/hello_world" android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>

View File

@ -7,5 +7,162 @@
android:gravity="center"
android:text="@string/ethereumj_tests"
android:textSize="20sp"
android:layout_centerInParent="true"/>
android:id="@+id/textView2"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/textView2"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:weightSum="1">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Connect"
android:id="@+id/connectButton"
android:layout_below="@+id/textView2"
android:layout_alignLeft="@+id/getBlockchainStatus"
android:layout_alignStart="@+id/getBlockchainStatus"
android:layout_marginTop="10dp"
android:layout_gravity="center_horizontal"
android:textSize="10sp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Ethereum Status"
android:id="@+id/getEthereumStatus"
android:layout_alignTop="@+id/connectButton"
android:layout_toRightOf="@+id/textView"
android:layout_toEndOf="@+id/textView"
android:layout_gravity="center_horizontal"
android:singleLine="true"
android:textSize="10sp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Blockchain Status"
android:id="@+id/getBlockchainStatus"
android:layout_marginBottom="42dp"
android:layout_above="@+id/textView"
android:layout_centerHorizontal="true"
android:textSize="10sp" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Ethereum Status: "
android:id="@+id/textView" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Not Connected"
android:id="@+id/connectionStatus" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Blockchain Status: "
android:id="@+id/textView4" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="None"
android:id="@+id/blockchainStatus" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Startup Time:"
android:id="@+id/textView6" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="None"
android:id="@+id/startupTime" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Is Consensus:"
android:id="@+id/textView3" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="None"
android:id="@+id/isConsensus" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Average block exec time:"
android:id="@+id/textView7" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="None"
android:id="@+id/blockExecTime" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>

View File

@ -1,6 +1,7 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
xmlns:tools="http://schemas.android.com/tools"
tools:context="org.ethereum.android_app.RemoteMainActivity">
<item android:id="@+id/action_settings" android:title="@string/action_settings"
android:orderInCategory="100" app:showAsAction="never" />
</menu>

View File

@ -9,5 +9,6 @@
<string name="title_activity_test">TestActivity</string>
<string name="hello_world">Hello world!</string>
<string name="title_activity_remote_main">RemoteMainActivity</string>
</resources>

View File

@ -6,7 +6,7 @@
android:allowBackup="true"
android:label="@string/app_name" >
<service
android:name=".EthereumAidlService"
android:name=".service.EthereumAidlService"
android:enabled="true"
android:exported="true"
android:process=":ethereum_process" >
@ -15,13 +15,13 @@
</intent-filter>
</service>
<service
android:name=".EthereumRemoteService"
android:name=".service.EthereumRemoteService"
android:enabled="true"
android:exported="true"
android:process=":ethereum_process" >
</service>
<service
android:name=".EthereumService"
android:name=".service.EthereumService"
android:enabled="true"
android:exported="true" >
</service>

View File

@ -4,7 +4,7 @@
<!-- Create a logcat appender -->
<appender name="logcat" class="ch.qos.logback.classic.android.LogcatAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>

View File

@ -2,8 +2,6 @@ package org.ethereum.android;
import android.content.Context;
import com.j256.ormlite.android.apptools.OpenHelperManager;
import org.ethereum.android.di.modules.EthereumModule;
import org.ethereum.android.di.components.DaggerEthereumComponent;
import org.ethereum.config.SystemProperties;

View File

@ -1,16 +0,0 @@
package org.ethereum.android;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class EthereumRemoteService extends Service {
public EthereumRemoteService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}

View File

@ -1,16 +0,0 @@
package org.ethereum.android;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class EthereumService extends Service {
public EthereumService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}

View File

@ -0,0 +1,82 @@
package org.ethereum.android.interop;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.List;
public class AdminInfo extends org.ethereum.manager.AdminInfo implements Parcelable {
public AdminInfo(org.ethereum.manager.AdminInfo adminInfo) {
startupTimeStamp = adminInfo.getStartupTimeStamp();
consensus = adminInfo.isConsensus();
blockExecTime = adminInfo.getBlockExecTime();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeLong(getStartupTimeStamp());
parcel.writeByte((byte) (isConsensus() ? 1 : 0));
parcel.writeLongArray(listToArray(getBlockExecTime()));
}
private long[] listToArray(List<Long> list) {
int length = list.size();
int arrayLength = 0;
for (int i = 0; i < length; i++) {
if (list.get(i) != null) {
arrayLength++;
}
}
long[] array = new long[arrayLength];
int arrayIndex = 0;
for (int i = 0; i < length; i++) {
Long item = list.get(i);
if (item != null) {
array[arrayIndex] = item;
arrayIndex++;
}
}
return array;
}
private List<Long> arrayToList(long[] array) {
ArrayList<Long> list = new ArrayList<>(array.length);
for (long item : array) {
list.add(item);
}
return list;
}
public static final Parcelable.Creator<AdminInfo> CREATOR = new Parcelable.Creator<AdminInfo>() {
public AdminInfo createFromParcel(Parcel in) {
return new AdminInfo(in);
}
public AdminInfo[] newArray(int size) {
return new AdminInfo[size];
}
};
private AdminInfo(Parcel in) {
startupTimeStamp = in.readLong();
consensus = in.readByte() == 1 ? true : false;
blockExecTime = arrayToList(in.createLongArray());
}
}

View File

@ -0,0 +1,61 @@
package org.ethereum.android.interop;
import android.os.Parcel;
import android.os.Parcelable;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class PeerInfo extends org.ethereum.net.peerdiscovery.PeerInfo implements Parcelable {
public PeerInfo(InetAddress ip, int port, String peerId) {
super(ip, port, peerId);
}
public PeerInfo(org.ethereum.net.peerdiscovery.PeerInfo peerInfo) {
super(peerInfo.getAddress(), peerInfo.getPort(), peerInfo.getPeerId());
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeByteArray(address.getAddress());
parcel.writeInt(port);
parcel.writeString(peerId);
}
public static final Parcelable.Creator<PeerInfo> CREATOR = new Parcelable.Creator<PeerInfo>() {
public PeerInfo createFromParcel(Parcel in) {
return new PeerInfo(in);
}
public PeerInfo[] newArray(int size) {
return new PeerInfo[size];
}
};
private PeerInfo(Parcel in) {
super(null, 0, "");
InetAddress ip = null;
try {
address = InetAddress.getByAddress(in.createByteArray());
} catch (UnknownHostException e) {
}
port = in.readInt();
peerId = in.readString();
}
}

View File

@ -0,0 +1,44 @@
package org.ethereum.android.interop;
import android.os.Parcel;
import android.os.Parcelable;
public class Transaction extends org.ethereum.core.Transaction implements Parcelable{
public Transaction(org.ethereum.core.Transaction transaction) {
super(transaction.getEncoded());
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeByteArray(getEncoded());
}
public static final Parcelable.Creator<Transaction> CREATOR = new Parcelable.Creator<Transaction>() {
public Transaction createFromParcel(Parcel in) {
return new Transaction(in);
}
public Transaction[] newArray(int size) {
return new Transaction[size];
}
};
private Transaction(Parcel in) {
super(in.createByteArray());
}
}

View File

@ -0,0 +1,10 @@
package org.ethereum.android.service;
import android.os.Message;
public interface ConnectorHandler {
boolean handleMessage(Message message);
String getID();
}

View File

@ -0,0 +1,101 @@
package org.ethereum.android.service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import org.ethereum.android.jsonrpc.JsonRpcServer;
import org.ethereum.android.manager.BlockLoader;
import org.ethereum.android.interop.*;
import java.util.ArrayList;
public class EthereumAidlService extends EthereumService {
protected ArrayList<IListener> clientListeners = new ArrayList<>();
public static String log = "";
IEthereumService.Stub binder = null;
public EthereumAidlService() {
initializeBinder();
}
protected void initializeBinder() {
binder = new IEthereumService.Stub() {
public void loadBlocks(String dumpFile) throws RemoteException {
BlockLoader blockLoader = (BlockLoader)ethereum.getBlockLoader();
blockLoader.loadBlocks(dumpFile);
}
public void connect(String ip, int port, String remoteId) throws RemoteException {
if (!isConnected) {
System.out.println("Connecting to : " + ip);
ethereum.connect(ip, port, remoteId);
isConnected = true;
} else {
System.out.println("Already connected");
System.out.println("x " + ethereum.isConnected());
}
}
public void addListener(IListener listener) throws RemoteException {
clientListeners.clear();
clientListeners.add(listener);
}
public void removeListener(IListener listener) throws RemoteException {
try {
clientListeners.remove(listener);
} catch (Exception e) {
System.out.println("ERRORRRR: " + e.getMessage());
}
}
public void startJsonRpcServer() throws RemoteException {
jsonRpcServer = new JsonRpcServer(ethereum);
}
public void getLog(IAsyncCallback callback) throws RemoteException {
callback.handleResponse(EthereumAidlService.log);
}
};
}
@Override
protected void broadcastMessage(String message) {
for (IListener listener: clientListeners) {
try {
listener.trace(message);
} catch (Exception e) {
// Remove listener
System.out.println("ERRORRRR: " + e.getMessage());
clientListeners.remove(listener);
}
}
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}

View File

@ -0,0 +1,39 @@
package org.ethereum.android.service;
public class EthereumClientMessage {
/**
* Send online peer to the client ("peerInfo" => PeerInfo)
*/
public static final int MSG_ONLINE_PEER = 1;
/**
* Send peers to the client ("peers" => PeerInfo[])
*/
public static final int MSG_PEERS = 2;
/**
* Send blockchain status to the client ("status" => "Loaded/Loading")
*/
public static final int MSG_BLOCKCHAIN_STATUS = 3;
/**
* Send ethereum connection status to the client
*/
public static final int MSG_CONNECTION_STATUS = 4;
/**
* Send submitted transaction to client
*/
public static final int MSG_SUBMIT_TRANSACTION_RESULT = 5;
/**
* Send admin info to client
*/
public static final int MSG_ADMIN_INFO = 6;
/**
* Send peers to the client
*/
public static final int MSG_PENDING_TRANSACTIONS = 7;
}

View File

@ -0,0 +1,391 @@
package org.ethereum.android.service;
import android.content.Context;
import android.os.Bundle;
import android.os.Message;
import android.os.RemoteException;
import org.ethereum.core.Transaction;
import org.ethereum.net.peerdiscovery.PeerInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EthereumConnector extends ServiceConnector {
private static final Logger logger = LoggerFactory.getLogger("EthereumConnector");
public EthereumConnector(Context context, Class serviceClass) {
super(context, serviceClass);
}
/**
* Connect ethereum to peer
* @param ip String Peer ip address
* @param port int Peer port
* @param remoteId String Peer remote id
*
* Sends message parameters ( "key": type [description] ):
* {
* "ip": String [Peer ip address]
* "port": int [Peer port]
* "remoteId": String [Peer remote id]
* }
*/
public void connect(String ip, int port, String remoteId) {
if (!isBound)
return;
Message msg = Message.obtain(null, EthereumServiceMessage.MSG_CONNECT, 0, 0);
Bundle data = new Bundle();
data.putString("ip", ip);
data.putInt("port", port);
data.putString("remoteId", remoteId);
msg.setData(data);
try {
serviceMessenger.send(msg);
} catch (RemoteException e) {
logger.error("Exception sending message(connect) to service: " + e.getMessage());
}
}
/**
* Load blocks from dump file
* @param dumpFile String Blocks dump file path
*
* Sends message parameters ( "key": type [description] ):
* {
* "dumpFile": String [Blocks dump file paths]
* }
*/
public void loadBlocks(String dumpFile) {
if (!isBound)
return;
Message msg = Message.obtain(null, EthereumServiceMessage.MSG_LOAD_BLOCKS, 0, 0);
Bundle data = new Bundle();
data.putString("dumpFile", dumpFile);
msg.setData(data);
try {
serviceMessenger.send(msg);
} catch (RemoteException e) {
logger.error("Exception sending message(loadBlocks) to service: " + e.getMessage());
}
}
/**
* Start the json rpc server
*
* Sends message parameters: none
*/
public void startJsonRpc() {
if (!isBound)
return;
Message msg = Message.obtain(null, EthereumServiceMessage.MSG_START_JSON_RPC_SERVER, 0, 0);
try {
serviceMessenger.send(msg);
} catch (RemoteException e) {
logger.error("Exception sending message(startJsonRpc) to service: " + e.getMessage());
}
}
/**
* Find an online peer
* @param identifier String Caller identifier used to return the response
*
* Sends message parameters: none
*/
public void findOnlinePeer(String identifier) {
if (!isBound)
return;
Message msg = Message.obtain(null, EthereumServiceMessage.MSG_FIND_ONLINE_PEER, 0, 0);
msg.replyTo = clientMessenger;
msg.obj = getIdentifierBundle(identifier);
try {
serviceMessenger.send(msg);
} catch (RemoteException e) {
logger.error("Exception sending message(findOnlinePeer1) to service: " + e.getMessage());
}
}
/**
* Find an online peer
* @param identifier String Caller identifier used to return the response
* @param excludePeer PeerInfo Excluded peer from search
*
* Sends message parameters ( "key": type [description] ):
* {
* "excludePeer": Parcelable(PeerInfo) [Exclude peer from search]
* }
*/
public void findOnlinePeer(String identifier, PeerInfo excludePeer) {
if (!isBound)
return;
Message msg = Message.obtain(null, EthereumServiceMessage.MSG_FIND_ONLINE_PEER, 0, 0);
msg.replyTo = clientMessenger;
msg.obj = getIdentifierBundle(identifier);
Bundle data = new Bundle();
data.putParcelable("excludePeer", (org.ethereum.android.interop.PeerInfo)excludePeer);
msg.setData(data);
try {
serviceMessenger.send(msg);
} catch (RemoteException e) {
logger.error("Exception sending message(findOnlinePeer2) to service: " + e.getMessage());
}
}
/**
* Find an online peer
* @param identifier String Caller identifier used to return the response
* @param excludePeerSet PeerInfo[] Excluded peers from search
*
* Sends message parameters ( "key": type [description] ):
* {
* "excludePeerSet": ParcelableArray(PeerInfo[]) [Excluded peers from search]
* }
*/
public void findOnlinePeer(String identifier, PeerInfo[] excludePeerSet) {
if (!isBound)
return;
Message msg = Message.obtain(null, EthereumServiceMessage.MSG_FIND_ONLINE_PEER, 0, 0);
msg.replyTo = clientMessenger;
msg.obj = getIdentifierBundle(identifier);
Bundle data = new Bundle();
data.putParcelableArray("excludePeerSet", (org.ethereum.android.interop.PeerInfo[]) excludePeerSet);
msg.setData(data);
try {
serviceMessenger.send(msg);
} catch (RemoteException e) {
logger.error("Exception sending message(findOnlinePeer3) to service: " + e.getMessage());
}
}
/**
* Get etherum peers
* @param identifier String Caller identifier used to return the response
*
* Sends message parameters: none
*/
public void getPeers(String identifier) {
if (!isBound)
return;
Message msg = Message.obtain(null, EthereumServiceMessage.MSG_GET_PEERS, 0, 0);
msg.replyTo = clientMessenger;
msg.obj = getIdentifierBundle(identifier);
try {
serviceMessenger.send(msg);
} catch (RemoteException e) {
logger.error("Exception sending message(getPeers) to service: " + e.getMessage());
}
}
/**
* Starts ethereum peer discovery
*
* Sends message parameters: none
*/
public void startPeerDiscovery() {
if (!isBound)
return;
Message msg = Message.obtain(null, EthereumServiceMessage.MSG_START_PEER_DISCOVERY, 0, 0);
try {
serviceMessenger.send(msg);
} catch (RemoteException e) {
logger.error("Exception sending message(startPeerDiscovery) to service: " + e.getMessage());
}
}
/**
* Stops ethereum peer discovery
*
* Sends message parameters: none
*/
public void stopPeerDiscovery() {
if (!isBound)
return;
Message msg = Message.obtain(null, EthereumServiceMessage.MSG_LOAD_BLOCKS, 0, 0);
try {
serviceMessenger.send(msg);
} catch (RemoteException e) {
logger.error("Exception sending message(stopPeerDiscovery) to service: " + e.getMessage());
}
}
protected Bundle getIdentifierBundle(String identifier) {
Bundle bundle = new Bundle();
bundle.putString("identifier", identifier);
return bundle;
}
/**
* Gets the blockchain status
* @param identifier String Caller identifier used to return the response
*
* Sends message parameters: none
*/
public void getBlockchainStatus(String identifier) {
if (!isBound)
return;
Message msg = Message.obtain(null, EthereumServiceMessage.MSG_GET_BLOCKCHAIN_STATUS, 0, 0);
msg.replyTo = clientMessenger;
msg.obj = getIdentifierBundle(identifier);
try {
serviceMessenger.send(msg);
} catch (RemoteException e) {
logger.error("Exception sending message(getBlockchainStatus) to service: " + e.getMessage());
}
}
/**
* Add ethereum event listener
* @param identifier String Caller identifier used to return the response
*
* Sends message parameters: none
*/
public void addListener(String identifier) {
if (!isBound)
return;
Message msg = Message.obtain(null, EthereumServiceMessage.MSG_ADD_LISTENER, 0, 0);
msg.replyTo = clientMessenger;
msg.obj = getIdentifierBundle(identifier);
// TODO: Add event flags to message
try {
serviceMessenger.send(msg);
} catch (RemoteException e) {
logger.error("Exception sending message(addListener) to service: " + e.getMessage());
}
}
/**
* Closes ethereum
*
* Sends message parameters: none
*/
public void closeEthereum() {
if (!isBound)
return;
Message msg = Message.obtain(null, EthereumServiceMessage.MSG_CLOSE, 0, 0);
try {
serviceMessenger.send(msg);
} catch (RemoteException e) {
logger.error("Exception sending message(closeEthereum) to service: " + e.getMessage());
}
}
/**
* Get connection status
* @param identifier String Caller identifier used to return the response
*
* Sends message parameters: none
*/
public void getConnectionStatus(String identifier) {
if (!isBound)
return;
Message msg = Message.obtain(null, EthereumServiceMessage.MSG_GET_CONNECTION_STATUS, 0, 0);
msg.replyTo = clientMessenger;
msg.obj = getIdentifierBundle(identifier);
try {
serviceMessenger.send(msg);
} catch (RemoteException e) {
logger.error("Exception sending message(getConnectionStatus) to service: " + e.getMessage());
}
}
/**
* Submit ethereum transaction
* @param identifier String Caller identifier used to return the response
* @param transaction Transaction Transaction to submit
*
* Sends message parameters ( "key": type [description] ):
* {
* "transaction": Parcelable(Transaction) [transaction to submit]
* }
*/
public void submitTransaction(String identifier, Transaction transaction) {
if (!isBound)
return;
Message msg = Message.obtain(null, EthereumServiceMessage.MSG_SUBMIT_TRANSACTION, 0, 0);
msg.replyTo = clientMessenger;
msg.obj = getIdentifierBundle(identifier);
Bundle data = new Bundle();
data.putParcelable("transaction", (org.ethereum.android.interop.Transaction)transaction);
msg.setData(data);
try {
serviceMessenger.send(msg);
} catch (RemoteException e) {
logger.error("Exception sending message(submitTransaction) to service: " + e.getMessage());
}
}
/**
* Get admin info
* @param identifier String Caller identifier used to return the response
*
* Sends message parameters: none
*/
public void getAdminInfo(String identifier) {
if (!isBound)
return;
Message msg = Message.obtain(null, EthereumServiceMessage.MSG_GET_ADMIN_INFO, 0, 0);
msg.replyTo = clientMessenger;
msg.obj = getIdentifierBundle(identifier);
try {
serviceMessenger.send(msg);
} catch (RemoteException e) {
logger.error("Exception sending message(getAdminInfo) to service: " + e.getMessage());
}
}
/**
* Get pending transactions
* @param identifier String Caller identifier used to return the response
*
* Sends message parameters: none
*/
public void getPendingTransactions(String identifier) {
if (!isBound)
return;
Message msg = Message.obtain(null, EthereumServiceMessage.MSG_GET_PENDING_TRANSACTIONS, 0, 0);
msg.replyTo = clientMessenger;
msg.obj = getIdentifierBundle(identifier);
try {
serviceMessenger.send(msg);
} catch (RemoteException e) {
logger.error("Exception sending message(getPendingTransactions) to service: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,542 @@
package org.ethereum.android.service;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import org.ethereum.android.jsonrpc.JsonRpcServer;
import org.ethereum.android.manager.BlockLoader;
import org.ethereum.core.Transaction;
import org.ethereum.facade.Ethereum;
import org.ethereum.manager.AdminInfo;
import org.ethereum.net.peerdiscovery.PeerInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static org.ethereum.config.SystemProperties.CONFIG;
public class EthereumRemoteService extends EthereumService {
private static final Logger logger = LoggerFactory.getLogger("EthereumRemoteService");
ArrayList<Messenger> clientListeners = new ArrayList<>();
public EthereumRemoteService() {
super();
}
/** Handles incoming messages from clients. */
static class IncomingHandler extends Handler {
private final WeakReference<EthereumRemoteService> service;
IncomingHandler(EthereumRemoteService service) {
this.service = new WeakReference<EthereumRemoteService>(service);
}
@Override
public void handleMessage(Message message) {
EthereumRemoteService service = this.service.get();
if (service != null) {
if (!service.handleMessage(message)) {
super.handleMessage(message);
}
}
}
}
/**
* Target we publish for clients to send messages to IncomingHandler.Note
* that calls to its binder are sequential!
*/
final Messenger serviceMessenger = new Messenger(new IncomingHandler(this));
/**
* When binding to the service, we return an interface to our messenger for
* sending messages to the service.
*/
@Override
public IBinder onBind(Intent intent) {
return serviceMessenger.getBinder();
}
@Override
public void onCreate() {
super.onCreate();
}
protected boolean handleMessage(Message message) {
switch (message.what) {
case EthereumServiceMessage.MSG_CONNECT:
connect(message);
break;
case EthereumServiceMessage.MSG_LOAD_BLOCKS:
loadBlocks(message);
break;
case EthereumServiceMessage.MSG_START_JSON_RPC_SERVER:
startJsonRpc(message);
break;
case EthereumServiceMessage.MSG_FIND_ONLINE_PEER:
findOnlinePeer(message);
break;
case EthereumServiceMessage.MSG_GET_PEERS:
getPeers(message);
break;
case EthereumServiceMessage.MSG_START_PEER_DISCOVERY:
startPeerDiscovery(message);
break;
case EthereumServiceMessage.MSG_STOP_PEER_DISCOVERY:
stopPeerDiscovery(message);
break;
case EthereumServiceMessage.MSG_GET_BLOCKCHAIN_STATUS:
getBlockchainStatus(message);
break;
case EthereumServiceMessage.MSG_ADD_LISTENER:
addListener(message);
break;
case EthereumServiceMessage.MSG_GET_CONNECTION_STATUS:
getConnectionStatus(message);
break;
case EthereumServiceMessage.MSG_CLOSE:
closeEthereum(message);
break;
case EthereumServiceMessage.MSG_SUBMIT_TRANSACTION:
submitTransaction(message);
break;
case EthereumServiceMessage.MSG_GET_ADMIN_INFO:
getAdminInfo(message);
break;
case EthereumServiceMessage.MSG_GET_PENDING_TRANSACTIONS:
getPendingTransactions(message);
break;
default:
return false;
}
return true;
}
/**
* Connect to peer
*
* Incoming message parameters ( "key": type [description] ):
* {
* "ip": String [peer ip address]
* "port": int [peer port]
* "remoteId": String [peer remoteId]
* }
* Sends message: none
*/
protected void connect(Message message) {
if (!isConnected) {
isConnected = true;
new ConnectTask(message).execute(ethereum);
}
}
protected class ConnectTask extends AsyncTask<Ethereum, Message, Void> {
String ip;
int port;
String remoteId;
public ConnectTask(Message message) {
Bundle data = message.getData();
ip = data.getString("ip");
port = data.getInt("port");
remoteId = data.getString("remoteId");
}
protected Void doInBackground(Ethereum... args) {
Ethereum ethereum = args[0];
ethereum.connect(ip, port, remoteId);
logger.info("Ethereum connecting to : " + ip + ":" + port);
return null;
}
protected void onPostExecute(Void results) {
}
}
/**
* Load blocks from dump file
*
* Incoming message parameters ( "key": type [description] ):
* {
* "dumpFile": String [blocks dump file path]
* }
* Sends message: none
*/
protected void loadBlocks(Message message) {
if (!isConnected) {
isConnected = true;
new LoadBlocksTask(message).execute(ethereum);
}
}
protected class LoadBlocksTask extends AsyncTask<Ethereum, Message, Void> {
String dumpFile;
public LoadBlocksTask(Message message) {
Bundle data = message.getData();
dumpFile = data.getString("dumpFile");
}
protected Void doInBackground(Ethereum... args) {
Ethereum ethereum = args[0];
logger.info("Loading blocks from: " + dumpFile);
BlockLoader blockLoader = (BlockLoader)ethereum.getBlockLoader();
blockLoader.loadBlocks(dumpFile);
logger.info("Finished loading blocks from: " + dumpFile);
return null;
}
protected void onPostExecute(Void results) {
}
}
/**
* Start the json rpc server
*
* Incoming message parameters: none
* Sends message: none
*/
protected void startJsonRpc(Message message) {
// TODO: Maybe send back if it started succesfully ?
jsonRpcServer = new JsonRpcServer(ethereum);
try {
jsonRpcServer.start();
logger.info("Started json rpc server!");
} catch (Exception e) {
logger.error("Exception starting json rpc server: " + e.getMessage());
}
}
/**
* Find an online peer
*
* Incoming message parameters ( "key": type [description] ):
* {
* "excludePeer": Parcelable(PeerInfo) [peer to exclude from search]
* }
* Sends message parameters ( "key": type [description] ):
* {
* "peerInfo": Parcelable(PeerInfo) [found online peer, or null if error / online peer not found]
* }
*/
protected void findOnlinePeer(Message message) {
Bundle data = message.getData();
PeerInfo foundPeerInfo;
PeerInfo peerInfo = data.getParcelable("excludePeer");
if (peerInfo != null) {
foundPeerInfo = ethereum.findOnlinePeer(peerInfo);
} else {
PeerInfo[] excludePeerSet = (PeerInfo[])data.getParcelableArray("excludePeerSet");
if (excludePeerSet != null) {
foundPeerInfo = ethereum.findOnlinePeer(new HashSet<>(Arrays.asList(excludePeerSet)));
} else {
foundPeerInfo = ethereum.findOnlinePeer();
}
}
// Send reply
Message replyMessage = Message.obtain(null, EthereumClientMessage.MSG_ONLINE_PEER, 0, 0, message.obj);
Bundle replyData = new Bundle();
replyData.putParcelable("peerInfo", new org.ethereum.android.interop.PeerInfo(foundPeerInfo));
replyMessage.setData(replyData);
try {
message.replyTo.send(replyMessage);
logger.info("Sent online peer to client: " + foundPeerInfo.toString());
} catch (RemoteException e) {
logger.error("Exception sending online peer to client: " + e.getMessage());
}
}
/**
* Get etherum peers
*
* Incoming message parameters: none
* Sends message ( "key": type [description] ):
* {
* "peers": Parcelable[](PeerInfo[]) [ethereum peers]
* }
*/
protected void getPeers(Message message) {
Set<PeerInfo> peers = ethereum.getPeers();
Message replyMessage = Message.obtain(null, EthereumClientMessage.MSG_PEERS, 0, 0, message.obj);
Bundle replyData = new Bundle();
org.ethereum.android.interop.PeerInfo[] convertedPeers = new org.ethereum.android.interop.PeerInfo[peers.size()];
int index = 0;
for (PeerInfo peerInfo: peers) {
convertedPeers[index] = new org.ethereum.android.interop.PeerInfo(peerInfo);
index++;
}
replyData.putParcelableArray("peers", convertedPeers);
replyMessage.setData(replyData);
try {
message.replyTo.send(replyMessage);
logger.info("Sent peers to client: " + peers.size());
} catch (RemoteException e) {
logger.error("Exception sending peers to client: " + e.getMessage());
}
}
/**
* Starts ethereum peer discovery
* Incoming message parameters: none
* Sends message: none
*/
protected void startPeerDiscovery(Message message) {
ethereum.startPeerDiscovery();
logger.info("Started peer discovery.");
}
/**
* Stops ethereum peer discovery
* Incoming message parameters: none
* Sends message: none
*/
protected void stopPeerDiscovery(Message message) {
ethereum.stopPeerDiscovery();
logger.info("Stopped peer discovery.");
}
/**
* Gets the blockchain status
*
* Incoming message parameters: none
* Sends message ( "key": type [description] ):
* {
* "status": String [blockchain status: Loading/Loaded]
* }
*/
protected void getBlockchainStatus(Message message) {
boolean isLoading = ethereum.isBlockchainLoading();
String status = isLoading ? "Loading" : "Loaded";
Message replyMessage = Message.obtain(null, EthereumClientMessage.MSG_BLOCKCHAIN_STATUS, 0, 0, message.obj);
Bundle replyData = new Bundle();
replyData.putString("status", status);
replyMessage.setData(replyData);
try {
message.replyTo.send(replyMessage);
logger.info("Sent blockchain status: " + status);
} catch (RemoteException e) {
logger.error("Exception sending blockchain status to client: " + e.getMessage());
}
}
/**
* Add ethereum event listener
*
* Incoming message parameters: none
* Sends message: none
*/
protected void addListener(Message message) {
// Register the client's messenger
clientListeners.add(message.replyTo);
// TODO: Add channel listener types flags
logger.info("Client listener registered!");
}
/**
* Closes ethereum
*
* Incoming message parameters: none
* Sends message: none
*/
protected void closeEthereum(Message message) {
ethereum.close();
logger.info("Closed ethereum.");
}
/**
* Get connection status
*
* Incoming message parameters: none
* Sends message ( "key": type [description] ):
* {
* "status": String [ethereum connection status: Connected/Not Connected]
* }
*/
protected void getConnectionStatus(Message message) {
String status = ethereum.isConnected() ? "Connected" : "Not Connected";
Message replyMessage = Message.obtain(null, EthereumClientMessage.MSG_CONNECTION_STATUS, 0, 0, message.obj);
Bundle replyData = new Bundle();
replyData.putString("status", status);
replyMessage.setData(replyData);
try {
message.replyTo.send(replyMessage);
logger.info("Sent ethereum connection status: " + status);
} catch (RemoteException e) {
logger.error("Exception sending ethereum connection status to client: " + e.getMessage());
}
}
/**
* Submit ethereum transaction
*
* Incoming message parameters ( "key": type [description] ):
* {
* "transaction": Parcelable(Transaction) [ethereum transaction to submit]
* }
* Sends message ( "key": type [description] ):
* {
* "transaction": Parcelable(Transaction) [submitted transaction]
* }
*/
protected void submitTransaction(Message message) {
if (!isConnected) {
isConnected = true;
new SubmitTransactionTask(message).execute(ethereum);
} else {
logger.warn("Ethereum not connected.");
}
}
protected class SubmitTransactionTask extends AsyncTask<Ethereum, Void, Transaction> {
Transaction transaction;
Message message;
public SubmitTransactionTask(Message message) {
this.message = message;
Bundle data = message.getData();
transaction = data.getParcelable("transaction");
}
protected Transaction doInBackground(Ethereum... args) {
Transaction submitedTransaction = null;
try {
submitedTransaction = ethereum.submitTransaction(transaction).get(CONFIG.transactionApproveTimeout(), TimeUnit.SECONDS);
logger.info("Submitted transaction.");
} catch (Exception e) {
logger.error("Exception submitting transaction: " + e.getMessage());
}
return submitedTransaction;
}
protected void onPostExecute(Transaction submittedTransaction) {
Message replyMessage = Message.obtain(null, EthereumClientMessage.MSG_SUBMIT_TRANSACTION_RESULT, 0, 0, message.obj);
Bundle replyData = new Bundle();
replyData.putParcelable("transaction", new org.ethereum.android.interop.Transaction(submittedTransaction));
replyMessage.setData(replyData);
try {
message.replyTo.send(replyMessage);
logger.info("Sent submitted transaction: " + submittedTransaction.toString());
} catch (RemoteException e) {
logger.error("Exception sending submitted transaction to client: " + e.getMessage());
}
}
}
/**
* Get admin info
*
* Incoming message parameters: none
* Sends message ( "key": type [description] ):
* {
* "adminInfo": Parcelable(AdminInfo) [ethereum admin info]
* }
*/
protected void getAdminInfo(Message message) {
Message replyMessage = Message.obtain(null, EthereumClientMessage.MSG_ADMIN_INFO, 0, 0, message.obj);
Bundle replyData = new Bundle();
AdminInfo info = ethereum.getAdminInfo();
replyData.putParcelable("adminInfo", new org.ethereum.android.interop.AdminInfo(info));
replyMessage.setData(replyData);
try {
message.replyTo.send(replyMessage);
logger.info("Sent admin info: " + info.toString());
} catch (RemoteException e) {
logger.error("Exception sending admin info to client: " + e.getMessage());
}
}
/**
* Get pending transactions
*
* Incoming message parameters: none
* Sends message ( "key": type [description] ):
* {
* "transactions": ParcelableArray(Transaction[]) [ethereum pending transactions]
* }
*/
protected void getPendingTransactions(Message message) {
Message replyMessage = Message.obtain(null, EthereumClientMessage.MSG_PENDING_TRANSACTIONS, 0, 0, message.obj);
Bundle replyData = new Bundle();
Set<Transaction> transactions = ethereum.getPendingTransactions();
org.ethereum.android.interop.Transaction[] convertedTransactions = new org.ethereum.android.interop.Transaction[transactions.size()];
int index = 0;
for (Transaction transaction: transactions) {
convertedTransactions[index] = new org.ethereum.android.interop.Transaction(transaction);
index++;
}
replyData.putParcelableArray("transactions", convertedTransactions);
replyMessage.setData(replyData);
try {
message.replyTo.send(replyMessage);
logger.info("Sent pending transactions: " + transactions.size());
} catch (RemoteException e) {
logger.error("Exception sending pending transactions to client: " + e.getMessage());
}
}
}

View File

@ -1,52 +1,37 @@
package org.ethereum.android;
package org.ethereum.android.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import org.ethereum.android.di.components.DaggerEthereumComponent;
import org.ethereum.android.di.modules.EthereumModule;
import org.ethereum.android.interop.IListener;
import org.ethereum.android.jsonrpc.JsonRpcServer;
import org.ethereum.android.manager.BlockLoader;
import org.ethereum.config.SystemProperties;
import org.ethereum.core.*;
import org.ethereum.core.Transaction;
import org.ethereum.core.TransactionReceipt;
import org.ethereum.facade.Ethereum;
import org.ethereum.android.interop.*;
import org.ethereum.net.p2p.HelloMessage;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class EthereumAidlService extends Service {
protected Ethereum ethereum = null;
protected JsonRpcServer jsonRpcServer;
protected ArrayList<IListener> clientListeners = new ArrayList<>();
public static String log = "";
public class EthereumService extends Service {
boolean isConnected = false;
boolean isInitialized = false;
public EthereumAidlService() {
protected Ethereum ethereum = null;
protected JsonRpcServer jsonRpcServer;
public EthereumService() {
}
protected void broadcastMessage(String message) {
for (IListener listener: clientListeners) {
try {
listener.trace(message);
} catch (Exception e) {
// Remove listener
System.out.println("ERRORRRR: " + e.getMessage());
clientListeners.remove(listener);
}
}
}
@Override
@ -85,56 +70,10 @@ public class EthereumAidlService extends Service {
@Override
public IBinder onBind(Intent intent) {
return mBinder;
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
IEthereumService.Stub mBinder = new IEthereumService.Stub() {
public void loadBlocks(String dumpFile) throws RemoteException {
BlockLoader blockLoader = (BlockLoader)ethereum.getBlockLoader();
blockLoader.loadBlocks(dumpFile);
}
public void connect(String ip, int port, String remoteId) throws RemoteException {
if (!isConnected) {
System.out.println("Connecting to : " + ip);
ethereum.connect(ip, port, remoteId);
isConnected = true;
} else {
System.out.println("Already connected");
System.out.println("x " + ethereum.isConnected());
}
}
public void addListener(IListener listener) throws RemoteException {
clientListeners.clear();
clientListeners.add(listener);
}
public void removeListener(IListener listener) throws RemoteException {
try {
clientListeners.remove(listener);
} catch (Exception e) {
System.out.println("ERRORRRR: " + e.getMessage());
}
}
public void startJsonRpcServer() throws RemoteException {
jsonRpcServer = new JsonRpcServer(ethereum);
}
public void getLog(IAsyncCallback callback) throws RemoteException {
callback.handleResponse(EthereumAidlService.log);
}
};
protected class EthereumListener implements org.ethereum.listener.EthereumListener {
@Override

View File

@ -0,0 +1,76 @@
package org.ethereum.android.service;
public class EthereumServiceMessage {
/**
* Command to the service to connect to a peer
*/
public static final int MSG_CONNECT = 1;
/**
* Command to the service to load blocks dumpr
*/
public static final int MSG_LOAD_BLOCKS = 2;
/**
* Command to the service to start json rpc server
*/
public static final int MSG_START_JSON_RPC_SERVER = 3;
/**
* Command to the service to find an online peer
*/
public static final int MSG_FIND_ONLINE_PEER = 4;
/**
* Command to the service to list the peers
*/
public static final int MSG_GET_PEERS = 5;
/**
* Command to the service to start peer discovery
*/
public static final int MSG_START_PEER_DISCOVERY = 6;
/**
* Command to the service to stop peer discovery
*/
public static final int MSG_STOP_PEER_DISCOVERY = 7;
/**
* Command to the service to get blockchain status (Loading/Loaded)
*/
public static final int MSG_GET_BLOCKCHAIN_STATUS = 8;
/**
* Command to the service to add a listener
* <Not Implemented>
*/
public static final int MSG_ADD_LISTENER = 9;
/**
* Command to the service to get connection status (Connected/Not Connected)
*/
public static final int MSG_GET_CONNECTION_STATUS = 10;
/**
* Command to the service to close
*/
public static final int MSG_CLOSE = 11;
/**
* Command to the service to submit a transaction
*/
public static final int MSG_SUBMIT_TRANSACTION = 12;
/**
* Command to the service to get the admin info
*/
public static final int MSG_GET_ADMIN_INFO = 13;
/**
* Command to the service to get the pernding transactions
*/
public static final int MSG_GET_PENDING_TRANSACTIONS = 14;
}

View File

@ -0,0 +1,140 @@
package org.ethereum.android.service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import java.util.ArrayList;
public class ServiceConnector {
/**
* Incoming message handler. Calls to its binder are sequential!
*/
protected final IncomingHandler handler;
/**
* Handler thread to avoid running on the main UI thread
*/
protected final HandlerThread handlerThread;
/** Context of the activity from which this connector was launched */
protected Context context;
/** The class of the service we want to connect to */
protected Class serviceClass;
/** Flag indicating if the service is bound. */
boolean isBound;
/** Sends messages to the service. */
Messenger serviceMessenger = null;
/** Receives messages from the service. */
Messenger clientMessenger = null;
ArrayList<ConnectorHandler> handlers = new ArrayList<>();
/** Handles incoming messages from service. */
class IncomingHandler extends Handler {
public IncomingHandler(HandlerThread thread) {
super(thread.getLooper());
}
@Override
public void handleMessage(Message message) {
boolean isClaimed = false;
String identifier = ((Bundle)message.obj).getString("identifier");
if (identifier != null) {
for (ConnectorHandler handler : handlers) {
if (identifier.equals(handler.getID())) {
isClaimed = handler.handleMessage(message);
}
}
}
if (!isClaimed) {
super.handleMessage(message);
}
}
}
/**
* Class for interacting with the main interface of the service.
*/
protected ServiceConnection serviceConnection = null;
public ServiceConnector(Context context, Class serviceClass) {
this.context = context;
this.serviceClass = serviceClass;
handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();
handler = new IncomingHandler(handlerThread);
clientMessenger = new Messenger(handler);
initializeServiceConnection();
}
protected void initializeServiceConnection() {
serviceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the object we can use to
// interact with the service. We are communicating with the
// service using a Messenger, so here we get a client-side
// representation of that from the raw IBinder object.
serviceMessenger = new Messenger(service);
isBound = true;
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
serviceMessenger = null;
isBound = false;
}
};
}
/** Bind to the service */
public boolean bindService() {
if (serviceConnection != null) {
Intent intent = new Intent(context, serviceClass);
return context.getApplicationContext().bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
} else {
return false;
}
}
/** Unbind from the service */
public void unbindService() {
if (isBound && serviceConnection != null) {
context.getApplicationContext().unbindService(serviceConnection);
isBound = false;
}
}
public void registerHandler(ConnectorHandler handler) {
handlers.add(handler);
}
}

View File

@ -14,9 +14,9 @@ import javax.inject.Singleton;
public class AdminInfo {
private long startupTimeStamp;
private boolean consensus = true;
private List<Long> blockExecTime = new LinkedList<>();
protected long startupTimeStamp;
protected boolean consensus = true;
protected List<Long> blockExecTime = new LinkedList<>();
public AdminInfo() {
this.init();

View File

@ -14,9 +14,9 @@ import java.util.List;
*/
public class PeerInfo {
private InetAddress address;
private int port;
private String peerId;
protected InetAddress address;
protected int port;
protected String peerId;
private List<Capability> capabilities;
private HelloMessage handshakeHelloMessage;