Merge branch 'develop' of github.com:Agorise/graphenej into develop

develop
Nelson R. Perez 2018-11-07 17:45:30 -05:00
commit c76548cbee
2 changed files with 115 additions and 58 deletions

View File

@ -80,9 +80,10 @@ public class NetworkService extends Service {
private final String TAG = this.getClass().getName(); private final String TAG = this.getClass().getName();
public static final int NORMAL_CLOSURE_STATUS = 1000; public static final int NORMAL_CLOSURE_STATUS = 1000;
private static final int NO_HISTORY_CLOSURE_STATUS = 1001;
// Time to wait before retrying a connection attempt // Time to wait before retrying a connection attempt
private final int DEFAULT_RETRY_DELAY = 500; private static final int DEFAULT_RETRY_DELAY = 500;
// Default connection delay when using the node latency verification strategy. This initial // Default connection delay when using the node latency verification strategy. This initial
// delay is required in order ot make sure we have a fair selection of node latencies from // delay is required in order ot make sure we have a fair selection of node latencies from
@ -285,7 +286,17 @@ public class NetworkService extends Service {
@Nullable @Nullable
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
Bundle extras = intent.getExtras(); return mBinder;
}
/**
* Initialize information and try to connect to a node accordingly. This methods were moved
* from onBind to avoid crashes due to components other than {@link NetworkServiceManager}
* binding to the service without submitting the proper information.
*
* @param extras Bundle that contains all required information for a proper initialization
*/
public void bootstrapService(Bundle extras) {
// Retrieving credentials and requested API data from the shared preferences // Retrieving credentials and requested API data from the shared preferences
mUsername = extras.getString(NetworkService.KEY_USERNAME, ""); mUsername = extras.getString(NetworkService.KEY_USERNAME, "");
mPassword = extras.getString(NetworkService.KEY_PASSWORD, ""); mPassword = extras.getString(NetworkService.KEY_PASSWORD, "");
@ -328,7 +339,6 @@ public class NetworkService extends Service {
mHandler.postDelayed(mConnectAttempt, DEFAULT_INITIAL_DELAY); mHandler.postDelayed(mConnectAttempt, DEFAULT_INITIAL_DELAY);
} }
} }
return mBinder;
} }
/** /**
@ -556,6 +566,9 @@ public class NetworkService extends Service {
} else if (requestClass == GetFullAccounts.class) { } else if (requestClass == GetFullAccounts.class) {
Type GetFullAccountsResponse = new TypeToken<JsonRpcResponse<List<FullAccountDetails>>>(){}.getType(); Type GetFullAccountsResponse = new TypeToken<JsonRpcResponse<List<FullAccountDetails>>>(){}.getType();
parsedResponse = gson.fromJson(text, GetFullAccountsResponse); parsedResponse = gson.fromJson(text, GetFullAccountsResponse);
if(parsedResponse != null)
verifyNodeHasHistoryApi(parsedResponse);
} else if(requestClass == GetKeyReferences.class){ } else if(requestClass == GetKeyReferences.class){
Type GetKeyReferencesResponse = new TypeToken<JsonRpcResponse<List<List<UserAccount>>>>(){}.getType(); Type GetKeyReferencesResponse = new TypeToken<JsonRpcResponse<List<List<UserAccount>>>>(){}.getType();
parsedResponse = gson.fromJson(text, GetKeyReferencesResponse); parsedResponse = gson.fromJson(text, GetKeyReferencesResponse);
@ -578,6 +591,30 @@ public class NetworkService extends Service {
RxBus.getBusInstance().send(parsedResponse); RxBus.getBusInstance().send(parsedResponse);
} }
/**
* This method inspects the node response to find out if the totalOps is equal to zero,
* in that case the current connected node may not have the history plugin so we would need
* to close the connection and choose a different node.
*
* @param parsedResponse A JSONRpcResponse from a GetFullAccounts API call
*/
private void verifyNodeHasHistoryApi(JsonRpcResponse parsedResponse) {
if(parsedResponse.result instanceof List &&
((List) parsedResponse.result).size() > 0 &&
((List) parsedResponse.result).get(0) instanceof FullAccountDetails) {
FullAccountDetails fullAccountDetails = (FullAccountDetails) ((List) parsedResponse.result).get(0);
long totalOps = fullAccountDetails.getStatistics().total_ops;
if (totalOps == 0) {
Log.d(TAG, "The node returned 0 total_ops for current account and may not have installed the history plugin. " +
"Trying to connect to a different node.");
mWebSocket.close(NO_HISTORY_CLOSURE_STATUS, null);
}
}
}
/** /**
* Private method that will just broadcast a de-serialized notification to all interested parties * Private method that will just broadcast a de-serialized notification to all interested parties
* @param notification De-serialized notification * @param notification De-serialized notification
@ -636,20 +673,11 @@ public class NetworkService extends Service {
public void onClosed(WebSocket webSocket, int code, String reason) { public void onClosed(WebSocket webSocket, int code, String reason) {
super.onClosed(webSocket, code, reason); super.onClosed(webSocket, code, reason);
Log.d(TAG,"onClosed"); Log.d(TAG,"onClosed");
RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.DISCONNECTED, ApiAccess.API_NONE));
isLoggedIn = false; if (code == NO_HISTORY_CLOSURE_STATUS)
handleWebSocketDisconnection(true);
// Marking the selected node as not connected else
mSelectedNode.setConnected(false); handleWebSocketDisconnection(false);
// Updating the selected node's 'connected' status on the NodeLatencyVerifier instance
if(nodeLatencyVerifier != null)
nodeLatencyVerifier.updateActiveNodeInformation(mSelectedNode);
// We have currently no selected node
mSelectedNode = null;
} }
@Override @Override
@ -660,36 +688,26 @@ public class NetworkService extends Service {
for(StackTraceElement element : t.getStackTrace()){ for(StackTraceElement element : t.getStackTrace()){
Log.v(TAG,String.format("%s#%s:%s", element.getClassName(), element.getMethodName(), element.getLineNumber())); Log.v(TAG,String.format("%s#%s:%s", element.getClassName(), element.getMethodName(), element.getLineNumber()));
} }
// Registering current status
isLoggedIn = false;
mCurrentId = 0;
mApiIds.clear();
// If there is a response, we print it // If there is a response, we print it
if(response != null){ if(response != null){
Log.e(TAG,"Response: "+response.message()); Log.e(TAG,"Response: "+response.message());
} }
// Adding a very high latency value to this node in order to prevent handleWebSocketDisconnection(true);
// us from getting it again }
mSelectedNode.addLatencyValue(Long.MAX_VALUE);
nodeProvider.updateNode(mSelectedNode);
/**
* Method that encapsulates the behavior of handling a disconnection to the current node, and
* potentially tries to reconnect to another one.
*
* @param tryReconnection Variable that states if a reconnection to other node should be tried.
*/
private void handleWebSocketDisconnection(boolean tryReconnection) {
RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.DISCONNECTED, ApiAccess.API_NONE)); RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.DISCONNECTED, ApiAccess.API_NONE));
if(nodeProvider.getBestNode() == null){ isLoggedIn = false;
Log.e(TAG,"Giving up on connections");
stopSelf();
}else{
Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
@Override
public void run() {
Log.d(TAG,"Retrying");
connect();
}
}, DEFAULT_RETRY_DELAY);
}
// Marking the selected node as not connected // Marking the selected node as not connected
mSelectedNode.setConnected(false); mSelectedNode.setConnected(false);
@ -697,6 +715,33 @@ public class NetworkService extends Service {
if(nodeLatencyVerifier != null) if(nodeLatencyVerifier != null)
nodeLatencyVerifier.updateActiveNodeInformation(mSelectedNode); nodeLatencyVerifier.updateActiveNodeInformation(mSelectedNode);
if(tryReconnection) {
// Registering current status
mCurrentId = 0;
mApiIds.clear();
// Adding a very high latency value to this node in order to prevent
// us from getting it again
mSelectedNode.addLatencyValue(Long.MAX_VALUE);
nodeProvider.updateNode(mSelectedNode);
RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.DISCONNECTED, ApiAccess.API_NONE));
if (nodeProvider.getBestNode() == null) {
Log.e(TAG, "Giving up on connections");
stopSelf();
} else {
Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
@Override
public void run() {
Log.d(TAG, "Retrying");
connect();
}
}, DEFAULT_RETRY_DELAY);
}
}
// We have currently no selected node // We have currently no selected node
mSelectedNode = null; mSelectedNode = null;
} }

