Android Amazon S3 Uploading Crash - android

I'm trying to figure out what is the cause of this crash when uploading on Amazon S3 bucket.
Log is:
Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean com.amazonaws.mobileconnectors.s3.transferutility.TransferService$NetworkInfoReceiver.isNetworkConnected()' on a null object reference
at com.amazonaws.mobileconnectors.s3.transferutility.TransferService.execCommand(TransferService.java:287)
at com.amazonaws.mobileconnectors.s3.transferutility.TransferService$UpdateHandler.handleMessage(TransferService.java:224)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.os.HandlerThread.run(HandlerThread.java:61)
Is there is something wrong on my code?
public AmazonTransferUtility uploadFileToAmazonS3(String data, Date date){
generateTextFileFromString(data, date);
File jsonFile = new File(getDataPath(), textName);
TransferObserver observer = transferUtility.upload(
textBucketName,
mUUID + File.separator + date.getTime() + textName ,
jsonFile
);
mListener.onAsyncStart();
observer.setTransferListener(new TransferListener() {
#Override
public void onStateChanged(int id, TransferState state) {
try {
if (state.toString().equals("COMPLETED")) {
deleteFile(textName);
if (mListener != null) {
JSONObject result = new JSONObject();
result.put("result", state.toString());
mListener.onAsyncSuccess(result);
}
}
else if (state.toString().equals("FAILED") ||
state.toString().equals("UNKNOWN")
){
mListener.onAsyncFail(id, state.toString());
}
else{
Log.i(TAG, "S3 TransferState :" + state.toString());
}
}catch (JSONException e){
Log.e(TAG, e.getLocalizedMessage());
}
}
#Override
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
if (bytesCurrent == bytesTotal){
Log.i(TAG, "Completed");
}
else{
Log.i(TAG, "Current bytes: " + bytesCurrent + " Of bytesTotal : " + bytesTotal);
}
}
#Override
public void onError(int id, Exception ex) {
mListener.onAsyncFail(id,ex.getMessage());
}
});
return this;
}
And if ever how can I catch this error so that my app stop crashing and just cancel my uploading task.
BTW.
That crash is intermittent and ratio is 1 out of 5 successful sync

This doesn't appear to be something you are doing, it's inside the AWS SDK code. The implication of that NPE is a flaky network. It's been reported to Amazon on github (and confirmed in another ticket) and it appears rolling back one version in the SDK (v2.2.13) may help.
That also makes sense given the changes made in 2.2.14, which are related to S3 transfer and the network.
I'd suggest following those tickets (please don't +1). It's reasonable to expect they will fix it within a week.

Here's a workaround until the bug is fixed, just fire this up in your application's onCreate, or well before any upload activity starts:
/**
* work around for a bug:
* http://stackoverflow.com/questions/36587511/android-amazon-s3-uploading-crash
*/
public static void startupTranferServiceEarlyToAvoidBugs(Context context) {
final TransferUtility tu = new TransferUtility(
new AmazonS3Client((AWSCredentials)null),
context);
tu.cancel(Integer.MAX_VALUE - 1);
}
essentially what this does is tell the TransferService to start up and initialize it's member variables so that it doesn't enter the condition where it tries to service commands before it's ready to.

Related

Android store photos and documents from s3 bucket locally

