PendingResult.setResultCallback() always returns the statusCode success - android

if i connect my google watch with a mobile device successfully, and then disable the bluetooth connection (for test reasons) and make a google api client call to my mobile device, the pending result always returns the status code success, even if its not successfull because there is no more connection
async task for the request
class DataTask extends AsyncTask<Node, Void, Void> {
#Override
protected Void doInBackground(Node... nodes) {
Gson gson = new Gson();
Request requestObject = new Request();
requestObject.setType(Constants.REQUEST_TYPE);
String jsonString = gson.toJson(requestObject);
PutDataMapRequest dataMap = PutDataMapRequest.create(Constants.PATH_REQUEST);
dataMap.setUrgent();
dataMap.getDataMap().putString(Constants.KEY_REQUEST, jsonString);
PutDataRequest request = dataMap.asPutDataRequest();
DataApi.DataItemResult dataItemResult = Wearable.DataApi
.putDataItem(googleApiClient, request).await();
boolean connected = googleApiClient.isConnected();
PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi.putDataItem(googleApiClient, request);
pendingResult.setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
#Override
public void onResult(#NonNull DataApi.DataItemResult dataItemResult) {
com.google.android.gms.common.api.Status status = dataItemResult.getStatus();
DataItem dataItem = dataItemResult.getDataItem();
boolean dataValid = dataItemResult.getDataItem().isDataValid();
boolean canceled = status.isCanceled();
boolean interrupted = status.isInterrupted();
float statusCode = status.getStatusCode();
if(status.isSuccess()){ // expected to be false because there is no bluetooth connection anymore
Log.d(TAG, "Success");
}else{
Log.d(TAG, "Failure");
}
}
});
return null;
}
}
why do i not get a false for status.isSuccess?
the only solution i found is to write following code inside the AsyncTask:
Wearable.NodeApi.getConnectedNodes(googleApiClient).await().getNodes();
if(connectedNodes.size() == 0){
// no connection
}
is it not possible to check if the request was successfully inside the ResultCallback?

I believe that the getStatus() call for DataItemResult is only indicating whether the call was successfully passed off to the Data API, not whether it was successfully relayed to another node. The Data API is asynchronous - it's a "store and forward" architecture - so it's not reasonable to expect it to notify you immediately of successful delivery.
In fact, I don't think that there is a way to determine from the Data API when your DataItem has been delivered at all. Your getConnectedNodes technique is only telling you that the watch is connected, not that the data has been delivered. If you need proof of delivery, you'll probably have to implement that yourself, perhaps using the Message API.
One other note: given you've wrapped your code in an AsyncTask, there's no need to use PendingResult.setResultCallback. You can simply await the result inline: http://developer.android.com/training/wearables/data-layer/events.html#sync-waiting

Related

HTTP requests received in wrong time sequence

I am creating an Android app that sends http requests contains IMU data every 20ms using Handler and Runnable.
public void onClickLogData(View view){
Log.d(TAG,"onClickLogData");
final OkHttpClient client = new OkHttpClient();
Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
if (Running) {
handler.postDelayed(this, 20);
String url = "http://192.168.86.43:5000/server";
Log.d(TAG, String.valueOf(time));
RequestBody body = new FormBody.Builder()
.add("Timestamp", String.valueOf(time))
.add("accx", String.valueOf(accx))
.add("accy", String.valueOf(accy))
.add("accz", String.valueOf(accz))
.add("gyrox", String.valueOf(gyrox))
.add("gyroy", String.valueOf(gyroy))
.add("gyroz", String.valueOf(gyroz))
.add("magx", String.valueOf(magx))
.add("magy", String.valueOf(magy))
.add("magz", String.valueOf(magz))
.build();
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
final Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(#NonNull Call call, #NonNull IOException e) {
Log.i("onFailure", e.getMessage());
}
#Override
public void onResponse(#NonNull Call call, #NonNull Response response)
throws IOException {
assert response.body() != null;
String result = response.body().string();
Log.i("result", result);
}
});
} else {
handler.removeCallbacks(this);
}
}
};
handler.postDelayed(runnable, 1000);
}
And the data are received and stored on my laptop.
with open('imu.csv','w') as csv_file:
writer = csv.writer(csv_file)
writer.writerow(['Timestamp','accx','accy','accz','gyrox','gyroy','gyroz','magx','magy','magz'])
app = Flask(__name__)
#app.route('/server', methods=['GET','POST'])
def server():
r = request.form
data = r.to_dict(flat=False)
t = int(str(data['Timestamp'])[2:-2])
print(t)
accx = float(str(data['accx'])[2:-2])
accy = float(str(data['accy'])[2:-2])
accz = float(str(data['accz'])[2:-2])
gyrox = float(str(data['gyrox'])[2:-2])
gyroy = float(str(data['gyroy'])[2:-2])
gyroz = float(str(data['gyroz'])[2:-2])
magx = float(str(data['magx'])[2:-2])
magy = float(str(data['magy'])[2:-2])
magz = float(str(data['magz'])[2:-2])
imu_data = [t,accx,accy,accz,gyrox,gyroy,gyroz,magx,magy,magz]
with open('imu.csv','a+') as csv_file:
writer = csv.writer(csv_file)
writer.writerow(imu_data)
return("ok")
if __name__ == '__main__':
app.run(host='0.0.0.0')
The requests are sent in chronological order on Android side as Log indicates, however on the receiving side many of the requests are received in wrong time sequence. enter image description here
It seems that this happens more frequently as time goes. What possibly could be the cause of this and where should I be looking at?
All sorts of things. Requests are sent over a network. They can take different paths to get there each time. Requests can even get lost. Using TCP you'd automatically resend a lost request, but then it would be even more out of order. They can be delayed in the network in different bridges and routers. There is no promise over the internet that different requests will be received in order. That's only a promise over a single socket using TCP- and that is only possible with a lot of work (basically keeping track of every packet sent and received and waiting until you have them in order to send it to the app). If your architecture requires you to receive them in order, your architecture cannot possibly work over the internet.
If you do need an ordering on the server, either embed a request number that's monotonically increasing, or embed a timestamp in the request.

