Android AWS S3 SDK TransferUtility Not Working in Service - android

I am trying to use the AWS Android SDK for S3 in a started service. I am a little new to both the SDK and Services. I am pretty confident that the Transfer Utility is also running a service.
Handler (android.os.Handler) {11629d87} sending message to a Handler on a dead thread
java.lang.IllegalStateException: Handler (android.os.Handler) {11629d87} sending message to a Handler on a dead thread
at android.os.MessageQueue.enqueueMessage(MessageQueue.java:325)
at android.os.Handler.enqueueMessage(Handler.java:631)
at android.os.Handler.sendMessageAtTime(Handler.java:600)
at android.os.Handler.sendMessageDelayed(Handler.java:570)
at sksDoneLater(TransferService.java:189)
at com.amazonaws.mobileconnectors.s3.transferutility.TransferService.access$200(TransferService.java:44)
at com.amazonaws.mobileconnectors.s3.transferutility.TransferService$2.handleMessage(TransferService.java:166)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:135)
at android.os.HandlerThread.run(HandlerThread.java:61)
Here is the code I am using to start it up:
AmazonS3 amazonS3 = new AmazonS3Client(credentialsProvider);
mTransferUtility = new TransferUtility(amazonS3, getApplicationContext());
TransferObserver observer = mTransferUtility.upload(
S3_RAW_BUCKET_ARN,
mVidFileKey,
mVidFile);
observer.setTransferListener(new TransferListener() {...})
The line previous it says it can't get s3 client. I created the client just as shown above in application class and successfully used the same code in an activity to perform a successful transfer so it must be something obvious about services that I am ignorant of. The above code is called in a method that is called from onStartCommand() in the service.
Any help would be much appreciated.
UPDATE - Whole Class was requested and is shown here:
public class VideoCompressionService extends Service {;
private Bus bus;
private TransferUtility mTransferUtility;
private int mVidWidth;
private int mVidHeight;
private File mCompressedVidFile;
private File mVidFile;
private String mVidFileKey;
private NotificationManager mNotificationManager;
private android.support.v4.app.NotificationCompat.Builder mNotification;
public VideoCompressionService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
String realPath = MediaUtils.getRealVideoPathFromURI(this.getContentResolver(), intent.getData());
if (realPath != null) {
this.mVidFile = new File(realPath);
this.mVidFileKey = intent.getStringExtra(EXTRA_VID_FILE_KEY);
this.mVidWidth = intent.getIntExtra(EXTRA_VID_WIDTH, 0);
this.mVidHeight = intent.getIntExtra(EXTRA_VID_HEIGHT, 0);
this.bus = CoPhotoApplication.getVideoCompressionBus();
if (mVidFile != null && mVidFile.exists() && mVidFile.canRead()) {
mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
mNotification = new NotificationCompat.Builder(this)
.setContentTitle("Compressing Video - Step 1 of 3")
.setContentText("Uploading video for processing...")
.setSmallIcon(R.drawable.ic_launcher);
if (mVidWidth == 0 || mVidHeight == 0) {
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(mVidFile.getAbsolutePath());
mVidWidth = Integer.parseInt(mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
mVidHeight = Integer.parseInt(mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
mmr.release();
}
uploadVidToS3();
}
return Service.START_NOT_STICKY;
} else {
VideoCompressionService.this.stopSelf();
return Service.START_NOT_STICKY;
}
} else {
VideoCompressionService.this.stopSelf();
return Service.START_NOT_STICKY;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void uploadVidToS3() {
compressionUploadStarted();
CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
getApplicationContext(),
"*********ID HERE********", // Identity Pool ID
Regions.US_EAST_1 // Region
);
AmazonS3 amazonS3 = new AmazonS3Client(credentialsProvider);
mTransferUtility = new TransferUtility(amazonS3, getApplicationContext());
TransferObserver observer = mTransferUtility.upload(
S3_RAW_BUCKET_ARN,
mVidFileKey,
mVidFile);
observer.setTransferListener(new TransferListener() {
#Override
public void onStateChanged(int id, TransferState state) {
if (state == TransferState.COMPLETED) {
compressionUploadFinished(true);
zencodeVideo(mVidFileKey);
} else if (state == TransferState.FAILED) {
compressionUploadFinished(false);
}
}
#Override
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
int progress = Math.round(100 * ((float) bytesCurrent / bytesTotal));
String progressPercentage = String.valueOf(progress) + "%";
compressionUploadProgress(progress, progressPercentage);
}
#Override
public void onError(int id, Exception ex) {
compressionUploadFinished(false);
}
});
}
private void compressionUploadStarted() {
bus.post(new CompressionUploadStartedEvent());
updateNotification();
}
private void compressionUploadProgress(int progress, String progressPercentage) {
bus.post(new CompressionUploadProgressEvent(progress, progressPercentage));
mNotification.setProgress(100, progress, false);
updateNotification();
}
private void compressionUploadFinished(boolean successfully) {
bus.post(new CompressionUploadFinishedEvent(successfully));
if (successfully) {
mNotification.setContentText("Upload complete");
} else {
mNotification.setContentTitle("Compression Failed");
mNotification.setContentText("Upload failed. Please try again later.");
}
updateNotification();
if (!successfully) {
VideoCompressionService.this.stopSelf();
}
}
private void updateNotification() {
mNotificationManager.notify(NOTIFICATION_ID, mNotification.build());
}

I found myself in the same issue...
Here it is what solved for me:
I noticed I had the following service for my SyncAdapter registered in Manifest:
<service
android:name=".sync.SyncService"
android:exported="true"
android:process=":sync">
<intent-filter>
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data android:name="android.content.SyncAdapter"
android:resource="#xml/syncadapter" />
</service>
The android:process=":sync" tells service to run in a private process called :sync
So I just told the TransferService from Amazon SDK to run in this same thread.
<service
android:name="com.amazonaws.mobileconnectors.s3.transferutility.TransferService"
android:process=":sync"
android:enabled="true" />
After that, I no more got the error TransferService can't get s3 client, and it will stop, and finally was able to upload my pictures to S3

You should declare the client as a global variable, as the garbage collector could delete it if you don't do it.

Related

WifiP2pManager failed to addLocalService and discoverServices returns WifiP2pManager.Error when application is in background

I'm new here. Anyways I've been scratching my head at this issue I have. I created a service that runs WiFiP2pHelper to discover nearby devices and retrieve WifiP2pDnsSdServiceInfo. I tested it on multiple phones, and they seem to work fine. If it works fine, it should return logs like these:
2020-08-12 13:32:23.449 24345-24345/com.example.acts D/WiFi P2P Helper: Service Request Added
2020-08-12 13:32:23.449 24345-24345/com.example.acts D/WiFi P2P Helper: Discover Services Successful
2020-08-12 13:32:23.473 24345-24345/com.example.acts D/WiFi P2P Helper: Local Service Added
Now I made the service to run in the background (even when the application is killed). It seems to work fine on the devices I've tested except any that runs on Android 10. I suspect it's something with Android 10 that causes WiFiP2pManager or WiFi P2P itself not to run in the background. But I'm not entirely sure if it's Android 10 causing it (I only got 2 Android 10 devices that were tested on). It returns the following logs:
2020-08-12 13:33:23.517 24345-24345/com.example.acts D/WiFi P2P Helper: Service Request Added
2020-08-12 13:33:23.517 24345-24345/com.example.acts D/WiFi P2P Helper: Error with P2P 0
2020-08-12 13:33:23.518 24345-24345/com.example.acts D/WiFi P2P Helper: Failed to Add Local Service
2020-08-12 13:33:23.518 24345-24345/com.example.acts E/WiFi P2P Helper: 0
As you can see, "Error with P2P 0" and "Failed to Add Local Service" points to the discoverServices and addLocalService. Below is the code. Sorry to dump it all here. Any help would really be appreciated! Thanks!
public class WiFiP2PHelper extends Service {
WifiP2pManager manager;
WifiP2pManager.Channel channel;
WifiP2pDnsSdServiceRequest serviceRequest;
IntentFilter intentFilter;
String iid;
private String baseServiceName = "ACTS";
private String serviceName = "";
final HashMap<String, String> buddies = new HashMap<String, String>();
final String TAG = "WiFi P2P Helper";
Handler handler = new Handler();
Runnable runnable;
int delay = 15000;
#RequiresApi(api = Build.VERSION_CODES.O)
#Override
public void onCreate() {
super.onCreate();
}
#RequiresApi(api = Build.VERSION_CODES.O)
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
channel = manager.initialize(this, getMainLooper(), null);
intentFilter = new IntentFilter();
intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
getUser();
startRegistration();
discoverService();
startMyOwnForeground();
return START_STICKY;
}
#RequiresApi(api = Build.VERSION_CODES.O)
private void startMyOwnForeground() {
String NOTIFICATION_CHANNEL_ID = "com.example.acts";
String channelName = "My Background Service";
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
assert manager != null;
manager.createNotificationChannel(chan);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
Notification notification = notificationBuilder.setOngoing(true)
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("Hello World")
.setPriority(NotificationManager.IMPORTANCE_MIN)
.setCategory(Notification.CATEGORY_SERVICE)
.build();
startForeground(1333, notification);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void getUser() {
List<User> users = User.getUser();
int count = users.size();
if (count > 0) {
User loggedInUser = users.get(0);
iid = loggedInUser.Id;
serviceName = baseServiceName + "_" + loggedInUser.Id;
Log.e("User", iid);
} else {
//uId = "User_Unregistered";
serviceName = baseServiceName + "_User_Unregistered";
Log.e("User in else", serviceName);
}
}
public String getWFDMacAddress() {
try {
List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface ntwInterface : interfaces) {
if (ntwInterface.getName().equalsIgnoreCase("p2p0")) {
byte[] byteMac = ntwInterface.getHardwareAddress();
if (byteMac == null) {
return null;
}
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < byteMac.length; i++) {
strBuilder.append(String.format("%02X:", byteMac[i]));
}
if (strBuilder.length() > 0) {
strBuilder.deleteCharAt(strBuilder.length() - 1);
}
return strBuilder.toString().toLowerCase();
}
}
} catch (Exception e) {
Log.d(TAG, e.getMessage());
}
return null;
}
public int findOpenSocket() throws IOException {
// Initialize a server socket on the next available port.
ServerSocket serverSocket = new ServerSocket(0);
// Store the chosen port.
int port = serverSocket.getLocalPort();
serverSocket.close();
return port;
}
private void startRegistration() {
manager.clearLocalServices(channel, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
Log.e(TAG, "onSuccess: Sucessss");
int port = 3030;
try {
port = findOpenSocket();
} catch (IOException e) {
e.printStackTrace();
}
Map record = new HashMap();
record.put("listenport", String.valueOf(port));
record.put("buddyname", iid);
record.put("available", "visible");
WifiP2pDnsSdServiceInfo serviceInfo =
WifiP2pDnsSdServiceInfo.newInstance(serviceName, "_presence._tcp", record);
manager.addLocalService(channel, serviceInfo, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
Log.d(TAG, "Local Service Added");
}
#Override
public void onFailure(int arg0) {
Log.d(TAG, "Failed to Add Local Service");
Log.e(TAG, String.valueOf(arg0));
}
});
}
#Override
public void onFailure(int arg0) {
Log.d(TAG, "P2P Unsupported");
}
});
}
private void discoverService() {
WifiP2pManager.DnsSdTxtRecordListener txtListener = new WifiP2pManager.DnsSdTxtRecordListener() {
#Override
public void onDnsSdTxtRecordAvailable(
String fullDomain, Map record, WifiP2pDevice device) {
Log.d(TAG, "DnsSdTxtRecord available - " + record.toString());
buddies.put(device.deviceAddress, (String) record.get("buddyname"));
}
};
WifiP2pManager.DnsSdServiceResponseListener servListener = new WifiP2pManager.DnsSdServiceResponseListener() {
#Override
public void onDnsSdServiceAvailable(String instanceName, String registrationType,
WifiP2pDevice resourceType) {
resourceType.deviceName = buddies
.containsKey(resourceType.deviceAddress) ? buddies
.get(resourceType.deviceAddress) : resourceType.deviceName;
String uId = instanceName.replace(baseServiceName + "_", "");
Log.d(TAG, "Received " + uId);
/*Discovered device1 = new Discovered();
device1.discoveredthreeID = uId;
Discovered.insertTransactionToSQLite(device1);*/
}
};
serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();
manager.addServiceRequest(channel,
serviceRequest,
new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
Log.d(TAG, "Service Request Added");
}
#Override
public void onFailure(int code) {
Log.d(TAG, "Failed to Add Service Request");
}
});
manager.setDnsSdResponseListeners(channel, servListener, txtListener);
manager.discoverServices(channel, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
Log.d(TAG, "Discover Services Successful");
}
#Override
public void onFailure(int code) {
// Command failed. Check for P2P_UNSUPPORTED, ERROR, or BUSY
if (code == WifiP2pManager.P2P_UNSUPPORTED) {
Log.d(TAG, "P2P isn't supported on this device.");
} else if (code == WifiP2pManager.ERROR) {
Log.d(TAG, "Error with P2P " + code);
} else if (code == WifiP2pManager.BUSY) {
Log.d(TAG, "P2P is Busy " + code);
}
}
});
}
}
Hi everyone who has the same issue as me. I found the solution to this. Basically, Android 10 (API 29) separates location permissions to the normal location permission and background location permission. You'll need to get your application to prompt for background permission (since it's not allowed by default).
Add these to your AndroidManifest.xml file
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
and in my MainActivity onCreate method I added a check to see if the device is on Android 10 (API 29) and if the permissions have already been granted
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if ((ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) ||
(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION}, 1);
}
} else {
if ((ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
}
}
If anyone else has a better way of implementing permission checks, please share it here. I'd like to learn as well. Thanks!

How to stop IntentService Android

I have build an IntentService in Android.
So if I received a pushNotification message, I muse stopped this service.
public class DosimeterDataSensingService extends IntentService {
private static final String TAG = "DosimeterDataSensingService";
public static boolean isStarted = false;
private Context mContext;
private int mStatus;
private BluetoothDevice mDevice;
private BluetoothGatt mConnGatt;
private boolean notificationsEnabled;
private long dosimeterScanningTime;
private boolean isThreadStarted = false;
private List<DosimeterDataRequest.DataBean> dosimeterDataList;
private List<DosimeterDataRequest.DataBean> dosimeterDataListMqtt;
private DosimeterMqttParcel dosimeterMqttParcel = new DosimeterMqttParcel();
private DosimeterDataRequest dosimeterDataRequest;
private String macAddress;
private boolean isRecordingEnabled = false;
private String dose = "";
private int battery = -1;
private Boolean syncRadiactionWS = false;
private Boolean syncRadiactionMQTT = false;
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* #param name Used to name the worker thread, important only for debugging.
*/
public DosimeterDataSensingService(String name) {
super(name);
}
public DosimeterDataSensingService() {
super(null);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
Log.d("dosimetro", "ON HANDLE INTENT");
if(intent!=null){
String action = intent.getStringExtra(PreferenceHandler.ACTION_STOP);
Log.d("dosimetro", action!=null ? action : "no action");
if(action!=null && action.equals(PreferenceHandler.ACTION_STOP)){
Log.d("dosimetro", "fermo il servizio");
String syncScanTime = PreferenceHandler.readString(getApplicationContext(), PreferenceHandler.DOSIMETER_SCANNING_TIME, null);
Log.d("dosimetro", syncScanTime!=null ? syncScanTime : "nullo");
String syncRotTime = PreferenceHandler.readString(getApplicationContext(), PreferenceHandler.DOSIMETER_ROTATION_TIME, null);
Log.d("dosimetro", syncRotTime!=null ? syncRotTime : "nullo");
super.stopSelf();
Log.d("dosimetro", "ho stoppato");
onDestroy();
return;
}
}
}
#Override
public void onCreate() {
super.onCreate();
Paper.init(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = createNotificationChannel();
}
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, getString(R.string.default_notification_channel_id))
.setSmallIcon(R.drawable.logoxhdpi)
.setCategory(Notification.CATEGORY_SERVICE)
.setContentTitle("Cardio App")
.setContentText("Getting data from Dosimeter")
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
Notification notification = mBuilder.build();
startForeground((int) (System.currentTimeMillis() + 1), notification);
isStarted = true;
if (PreferenceHandler.readString(this, PreferenceHandler.TYPE_USER, null).equals("2")) {
checkDosage();
}
List<GetAssignedDevicesListResponse.Parameters> preferenzeParametri= Paper.book().read(PreferenceHandler.PARAMETRI_VITALI, new ArrayList<>());
if(preferenzeParametri!=null && preferenzeParametri.size()>0){
for (GetAssignedDevicesListResponse.Parameters p: preferenzeParametri) {
if(p.getIdParameter() == PreferenceHandler.ID_RADIACTION){
//VERIFICO COME L'RR DEVE ESSERE SINCRONIZZATO
syncRadiactionWS = p.getSyncWs()!=null ? p.getSyncWs() : false;
syncRadiactionMQTT = p.getSyncMqtt()!=null ? p.getSyncMqtt() : false;
Log.e("DOSIMETER", "syncRadiactionWS true");
}
}
}else{
Log.e("DOSIMETER", "paperi init false");
}
checkBattery();
Log.i("SCANNINGTIME", "SYNC WS STARTED");
syncRadiactionLevel();
syncMqtt();
}
#Override
public void onDestroy() {
Log.d("DOSIMETRO", "ondestroy");
isStarted = false;
disconnectDosimeter();
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#SuppressLint("LongLogTag")
public void disconnectDosimeter() {
if (mConnGatt != null) {
isThreadStarted = false;
if ((mStatus != BluetoothProfile.STATE_DISCONNECTING)
&& (mStatus != BluetoothProfile.STATE_DISCONNECTED)) {
mConnGatt.disconnect();
mConnGatt.close();
mConnGatt = null;
mStatus = BluetoothProfile.STATE_DISCONNECTED;
}
}
try {
Method m = mDevice.getClass().getMethod("removeBond", (Class[]) null);
m.invoke(mDevice, (Object[]) null);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
Log.v("Dosimeter", "isRecordingEnabled" + isRecordingEnabled);
Log.v("Dosimeter", "Disconnecteddd");
}
/**
* Enable recording of values
*/
private void startRecordingData() {
String dosimeterRotation = PreferenceHandler.readString(getApplicationContext(), PreferenceHandler.DOSIMETER_ROTATION_TIME, null);
Log.i("ROTATIONTIME", dosimeterRotation);
long dosimeterRotationTime = 0L;
if (dosimeterRotation != null) {
dosimeterRotationTime = Long.parseLong(dosimeterRotation);
new Handler().postDelayed(() -> {
isRecordingEnabled = true;
isThreadStarted = false;
}, dosimeterRotationTime);
}
}
}
To stop the service I m using this code:
Intent i = new Intent(this, DosimeterDataSensingService.class);
i.putExtra(PreferenceHandler.ACTION_STOP,PreferenceHandler.ACTION_STOP);
stopService(new Intent(this, DosimeterDataSensingService.class));
From my log I can see that the system call
super.stopSelf();
onDestroy();
method but the IntentService works always.
You need not call stopSelf() or stopService() for IntentService.
As per the description mentioned in Docs:
Because most of the started services don't need to handle multiple requests simultaneously (which can actually be a dangerous multi-threading scenario), it's best that you implement your service using the IntentService class.
The IntentService class does the following:
It creates a default worker thread that executes all of the intents that are delivered to onStartCommand(), separate from your application's main thread.
Creates a work queue that passes one intent at a time to your onHandleIntent() implementation, so you never have to worry about multi-threading.
Stops the service after all of the start requests are handled, **so you never have to call stopSelf().**
Provides a default implementation of onBind() that returns null.
Provides a default implementation of onStartCommand() that sends the intent to the work queue and then to your onHandleIntent() implementation.
If the service is still running may be some intents are running.
Hope this helps.

Android create thread for pusher to run at background

In my app, I am getting my messages instantly from my server via pusher. I have created a service designated to handle connections and firing broadcast messages to other activities in my app.
The problem that I face now is to have this service run in a new thread to have it still run even when my app goes to the background. I've found from this that I should create and connect it to the "service thread", but I cannot find examples for it with pusher.
If anyone can, could you please provide an example to do so? If not, insights to writing code with these "service threads" would be helpful as well. Thanks in advance for the help :D
PusherService.java
public class PusherService extends Service {
private static final String TAG = "PusherService";
private Pusher pusher = new Pusher("myKey");
private Channel channel = pusher.subscribe("cafe_channel");
private JSONObject pusherJSONObj;
private Order order;
public PusherService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//this service will run until we stop it
setupPusher();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(this, "Service Stopped", Toast.LENGTH_LONG).show();
}
private void setupPusher() {
Log.d(TAG, System.currentTimeMillis()+"");
channel.bind("customer_order", new SubscriptionEventListener() {
#Override
public void onEvent(String channelName, String eventName, final String data) {
Intent broadcastIntent = new Intent();
try {
pusherJSONObj = new JSONObject(data);
order = new Order(pusherJSONObj);
broadcastIntent.setAction("customer_order");
broadcastIntent.putExtra("message", "success");
broadcastIntent.putExtra("order", order);
} catch (JSONException e) {
e.printStackTrace();
Log.d("Pusher", "conversion failed");
broadcastIntent.setAction("customer_order");
broadcastIntent.putExtra("message", "JSON conversion error");
}
sendBroadcast(broadcastIntent);
}
});
pusher.connect();
}
}
OrdersActivity.java
private BroadcastReceiver pusherReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equalsIgnoreCase("customer_order")) {
adapter.newOrder((Order) intent.getParcelableExtra("order"));
}
}
};
It turns out that multithreading on one process does not solve my problem.
So instead, I split the service into a new process, which will keep the service running independent of the status of the main thread & process. Tested and found that service does not stall when my activities go background.