I have a back-end that I have written with Laravel and I am currently writing and Android app which is doing calls to my back-end.
I have some png's and pdf's stored in s3 buckets in my aws account. I need to get the images and documents from the bucket and store them locally on the device as well as displaying them.
I also need to send new png's from the phone to be stored in the s3 bucket.
What is the best way to go around doing this. Are there any useful libraries. I have already added Picasso but that only helps with displaying the image not getting from/storing in the s3 bucket.
You can use the AWS Android SDK for S3. You can consume it in gradle via maven as:
dependencies {
compile 'com.amazonaws:aws-android-sdk-s3:2.6.+'
}
For example to upload a file to S3:
import android.app.Activity;
import android.util.Log;
import com.amazonaws.mobile.client.AWSMobileClient;
import com.amazonaws.mobileconnectors.s3.transferutility.TransferUtility;
import com.amazonaws.mobileconnectors.s3.transferutility.TransferState;
import com.amazonaws.mobileconnectors.s3.transferutility.TransferObserver;
import com.amazonaws.mobileconnectors.s3.transferutility.TransferListener;
import com.amazonaws.services.s3.AmazonS3Client;
import java.io.File;
public class YourActivity extends Activity {
public void uploadData() {
// Initialize AWSMobileClient if not initialized upon the app startup.
// AWSMobileClient.getInstance().initialize(this).execute();
TransferUtility transferUtility =
TransferUtility.builder()
.context(getApplicationContext())
.awsConfiguration(AWSMobileClient.getInstance().getConfiguration())
.s3Client(new AmazonS3Client(AWSMobileClient.getInstance().getCredentialsProvider()))
.build();
TransferObserver uploadObserver =
transferUtility.upload(
"s3Folder/s3Key.txt",
new File("/path/to/file/localFile.txt"));
uploadObserver.setTransferListener(new TransferListener() {
#Override
public void onStateChanged(int id, TransferState state) {
if (TransferState.COMPLETED == state) {
// Handle a completed upload.
}
}
#Override
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
float percentDonef = ((float)bytesCurrent/(float)bytesTotal) * 100;
int percentDone = (int)percentDonef;
Log.d("MainActivity", " ID:" + id + " bytesCurrent: " + bytesCurrent + " bytesTotal: " + bytesTotal + " " + percentDone + "%");
}
#Override
public void onError(int id, Exception ex) {
// Handle errors
}
});
// If your upload does not trigger the onStateChanged method inside your
// TransferListener, you can directly check the transfer state as shown here.
if (TransferState.COMPLETED == uploadObserver.getState()) {
// Handle a completed upload.
}
}
}
For more information:
https://docs.aws.amazon.com/aws-mobile/latest/developerguide/add-aws-mobile-user-data-storage.html#add-aws-user-data-storage-upload
https://docs.aws.amazon.com/aws-mobile/latest/developerguide/how-to-storage.html
AWS has a set of libraries that you could use to get and store in the S3 bucket.
You should check: https://docs.aws.amazon.com/aws-mobile/latest/developerguide/getting-started.html
For upload file to s3
String ACCESS_KEY="****************",
SECRET_KEY="****************",
MY_BUCKET="bucket_name",
OBJECT_KEY="unique_id";
AWSCredentials credentials = new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY);
AmazonS3 s3 = new AmazonS3Client(credentials);
java.security.Security.setProperty("networkaddress.cache.ttl" , "60");
s3.setRegion(Region.getRegion(Regions.AP_SOUTHEAST_1));
s3.setEndpoint("https://s3-ap-southeast-1.amazonaws.com/");
List<Bucket> buckets=s3.listBuckets();
for(Bucket bucket:buckets){
Log.e("Bucket ","Name "+bucket.getName()+" Owner "+bucket.getOwner()+ " Date " + bucket.getCreationDate());
}
Log.e("Size ", "" + s3.listBuckets().size());
TransferUtility transferUtility = new TransferUtility(s3, getApplicationContext());
UPLOADING_IMAGE=new File(Environment.getExternalStorageDirectory().getPath()+"/Screenshot.png");
TransferObserver observer = transferUtility.upload(MY_BUCKET,OBJECT_KEY,UPLOADING_IMAGE);
observer.setTransferListener(new TransferListener() {
#Override
public void onStateChanged(int id, TransferState state) {
// do something
progress.hide();
path.setText("ID "+id+"\nState "+state.name()+"\nImage ID "+OBJECT_KEY);
}
#Override
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
int percentage = (int) (bytesCurrent / bytesTotal * 100);
progress.setProgress(percentage);
//Display percentage transfered to user
}
#Override
public void onError(int id, Exception ex) {
// do something
Log.e("Error ",""+ex );
}
});
For downloading image
https://github.com/jontyankit/Glide-Amazon-Image-Load