retrofit enqueue stop and exit suddenly

I'm using Retrofit enqueue to call CodeIgniter Restful API and get subscription message then move to next screen.
inside subscription API method I'm calling SMS and Email services.
if I use only SMS or Email during subscription then program works fine and enqueue OnResponse method being executed. BUT if I use both SMS and Email in same subscription function then enqueue call will exit without executing OnResponse or OnFailure.
I have tried to increase max_execution_time and max_input_time but without success. I don't know what the reason cause my application to exit without executing OnResponse/ OnFailure methods.
also I couldn't trace logs to know the issue because OnFailure is not executed.
I'm using Centos 8 and Apache as web server.
here is the code in CodeIgniter Restful API that insert user to database, then send SMS & email:
$data['user_name']='essa';
$mobnumber = $this->trim_number($data["phone"]);
$data['otp'] = $this->random_number();
$data['otp_status'] = '0';
$this->services_model->insert_data('subscribers',$data);
$id = $this->db->insert_id();
$this->sendsms($mobnumber ,$data['otp']);
$template_data['user_name'] = $data['username'];
$template_data['userid'] = base64_encode($id);
$em_message = $this->parser->parse("subscription.html", $template_data, TRUE);
$email_result = send_mail($data['email'],'registration',$em_message);
$result = ["status"=>1,"message"=>'Registration successful'];
$this->response($result,REST_Controller::HTTP_OK);
and here is my Retrofit.enqueue code:
retrofitCall .enqueue(new Callback<JsonElement>() {
#Override
public void onResponse(Call<JsonElement> call, Response<JsonElement> response)
{
if (response.isSuccessful())
{
try {
JSONObject jsonObject = new JSONObject(response.body().toString());
int status = jsonObject.getInt("status");
String message = jsonObject.getString("message");
showToast(message);
Intent i = new Intent(Welcome.this, Welcome.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK);
i.putExtra("message",message);
startActivity(i);
} catch (Exception e) {
e.printStackTrace();
}
}
}
#Override
public void onFailure(Call<JsonElement> call, Throwable t) {
Log.e("retrofiError",t.toString());
}
});

httpClient.PostAsync crashes app in Xamarin Android JobService