How to integrate SIP into Android?

How to implement SIP protocol in Android ?
there is any SDK or library available to implement it easily into Android?
Here is a third party Library with sample code. You can use this, I have used it and it works fine.
Android 2.3 or higher provides API for SIP.
Refer this link for SIP in Android
also you can see DEMO project for SIP from Sample
update:
Android SDK Samples on github.
SipDemo1, SipDemo2
Search for SipDemo project in samples for android 4.0.3 SDK version(API level -15)
I have been investigated this sort of problem for a long time and found out that SipManager and SipProfile are unfortunatelly poor and extremelly buggy.
So I found a Linphone library. There is a link for their wiki. I implemented it in my project using maven:
repositories {
...
maven { "https://linphone.org/maven_repository/"}
}
Also there is a sample of using it on gitlab: link here, it's pretty fresh, for now :)
If the link would crash, I just copy/paste the most important part of how to use linphone's core:
public class LinphoneService extends Service {
private static final String START_LINPHONE_LOGS = " ==== Device information dump ====";
// Keep a static reference to the Service so we can access it from anywhere in the app
private static LinphoneService sInstance;
private Handler mHandler;
private Timer mTimer;
private Core mCore;
private CoreListenerStub mCoreListener;
public static boolean isReady() {
return sInstance != null;
}
public static LinphoneService getInstance() {
return sInstance;
}
public static Core getCore() {
return sInstance.mCore;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
// The first call to liblinphone SDK MUST BE to a Factory method
// So let's enable the library debug logs & log collection
String basePath = getFilesDir().getAbsolutePath();
Factory.instance().setLogCollectionPath(basePath);
Factory.instance().enableLogCollection(LogCollectionState.Enabled);
Factory.instance().setDebugMode(true, getString(R.string.app_name));
// Dump some useful information about the device we're running on
Log.i(START_LINPHONE_LOGS);
dumpDeviceInformation();
dumpInstalledLinphoneInformation();
mHandler = new Handler();
// This will be our main Core listener, it will change activities depending on events
mCoreListener = new CoreListenerStub() {
#Override
public void onCallStateChanged(Core core, Call call, Call.State state, String message) {
Toast.makeText(LinphoneService.this, message, Toast.LENGTH_SHORT).show();
if (state == Call.State.IncomingReceived) {
Toast.makeText(LinphoneService.this, "Incoming call received, answering it automatically", Toast.LENGTH_LONG).show();
// For this sample we will automatically answer incoming calls
CallParams params = getCore().createCallParams(call);
params.enableVideo(true);
call.acceptWithParams(params);
} else if (state == Call.State.Connected) {
// This stats means the call has been established, let's start the call activity
Intent intent = new Intent(LinphoneService.this, CallActivity.class);
// As it is the Service that is starting the activity, we have to give this flag
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
};
try {
// Let's copy some RAW resources to the device
// The default config file must only be installed once (the first time)
copyIfNotExist(R.raw.linphonerc_default, basePath + "/.linphonerc");
// The factory config is used to override any other setting, let's copy it each time
copyFromPackage(R.raw.linphonerc_factory, "linphonerc");
} catch (IOException ioe) {
Log.e(ioe);
}
// Create the Core and add our listener
mCore = Factory.instance()
.createCore(basePath + "/.linphonerc", basePath + "/linphonerc", this);
mCore.addListener(mCoreListener);
// Core is ready to be configured
configureCore();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
// If our Service is already running, no need to continue
if (sInstance != null) {
return START_STICKY;
}
// Our Service has been started, we can keep our reference on it
// From now one the Launcher will be able to call onServiceReady()
sInstance = this;
// Core must be started after being created and configured
mCore.start();
// We also MUST call the iterate() method of the Core on a regular basis
TimerTask lTask =
new TimerTask() {
#Override
public void run() {
mHandler.post(
new Runnable() {
#Override
public void run() {
if (mCore != null) {
mCore.iterate();
}
}
});
}
};
mTimer = new Timer("Linphone scheduler");
mTimer.schedule(lTask, 0, 20);
return START_STICKY;
}
#Override
public void onDestroy() {
mCore.removeListener(mCoreListener);
mTimer.cancel();
mCore.stop();
// A stopped Core can be started again
// To ensure resources are freed, we must ensure it will be garbage collected
mCore = null;
// Don't forget to free the singleton as well
sInstance = null;
super.onDestroy();
}
#Override
public void onTaskRemoved(Intent rootIntent) {
// For this sample we will kill the Service at the same time we kill the app
stopSelf();
super.onTaskRemoved(rootIntent);
}
private void configureCore() {
// We will create a directory for user signed certificates if needed
String basePath = getFilesDir().getAbsolutePath();
String userCerts = basePath + "/user-certs";
File f = new File(userCerts);
if (!f.exists()) {
if (!f.mkdir()) {
Log.e(userCerts + " can't be created.");
}
}
mCore.setUserCertificatesPath(userCerts);
}
private void dumpDeviceInformation() {
StringBuilder sb = new StringBuilder();
sb.append("DEVICE=").append(Build.DEVICE).append("\n");
sb.append("MODEL=").append(Build.MODEL).append("\n");
sb.append("MANUFACTURER=").append(Build.MANUFACTURER).append("\n");
sb.append("SDK=").append(Build.VERSION.SDK_INT).append("\n");
sb.append("Supported ABIs=");
for (String abi : Version.getCpuAbis()) {
sb.append(abi).append(", ");
}
sb.append("\n");
Log.i(sb.toString());
}
private void dumpInstalledLinphoneInformation() {
PackageInfo info = null;
try {
info = getPackageManager().getPackageInfo(getPackageName(), 0);
} catch (PackageManager.NameNotFoundException nnfe) {
Log.e(nnfe);
}
if (info != null) {
Log.i(
"[Service] Linphone version is ",
info.versionName + " (" + info.versionCode + ")");
} else {
Log.i("[Service] Linphone version is unknown");
}
}
private void copyIfNotExist(int ressourceId, String target) throws IOException {
File lFileToCopy = new File(target);
if (!lFileToCopy.exists()) {
copyFromPackage(ressourceId, lFileToCopy.getName());
}
}
private void copyFromPackage(int ressourceId, String target) throws IOException {
FileOutputStream lOutputStream = openFileOutput(target, 0);
InputStream lInputStream = getResources().openRawResource(ressourceId);
int readByte;
byte[] buff = new byte[8048];
while ((readByte = lInputStream.read(buff)) != -1) {
lOutputStream.write(buff, 0, readByte);
}
lOutputStream.flush();
lOutputStream.close();
lInputStream.close();
}
}
I hope, that will help somebody, because I spend a lot of time trying to find it!
I used by this library:
https://www.mizu-voip.com/Software/SIPSDK/AndroidSIPSDK.aspx
it is very easy.
also i add button for answer the call:
mysipclient.Accept(mysipclient.GetLine());

onServiceConnected sometimes not called after bindService on some devices

I've looked at a number of other threads with similar titles, and none seem to cover my problem. So, here goes.
I'm using the Google market expansion files (apkx) library and sample code, with a few modifications. This code relies on receiving callbacks from a service which handles background downloading, licence checks etc.
I have a bug where the service doesn't get correctly attached, which results in a softlock. To make this more unhelpful, this bug never happens on some devices, but occurs about two thirds of the time on other devices. I believe it to be independent of Android version, certainly I have two devices running 2.3.4, one of which (a Nexus S) doesn't have the problem, the other (an HTC Evo 3D) does.
To attempt to connect to the service, bindService is called and returns true. OnBind then gets called as expected and returns a sensible value but (when the bug occurs) onServiceConnected doesn't happen (I've waited 20 minutes just in case).
Has anyone else seen anything like this? If not, any guesses for what I might have done to cause such behaviour? If no-one has any thoughts, I'll post some code tomorrow.
EDIT: Here's the relevant code. If I've missed anything, please ask.
Whilst adding this code, I found a minor bug. Fixing it caused the frequency of the problem I'm trying to solve to change from 2 times in 3 to about 1 time in 6 on the phone I'm testing it on; no idea about effects on other phones. This continues to suggest to me a race condition or similar, but I've no idea what with.
OurDownloaderActivity.java (copied and changed from Google sample code)
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
//Test the licence is up to date
//if (current stored licence has expired)
{
startLicenceCheck();
initializeDownloadUI();
return;
}
...
}
#Override
protected void onResume() {
if (null != mDownloaderClientStub) {
mDownloaderClientStub.connect(this);
}
super.onResume();
}
private void startLicenceCheck()
{
Intent launchIntent = OurDownloaderActivity.this
.getIntent();
Intent intentToLaunchThisActivityFromNotification = new Intent(OurDownloaderActivity
.this, OurDownloaderActivity.this.getClass());
intentToLaunchThisActivityFromNotification.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TOP);
intentToLaunchThisActivityFromNotification.setAction(launchIntent.getAction());
if (launchIntent.getCategories() != null) {
for (String category : launchIntent.getCategories()) {
intentToLaunchThisActivityFromNotification.addCategory(category);
}
}
// Build PendingIntent used to open this activity from Notification
PendingIntent pendingIntent = PendingIntent.getActivity(OurDownloaderActivity.this,
0, intentToLaunchThisActivityFromNotification,
PendingIntent.FLAG_UPDATE_CURRENT);
DownloaderService.startLicenceCheck(this, pendingIntent, OurDownloaderService.class);
}
initializeDownloadUI()
{
mDownloaderClientStub = DownloaderClientMarshaller.CreateStub
(this, OurDownloaderService.class);
//do a load of UI setup
...
}
//This should be called by the Stub's onServiceConnected method
/**
* Critical implementation detail. In onServiceConnected we create the
* remote service and marshaler. This is how we pass the client information
* back to the service so the client can be properly notified of changes. We
* must do this every time we reconnect to the service.
*/
#Override
public void onServiceConnected(Messenger m) {
mRemoteService = DownloaderServiceMarshaller.CreateProxy(m);
mRemoteService.onClientUpdated(mDownloaderClientStub.getMessenger());
}
DownloaderService.java (in Google market expansion library but somewhat edited )
//this is the onBind call that happens fine; the value it returns is definitely not null
#Override
public IBinder onBind(Intent paramIntent) {
return this.mServiceMessenger.getBinder();
}
final private IStub mServiceStub = DownloaderServiceMarshaller.CreateStub(this);
final private Messenger mServiceMessenger = mServiceStub.getMessenger();
//MY CODE, derived from Google's code
//I have seen the bug occur with a service started by Google's code too,
//but this code happens more often so is more repeatably related to the problem
public static void startLicenceCheck(Context context, PendingIntent pendingIntent, Class<?> serviceClass)
{
String packageName = serviceClass.getPackage().getName();
String className = serviceClass.getName();
Intent fileIntent = new Intent();
fileIntent.setClassName(packageName, className);
fileIntent.putExtra(EXTRA_LICENCE_EXPIRED, true);
fileIntent.putExtra(EXTRA_PENDING_INTENT, pendingIntent);
context.startService(fileIntent);
}
#Override
protected void onHandleIntent(Intent intent) {
setServiceRunning(true);
try {
final PendingIntent pendingIntent = (PendingIntent) intent
.getParcelableExtra(EXTRA_PENDING_INTENT);
if (null != pendingIntent)
{
mNotification.setClientIntent(pendingIntent);
mPendingIntent = pendingIntent;
} else if (null != mPendingIntent) {
mNotification.setClientIntent(mPendingIntent);
} else {
Log.e(LOG_TAG, "Downloader started in bad state without notification intent.");
return;
}
if(intent.getBooleanExtra(EXTRA_LICENCE_EXPIRED, false))
{
//we are here due to startLicenceCheck
updateExpiredLVL(this);
return;
}
...
}
}
//MY CODE, based on Google's, again
public void updateExpiredLVL(final Context context) {
Context c = context.getApplicationContext();
Handler h = new Handler(c.getMainLooper());
h.post(new LVLExpiredUpdateRunnable(c));
}
private class LVLExpiredUpdateRunnable implements Runnable
{
LVLExpiredUpdateRunnable(Context context) {
mContext = context;
}
final Context mContext;
#Override
public void run() {
setServiceRunning(true);
mNotification.onDownloadStateChanged(IDownloaderClient.STATE_LVL_UPDATING);
String deviceId = getDeviceId(mContext);
final APKExpansionPolicy aep = new APKExpansionPolicy(mContext,
new AESObfuscator(getSALT(), mContext.getPackageName(), deviceId));
// Construct the LicenseChecker with a Policy.
final LicenseChecker checker = new LicenseChecker(mContext, aep,
getPublicKey() // Your public licensing key.
);
checker.checkAccess(new LicenseCheckerCallback() {
...
});
}
}
DownloaderClientMarshaller.java (in Google market expansion library)
public static IStub CreateStub(IDownloaderClient itf, Class<?> downloaderService) {
return new Stub(itf, downloaderService);
}
and the Stub class from the same file:
private static class Stub implements IStub {
private IDownloaderClient mItf = null;
private Class<?> mDownloaderServiceClass;
private boolean mBound;
private Messenger mServiceMessenger;
private Context mContext;
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_ONDOWNLOADPROGRESS:
Bundle bun = msg.getData();
if ( null != mContext ) {
bun.setClassLoader(mContext.getClassLoader());
DownloadProgressInfo dpi = (DownloadProgressInfo) msg.getData()
.getParcelable(PARAM_PROGRESS);
mItf.onDownloadProgress(dpi);
}
break;
case MSG_ONDOWNLOADSTATE_CHANGED:
mItf.onDownloadStateChanged(msg.getData().getInt(PARAM_NEW_STATE));
break;
case MSG_ONSERVICECONNECTED:
mItf.onServiceConnected(
(Messenger) msg.getData().getParcelable(PARAM_MESSENGER));
break;
}
}
});
public Stub(IDownloaderClient itf, Class<?> downloaderService) {
mItf = itf;
mDownloaderServiceClass = downloaderService;
}
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
//this is the critical call that never happens
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.
mServiceMessenger = new Messenger(service);
mItf.onServiceConnected(
mServiceMessenger);
mBound = true;
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mServiceMessenger = null;
mBound = false;
}
};
#Override
public void connect(Context c) {
mContext = c;
Intent bindIntent = new Intent(c, mDownloaderServiceClass);
bindIntent.putExtra(PARAM_MESSENGER, mMessenger);
if ( !c.bindService(bindIntent, mConnection, 0) ) {
if ( Constants.LOGVV ) {
Log.d(Constants.TAG, "Service Unbound");
}
}
}
#Override
public void disconnect(Context c) {
if (mBound) {
c.unbindService(mConnection);
mBound = false;
}
mContext = null;
}
#Override
public Messenger getMessenger() {
return mMessenger;
}
}
DownloaderServiceMarshaller.java (in Google market expansion library, unchanged)
private static class Proxy implements IDownloaderService {
private Messenger mMsg;
private void send(int method, Bundle params) {
Message m = Message.obtain(null, method);
m.setData(params);
try {
mMsg.send(m);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public Proxy(Messenger msg) {
mMsg = msg;
}
#Override
public void requestAbortDownload() {
send(MSG_REQUEST_ABORT_DOWNLOAD, new Bundle());
}
#Override
public void requestPauseDownload() {
send(MSG_REQUEST_PAUSE_DOWNLOAD, new Bundle());
}
#Override
public void setDownloadFlags(int flags) {
Bundle params = new Bundle();
params.putInt(PARAMS_FLAGS, flags);
send(MSG_SET_DOWNLOAD_FLAGS, params);
}
#Override
public void requestContinueDownload() {
send(MSG_REQUEST_CONTINUE_DOWNLOAD, new Bundle());
}
#Override
public void requestDownloadStatus() {
send(MSG_REQUEST_DOWNLOAD_STATE, new Bundle());
}
#Override
public void onClientUpdated(Messenger clientMessenger) {
Bundle bundle = new Bundle(1);
bundle.putParcelable(PARAM_MESSENGER, clientMessenger);
send(MSG_REQUEST_CLIENT_UPDATE, bundle);
}
}
private static class Stub implements IStub {
private IDownloaderService mItf = null;
final Messenger mMessenger = new Messenger(new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REQUEST_ABORT_DOWNLOAD:
mItf.requestAbortDownload();
break;
case MSG_REQUEST_CONTINUE_DOWNLOAD:
mItf.requestContinueDownload();
break;
case MSG_REQUEST_PAUSE_DOWNLOAD:
mItf.requestPauseDownload();
break;
case MSG_SET_DOWNLOAD_FLAGS:
mItf.setDownloadFlags(msg.getData().getInt(PARAMS_FLAGS));
break;
case MSG_REQUEST_DOWNLOAD_STATE:
mItf.requestDownloadStatus();
break;
case MSG_REQUEST_CLIENT_UPDATE:
mItf.onClientUpdated((Messenger) msg.getData().getParcelable(
PARAM_MESSENGER));
break;
}
}
});
public Stub(IDownloaderService itf) {
mItf = itf;
}
#Override
public Messenger getMessenger() {
return mMessenger;
}
#Override
public void connect(Context c) {
}
#Override
public void disconnect(Context c) {
}
}
/**
* Returns a proxy that will marshall calls to IDownloaderService methods
*
* #param ctx
* #return
*/
public static IDownloaderService CreateProxy(Messenger msg) {
return new Proxy(msg);
}
/**
* Returns a stub object that, when connected, will listen for marshalled
* IDownloaderService methods and translate them into calls to the supplied
* interface.
*
* #param itf An implementation of IDownloaderService that will be called
* when remote method calls are unmarshalled.
* #return
*/
public static IStub CreateStub(IDownloaderService itf) {
return new Stub(itf);
}

Categories

Resources