Provoke conflict when using Android Snapshots (Game Play Services)

I have implemented functions to save and load snapshots using the Game API from Google Play Services. The next step I am working on is to handle conflicts when there is more then one snapshot. And here comes the problem.
In my understanding a conflict should occure when doing the following:
Save a snapshot after making game progress
Sign out (Google Play Services)
Delete all app data
Start game again and make some progress
Sign in (Google Play Services)
Save a new snapshop
Load snapshop
Unfortunately in my case no conflict occures. Instead the current game progress is being saved (step 6) and loading (step 7) just returns this snapshop. There is no indicator that snapshot (step 1) was overwritten - resulting in loosing game progress.
Code for saving:
private void executeSave() {
AsyncTask.execute(new Runnable() {
#Override
public void run() {
GoogleApiClient googleApiClient = App.getGoogleApiHelper().getmGoogleApiClient();
Snapshots.OpenSnapshotResult openResult = Games.Snapshots.open(googleApiClient, getSavegameFilename(), true).await();
Status resultStatus = openResult.getStatus();
Log.d(TAG, "openstatus is: " + resultStatus.getStatusMessage());
if(resultStatus.isSuccess()) {
byte[] localSavegame = getPersistingManager().readBytes();
if(localSavegame != null) {
Log.d(TAG, "Going to save:" + getPersistingManager().read());
createSnapshot(openResult.getSnapshot(), localSavegame).await();
}
}
}
});
}
private PendingResult<Snapshots.CommitSnapshotResult> createSnapshot(Snapshot snapshot, byte[] data) {
GoogleApiClient googleApiClient = App.getGoogleApiHelper().getmGoogleApiClient();
snapshot.getSnapshotContents().writeBytes(data);
SnapshotMetadataChange metadataChange = new SnapshotMetadataChange.Builder().build();
return Games.Snapshots.commitAndClose(googleApiClient, snapshot, metadataChange);
}
Code for loading:
private void executeLoad() {
AsyncTask.execute(new Runnable() {
#Override
public void run() {
GoogleApiClient googleApiClient = App.getGoogleApiHelper().getmGoogleApiClient();
Snapshots.OpenSnapshotResult result = Games.Snapshots.open(googleApiClient, getSavegameFilename(), true).await();
processResult(result, 0);
}
});
}
private void processResult(Snapshots.OpenSnapshotResult result, int retryCount) {
Status resultStatus = result.getStatus();
retryCount++;
if(resultStatus.isSuccess()) {
Log.d(TAG, "No conflict, thats great!");
handleSuccess(result);
} else if (resultStatus.getStatusCode() == GamesStatusCodes.STATUS_SNAPSHOT_CONFLICT) {
Log.d(TAG, "Aww... a conflict!");
handleConflict(result, retryCount);
} else {
Log.e(TAG, "Error while getting savegame, status message: " + resultStatus.getStatusMessage());
}
}
When performing the steps from above this is happening:
1) Save a snapshot after making game progress
absRemotePersistMgmt: openstatus is: STATUS_OK
absRemotePersistMgmt: Going to save:{"solvedQuestions":{"1":[],"2":[1]}}
6) Save a new snapshot
absRemotePersistMgmt: openstatus is: STATUS_OK
absRemotePersistMgmt: Going to save:{"solvedQuestions":{"1":[1,2],"2":[]}}
7) Load snapshop
absRemotePersistMgmt: No conflict, thats great!
What am I missing?
Sources I used:
https://developers.google.com/games/services/android/savedgames
https://www.youtube.com/watch?v=iHc2RBZs5T0
https://www.youtube.com/watch?v=naQhSkzNGAI

PjSIP Callling it from unknown/external thread