I have an Android Xamarin app that handles notifications. When a notification is displayed, there are buttons that ask for a response. The app needs to send this response back to a server via an httpClient.PostAsync call. I am using the same http client wrapper in other parts of the code and it is working correctly. However, when I call it from the JobService code, the app crashes. I have enclosed the http call in a try/catch and no exception occurs. There are also no errors in the device log. i would like to know how to debug this. Here is my flow:
I have a class that derives from FirebaseMessagingService with an OnMessageReceived method. That gets called when a notification arrives. I build a local notification via the notification manager and call .Notify. The notification appears with the buttons. I have a BroadcastReceiver with an OnReceive method. That method schedules a job to do the post back of the button click. The job gets started and runs until the point I call the PostAsync. From there it crashes with no exception. Here is the relevant part of the JobWorker:
public override bool OnStartJob(JobParameters jobParams)
{
_backgroundWorker = Task.Run(() => { DoWork(jobParams); });
return true;
}
private void DoWork(JobParameters jobParams)
{
var logger = App.ResolveDependency<ILogger>() as ILogger;
var callActions = App.ResolveDependency<ICallActionsHandler>() as ICallActionsHandler;
var callToken = jobParams.Extras.GetString(JobParameterCallToken);
var subsciberPhoneNumber = jobParams.Extras.GetString(JobParameterSubscriberPhoneNumber);
var action = jobParams.Extras.GetString(JobParametersCallAction);
logger.TraceInfo($"starting {nameof(CallActionService)}: starting job {jobParams.JobId}");
callActions.SendAction(
callToken,
subsciberPhoneNumber,
(CallActions)Enum.Parse(typeof(CallActions), action));
}
The SendAction code calls the http client wrapper. The http client wrapper code looks like this:
public async Task<int> PostAsync(string api, object message)
{
var apiUrl = Constants.DefaultAppApi + api;
var contentText = JsonConvert.SerializeObject(message);
var content = new StringContent(contentText, Encoding.UTF8, "application/json");
var backOff = 10;
var retryCount = 5;
HttpResponseMessage response = null;
for (var attempt = 1; attempt <= retryCount; attempt++)
{
_logger.TraceInfo($"DataServerClient Post message: {message.GetType().Name}, attempt = {attempt}");
try
{
response = await _client.PostAsync(apiUrl, content);
}
catch (Exception ex)
{
if (attempt == retryCount)
_logger.TraceException($"DataServerClient Post failed", ex);
}
if (response != null && response.IsSuccessStatusCode)
{
_logger.TraceInfo($"DataServerClient post was successful at retry count: {attempt}");
break;
}
backOff *= 2;
await Task.Delay(backOff);
}
return (int)response.StatusCode;
}
Can anyone provide clues for why this is failing or how I can gather diagnostics to find out what is happening? As I mentioned, the exception is not caught, the task that I create gets marked as completed, and no message gets posted.

consumeAsync Return Error Code 5: Developer Error

Edited with more information.
I am getting error code 5 which I believe is 'Developer Error'. The mDebugMessage in the BillingResult is Invalid Token. (I have checked the name and I am sure it is correct.)
I am calling consumeAsync("noadverts"); where "noadverts" is the name of my in app product.
When I check my purchases, it records the following:
{"orderId":"GPA.1111-1111-1111-1111",
"packageName":"myname.com.myname",
"productId":"noadverts",
"purchaseTime":1565188785096,
"purchaseState":0,
"purchaseToken":"edited for length",
"acknowledged":true}
Unusually, when I call:
int val1 = purchase.getPurchaseState();
then val1 is 1 and not 0 as it suggested in the purchase JSON.
I am using much of the TestDrive example, but without the BillingUpdatesListener.
public void consumeAsync(final String purchaseToken) {
// If we've already scheduled to consume this token - no action is needed (this could happen
// if you received the token when querying purchases inside onReceive() and later from
// onActivityResult()
if (mTokensToBeConsumed == null) {
mTokensToBeConsumed = new HashSet<>();
} else if (mTokensToBeConsumed.contains(purchaseToken)) {
//Log.i(TAG, "Token was already scheduled to be consumed - skipping...");
return;
}
mTokensToBeConsumed.add(purchaseToken);
// Generating Consume Response listener
final ConsumeResponseListener onConsumeListener = new ConsumeResponseListener() {
#Override
public void onConsumeResponse(BillingResult response, String purchaseToken) {
#BillingClient.BillingResponseCode int responseCode = response.getResponseCode();
// THIS IS WHERE I GET ERROR CODE 5
}
};
// Creating a runnable from the request to use it inside our connection retry policy below
Runnable consumeRequest = new Runnable() {
#Override
public void run() {
// Consume the purchase async
//mBillingClient.consumeAsync(purchaseToken, onConsumeListener);
ConsumeParams consumeParams = ConsumeParams.newBuilder().setPurchaseToken(purchaseToken).build();
// Consume the purchase async
mBillingClient.consumeAsync(consumeParams, onConsumeListener);
}
};
executeServiceRequest(consumeRequest);
}
Any ideas what might be the issue?
If I try and purchase "noadverts" again I get the error code Item is already owned.

Android - Retrofit 2 - Authenticator Result