View File

@ -25,14 +25,14 @@ import cy.agorise.graphenej.stats.ExponentialMovingAverage;
*/ */
public class NetworkServiceManager implements Application.ActivityLifecycleCallbacks { public class NetworkServiceManager implements Application.ActivityLifecycleCallbacks {
private final static String TAG = "NetworkServiceManager"; private final String TAG = this.getClass().getName();
/** /**
* Constant used to specify how long will the app wait for another activity to go through its starting life * Constant used to specify how long will the app wait for another activity to go through its starting life
* cycle events before running the teardownConnectionTask task. * cycle events before running the teardownConnectionTask task.
* *
* This is used as a means to detect whether or not the user has left the app. * This is used as a means to detect whether or not the user has left the app.
*/ */
private final int DISCONNECT_DELAY = 1500; private static final int DISCONNECT_DELAY = 1500;
/** /**
* Handler instance used to schedule tasks back to the main thread * Handler instance used to schedule tasks back to the main thread
@ -72,8 +72,8 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
} }
}; };
public NetworkServiceManager(Context context){ private NetworkServiceManager(Context context){
mContextReference = new WeakReference<Context>(context); mContextReference = new WeakReference<>(context);
} }
@Override @Override
@ -89,27 +89,37 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
// Creating a new Intent that will be used to start the NetworkService // Creating a new Intent that will be used to start the NetworkService
Context context = mContextReference.get(); Context context = mContextReference.get();
Intent intent = new Intent(context, NetworkService.class); Intent intent = new Intent(context, NetworkService.class);
// Adding user-provided node URLs
StringBuilder stringBuilder = new StringBuilder();
Iterator<String> it = mCustomNodeUrls.iterator();
while(it.hasNext()){
stringBuilder.append(it.next());
if(it.hasNext()) stringBuilder.append(",");
}
String customNodes = stringBuilder.toString();
// Adding all
intent.putExtra(NetworkService.KEY_USERNAME, mUserName)
.putExtra(NetworkService.KEY_PASSWORD, mPassword)
.putExtra(NetworkService.KEY_REQUESTED_APIS, mRequestedApis)
.putExtra(NetworkService.KEY_NODE_URLS, customNodes)
.putExtra(NetworkService.KEY_AUTO_CONNECT, mAutoConnect)
.putExtra(NetworkService.KEY_ENABLE_LATENCY_VERIFIER, mVerifyLatency);
context.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); context.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
} }
} }
/**
* This method passes all the required information to the NetworkService to properly
* initialize itself
*/
private void passRequiredInfoToConfigureService() {
// Adding user-provided node URLs
StringBuilder stringBuilder = new StringBuilder();
Iterator<String> it = mCustomNodeUrls.iterator();
while(it.hasNext()){
stringBuilder.append(it.next());
if(it.hasNext()) stringBuilder.append(",");
}
String customNodes = stringBuilder.toString();
Bundle b = new Bundle();
// Adding all
b.putString(NetworkService.KEY_USERNAME, mUserName);
b.putString(NetworkService.KEY_PASSWORD, mPassword);
b.putInt(NetworkService.KEY_REQUESTED_APIS, mRequestedApis);
b.putString(NetworkService.KEY_NODE_URLS, customNodes);
b.putBoolean(NetworkService.KEY_AUTO_CONNECT, mAutoConnect);
b.putBoolean(NetworkService.KEY_ENABLE_LATENCY_VERIFIER, mVerifyLatency);
mService.bootstrapService(b);
}
@Override @Override
public void onActivityPaused(Activity activity) { public void onActivityPaused(Activity activity) {
mHandler.postDelayed(mDisconnectRunnable, DISCONNECT_DELAY); mHandler.postDelayed(mDisconnectRunnable, DISCONNECT_DELAY);
@ -133,6 +143,8 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
// We've bound to LocalService, cast the IBinder and get LocalService instance // We've bound to LocalService, cast the IBinder and get LocalService instance
NetworkService.LocalBinder binder = (NetworkService.LocalBinder) service; NetworkService.LocalBinder binder = (NetworkService.LocalBinder) service;
mService = binder.getService(); mService = binder.getService();
passRequiredInfoToConfigureService();
} }
@Override @Override