I'm trying to build pjsip and make a basic phone call by building a cordova plugin in Android. The following function is in a cordova plugin named
public class PJSIP extends CordovaPlugin{ .... }
private void makeCall(String number,String hostip ) {
String buddy_uri = "sip:"+number+"#"+hostip;
MyAccount account = null;
AccountConfig accCfg = null;
accCfg = new AccountConfig();
accCfg.setIdUri("sip:localhost");
accCfg.getNatConfig().setIceEnabled(true);
accCfg.getVideoConfig().setAutoTransmitOutgoing(true);
accCfg.getVideoConfig().setAutoShowIncoming(true);
MyAccount acc = new MyAccount(accCfg);
account = acc;
MyCall call = new MyCall(account, -1);
CallOpParam prm = new CallOpParam(true);
try {
call.makeCall(buddy_uri, prm);
} catch (Exception e) {
call.delete();
return;
}
currentCall = call;
}
What I receive as an error is the following:
A/libc: ../src/pj/os_core_unix.c:692: pj_thread_this: assertion
"!"Calling pjlib from unknown/external thread. You must " "register
external threads with pj_thread_register() " "before calling any pjlib
functions."" failed
I was checking around and it seems that there is an issue with the garbage collector but I'm not sure how to fix it.
Thanks
In pjsip, every call must come from a thread known to pjsip.
On your EndPoint object there is a method that helps you with that.
Basically, I just created a static method checkThread which helps me registering the currentThread.
I call this method at the beginning of every method that accesses pjsip objects.
You need to synchronize this method.
public static synchronized void checkThread() {
try {
if (mEndpoint != null && !mEndpoint.libIsThreadRegistered())
mEndpoint.libRegisterThread(Thread.currentThread().getName());
} catch (Exception e) {
Log.w("SIP", "Threading: libRegisterThread failed: " + e.getMessage());
}
}
And now each of your methods that access sip objects has to look like:
public void makeCall(String number) {
checkThread();
//...do your stuff...
}
Hope this helps, Cheers.

my query.findInBackground not getting executed in android

I am using parse for my data in android app. for some reason the code inside query.findInBackground is not getting executed.
public List<Date> sessionHeaderFetch(){
Log.d("test", "session fetch entry");
ParseQuery<Sessions> query = ParseQuery.getQuery(Sessions.class);
final List<Date> sessionHeaders = null;
query.findInBackground(new FindCallback<Sessions>() {
#Override
public void done(List<Sessions> sessionsObjects, com.parse.ParseException e) {
Log.d("test", "session internal entry");
if (e == null) {
Log.d("test", "Retrieved " + sessionsObjects.size() + " sessions");
for (Sessions session : sessionsObjects) {
sessionHeaders.add(session.getNetsDate());
};
} else {
Log.d("score", "Error: " + e.getMessage());
}
}
});
Log.d("test", "session last value");
return sessionHeaders;
}
the code inside public void done() is not all invoked.
I don't think you have understood how to query correctly in Parse.
When you define the parse query. The get query should contain the name of the table that you are trying to query. Also the queries returned will be ParseObjects normally, so I would expect that your callback should be new FindCallback().
I've adjusted the parse query below.
ParseQuery<Sessions> query = ParseQuery.getQuery("ParseTableName");
final List<Date> sessionHeaders = null;
query.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(List<ParseObject> sessionsObjects, com.parse.ParseException e) {
Log.d("test", "session internal entry");
if (e == null) {
// Find succeeded, so do something
} else {
Log.d("score", "Error: " + e.getMessage());
}
}
});
Obviously you will need to replace "ParseTableName" with the name of the table that you are trying to query in parse.
I actually solved it by using an ArrayList, there were some problems with the List initialization and also the results are fetched asynchronous manner so my functions which calls these functions were getting null values so I had to write by calling functions that calls the parse fetch functions asynchronously as well.
but the query can be written like this and it works.
public void sessionFetch(Date headers, final ParseIOListener<Sessions> listener){
ParseQuery<Sessions> query = ParseQuery.getQuery(Sessions.class);
Log.d("test", "input header" + headers );
query.whereEqualTo("NetsDate",headers);
final ArrayList<Sessions> sessionsData = new ArrayList<Sessions>();
query.findInBackground(new FindCallback<Sessions>() {
#Override
public void done(List<Sessions> sessionsObjects, com.parse.ParseException e) {
if (e == null) {
for (Sessions session : sessionsObjects) {
Log.d("test", "output objects" + session.toString() );
sessionsData.add(session);
Log.d("test", "Retrieved -- sessions" + sessionsObjects.size() );
}
listener.onDataRetrieved(sessionsData);
} else {
listener.onDataRetrieveFail(e);
}
}
});
}
If you want more details on implementing this, check this link
The reason why you feel the code is not being invoked is because you are returning from the method before the ".findInBackground" operation has completed. Remember the query is performed on a background thread. So this is how your code will run:
ParseQuery query = ParseQuery.getQuery(Sessions.class);
final List sessionHeaders = null;
------> 1. query.findInBackground (Popped onto background thread)
return sessionHeaders; (by the time you get here, sessionHeaders will probably still be null).
So change the logic of the code and wait till the callback returns before doing any processing on the "sessionHeaders" object.