I'm trying to use Retrofit (2.0.0-beta3), but when using an Authenticator to add a token, I can't seem to get the data from the synchronous call. Our logging on the back-end just shows a lot of login attempts, but I can't get the data from the body to actually add to the header.
public static class TokenAuthenticator implements Authenticator {
#Override
public Request authenticate(Route route, Response response) throws IOException {
// Refresh your access_token using a synchronous api request
UserService userService = createService(UserService.class);
Call<Session> call = userService.emailLogin(new Credentials("handle", "pass"));
// This call is made correctly, as it shows up on the back-end.
Session body = call.execute().body();
// This line is never hit.
Logger.d("Session token: " + body.token);
// Add new header to rejected request and retry it
return response.request().newBuilder()
.header("Auth-Token", body.token)
.build();
}
}
I'm not exactly too sure on why it's not even printing anything out. Any tips on how to solve this issue would be greatly appreciated, thanks for taking the time to help.
These are the sources I've been reading on how to implement Retrofit.
Using Authenticator:
https://stackoverflow.com/a/31624433/3106174
https://github.com/square/okhttp/wiki/Recipes#handling-authentication
Making synchronous calls with Retrofit 2:
https://futurestud.io/blog/retrofit-synchronous-and-asynchronous-requests
I managed to get a decent solution using the TokenAuthenticator and an Interceptor and thought I'd share the idea as it may help some others.
Adding the 'TokenInterceptor' class that handles adding the token to the header is the token exists, and the 'TokenAuthenticator' class handles the case when there is no token, and we need to generate one.
I'm sure there are some better ways to implement this, but it's a good starting point I think.
public static class TokenAuthenticator implements Authenticator {
#Override
public Request authenticate( Route route, Response response) throws IOException {
...
Session body = call.execute().body();
Logger.d("Session token: " + body.token);
// Storing the token somewhere.
session.token = body.token;
...
}
private static class TokenInterceptor implements Interceptor {
#Override
public Response intercept( Chain chain ) throws IOException {
Request originalRequest = chain.request();
// Nothing to add to intercepted request if:
// a) Authorization value is empty because user is not logged in yet
// b) There is already a header with updated Authorization value
if (authorizationTokenIsEmpty() || alreadyHasAuthorizationHeader(originalRequest)) {
return chain.proceed(originalRequest);
}
// Add authorization header with updated authorization value to intercepted request
Request authorisedRequest = originalRequest.newBuilder()
.header("Auth-Token", session.token )
.build();
return chain.proceed(authorisedRequest);
}
}
Source:
http://lgvalle.xyz/2015/07/27/okhttp-authentication/
I have similar authenticator and it works with 2.0.0-beta2.
If you get lots of login attempts from you Authenticator, I suggest make sure that when you make the synchronous call, you are not using Authenticator with that call.
That could end up in loop, if also your "emailLogin" fails.
Also I would recommend adding loggingInterceptor to see all trafic to server: Logging with Retrofit 2
I know it's a late answer but for anyone still wondering how to Add / Refresh token with Retrofit 2 Authenticator, here is a working solution:
Note: preferenceHelper is your Preference Manager class where you set/get your shared preferences.
public class AuthenticationHelper implements Authenticator {
private static final String HEADER_AUTHORIZATION = "Authorization";
private static final int REFRESH_TOKEN_FAIL = 403;
private Context context;
AuthenticationHelper(#ApplicationContext Context context) {
this.context = context;
}
#Override
public Request authenticate(#NonNull Route route, #NonNull Response response) throws IOException {
// We need to have a token in order to refresh it.
String token = preferencesHelper.getAccessToken();
if (token == null)
return null;
synchronized (this) {
String newToken = preferencesHelper.getAccessToken();
if (newToken == null)
return null;
// Check if the request made was previously made as an authenticated request.
if (response.request().header(HEADER_AUTHORIZATION) != null) {
// If the token has changed since the request was made, use the new token.
if (!newToken.equals(token)) {
return response.request()
.newBuilder()
.removeHeader(HEADER_AUTHORIZATION)
.addHeader(HEADER_AUTHORIZATION, "Bearer " + newToken)
.build();
}
JsonObject refreshObject = new JsonObject();
refreshObject.addProperty("refreshToken", preferencesHelper.getRefreshToken());
retrofit2.Response<UserToken> tokenResponse = apiService.refreshToken(refreshObject).execute();
if (tokenResponse.isSuccessful()) {
UserToken userToken = tokenResponse.body();
if (userToken == null)
return null;
preferencesHelper.saveAccessToken(userToken.getToken());
preferencesHelper.saveRefreshToken(userToken.getRefreshToken());
// Retry the request with the new token.
return response.request()
.newBuilder()
.removeHeader(HEADER_AUTHORIZATION)
.addHeader(HEADER_AUTHORIZATION, "Bearer " + userToken.getToken())
.build();
} else {
if (tokenResponse.code() == REFRESH_TOKEN_FAIL) {
logoutUser();
}
}
}
}
return null;
}
private void logoutUser() {
// logout user
}
}
Also note:
preferenceHelper and apiService needs to be provided in some way.
This is not an example that will work for all systems and api's but an example in how adding and refreshing the token should be done using Retrofit 2 Authenticator

Categories

Resources