Connecting to existing Google Chromecast Session from Android (for generic remote control)

I am creating a generic Chromecast remote control app. Most of the guts of the app are already created and I've managed to get Chromecast volume control working (by connecting to a Chromecast device along side another app that is casting - YouTube for example).
What I've having difficult with is performing other media commands such as play, pause, seek, etc.
Use case example:
1. User opens YouTube on their android device and starts casting a video.
2. User opens my app and connects to the same Chromecast device.
3. Volume control from my app (works now)
4. Media control (play, pause, etc) (does not yet work)
I found the Cast api reference that explains that you can sendMessage(ApiClient, namespace, message) with media commands; however the "message" (JSON) requires the sessionId of the current application (Youtube in this case). I have tried the following, but the connection to the current application always fails; status.isSuccess() is always false:
Cast.CastApi
.joinApplication(mApiClient)
.setResultCallback(
new ResultCallback<Cast.ApplicationConnectionResult>() {
#Override
public void onResult(
Cast.ApplicationConnectionResult result) {
Status status = result.getStatus();
if (status.isSuccess()) {
ApplicationMetadata applicationMetadata = result
.getApplicationMetadata();
sessionId = result.getSessionId();
String applicationStatus = result
.getApplicationStatus();
boolean wasLaunched = result
.getWasLaunched();
Log.i(TAG,
"Joined Application with sessionId: "
+ sessionId
+ " Application Status: "
+ applicationStatus);
} else {
// teardown();
Log.e(TAG,
"Could not join application: "
+ status.toString());
}
}
});
Is is possible to get the sessionId of an already running cast application from a generic remote control app (like the one I am creating)? If so, am I right in my assumption that I can then perform media commands on the connected Chromecast device using something like this:
JSONObject message = new JSONObject();
message.put("mediaSessionId", sessionId);
message.put("requestId", 9999);
message.put("type", "PAUSE");
Cast.CastApi.sendMessage(mApiClient,
"urn:x-cast:com.google.cast.media", message.toString());
Update:
I have tried the recommendations provided by #Ali Naddaf but unfortunately they are not working. After creating mRemoteMediaPlayer in onCreate, I also do requestStatus(mApiClient) in the onConnected callback (in the ConnectionCallbacks). When I try to .play(mApiClient) I get an IllegalStateException stating that there is no current media session. Also, I tried doing joinApplication and in the callback performed result.getSessionId; which returns null.
A few comments and answers:
You can get the sessionId from the callback of launchApplication or joinApplication; in the "onResult(result)", you can get the sessionId from: result.getSessionId()
YouTube is still not on the official SDK so YMMV, for apps using official SDK, you should be able to use the above approach (most of it)
Why are you trying to set up a message yourself? Why not building a RemoteMediaPlayer and using play/pause that is provided there? Whenever you are working with the media playback through the official channel, always use the RemoteMediaPlayer (don't forget to call requestStatus() on it after creating it).
Yes it is possible , First you have to save sesionId and CastDevice device id
and when remove app from background and again open app please check is there sessionId then call bello line.
Cast.CastApi.joinApplication(apiClient, APP_ID,sid).setResultCallback(connectionResultCallback);
if you get success result then need to implement further process in connectionResultCallback listener.
//Get selected device which you selected before
#Override
public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo route) {
// Log.d("Route Added", "onRouteAdded");
/* if (router.getRoutes().size() > 1)
Toast.makeText(homeScreenActivity, "'onRouteAdded :: " + router.getRoutes().size() + " -- " + router.getRoutes().get(1).isSelected(), Toast.LENGTH_SHORT).show();
else
Toast.makeText(homeScreenActivity, "'onRouteAdded :: " + router.getRoutes(), Toast.LENGTH_SHORT).show();*/
if (router != null && router.getRoutes() != null && router.getRoutes().size() > 1) {
// Show the button when a device is discovered.
// Toast.makeText(homeScreenActivity, "'onRouteAdded :: " + router.getRoutes().size() + " -- " + router.getRoutes().get(1).isSelected(), Toast.LENGTH_SHORT).show();
mMediaRouteButton.setVisibility(View.VISIBLE);
titleLayout.setVisibility(View.GONE);
castName.setVisibility(View.VISIBLE);
selectedDevice = CastDevice.getFromBundle(route.getExtras());
routeInfoArrayList = router.getRoutes();
titleLayout.setVisibility(View.GONE);
if (!isCastConnected) {
String deid = MyPref.getInstance(homeScreenActivity).readPrefs(MyPref.CAST_DEVICE_ID);
for (int i = 0; i < routeInfoArrayList.size(); i++) {
if (routeInfoArrayList.get(i).getExtras() != null && CastDevice.getFromBundle(routeInfoArrayList.get(i).getExtras()).getDeviceId().equalsIgnoreCase(deid)) {
selectedDevice = CastDevice.getFromBundle(routeInfoArrayList.get(i).getExtras());
routeInfoArrayList.get(i).select();
ReSelectedDevice(selectedDevice, routeInfoArrayList.get(i).getName());
break;
}
}
}
}
}
//Reconnect google Api Client
public void reConnectGoogleApiClient() {
if (apiClient == null) {
Cast.CastOptions apiOptions = new
Cast.CastOptions.Builder(selectedDevice, castClientListener).build();
apiClient = new GoogleApiClient.Builder(this)
.addApi(Cast.API, apiOptions)
.addConnectionCallbacks(reconnectionCallback)
.addOnConnectionFailedListener(connectionFailedListener)
.build();
apiClient.connect();
}
}
// join Application
private final GoogleApiClient.ConnectionCallbacks reconnectionCallback = new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(Bundle bundle) {
// Toast.makeText(homeScreenActivity, "" + isDeviceSelected(), Toast.LENGTH_SHORT).show();
try {
String sid = MyPref.getInstance(homeScreenActivity).readPrefs(MyPref.CAST_SESSION_ID);
String deid = MyPref.getInstance(homeScreenActivity).readPrefs(MyPref.CAST_DEVICE_ID);
if (sid != null && deid != null && sid.length() > 0 && deid.length() > 0)
Cast.CastApi.joinApplication(apiClient, APP_ID, sid).setResultCallback(connectionResultCallback);
isApiConnected = true;
} catch (Exception e) {
}
}
#Override
public void onConnectionSuspended(int i) {
isCastConnected = false;
isApiConnected = false;
}
};

Categories

Resources