BLE Advertising on Background - java

I´m working on a android app of inside of a Contact tracing project. What i need right now is make a background process (idk if, service, foreground service, worker, etc), that can keep the BLE advertiser sending beacons even if the application closes.
Is working without issues on the main thread but when i try to convert in a service, it can´t start.
This is the service class that i tried:
public class AdvertiserService extends Service {
private BluetoothLeAdvertiser advertiser;
private AdvertiseSettings settings;
private AdvertiseCallback callback;
#Override
public void onCreate() {
super.onCreate();
advertiser = BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser();
settings = new AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
.setConnectable(false)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
.setTimeout(0)
.build();
callback = new AdvertiseCallback() {
#Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
}
#Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
}
};
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
long userId =
Long.parseLong(PreferenceManager.getDefaultSharedPreferences(this)
.getString(getString(R.string.user_id_pref_key), "0"));
MeetingBeacon beacon = new MeetingBeacon(userId, userId);
Log.d("MEETING_BACON", beacon.getBeaconUUID().toString() );
AdvertiseData data = new AdvertiseData.Builder()
.addServiceData(beacon.getBeaconUUID(), beacon.getBeaconData())
.build();
advertiser.startAdvertising(settings, data, callback);
return START_STICKY;
}
#Override
public void onDestroy(){
Log.d("ADVER-Sv","Automate service destroyed...");
advertiser.stopAdvertising(callback);
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public boolean stopService(Intent name) {
Log.d("SCAN-Sv","Automate service stop...");
advertiser.stopAdvertising(callback);
stopSelf();
return super.stopService(name);
}
}
And i´m starting this service from main thread as:
advertiserService = new Intent(this, AdvertiserService.class);
if(advertiserEnabled) {
ComponentName ret = startService(advertiserService);
if(ret != null){
Log.d("SERVICE:", sv.toString());
}
Log.d("SERVICE ", "fail");
}
But ret is always null , so the service cannot start.
Idk if using service is the better solution in this case.
Anyone can suggest which could be the better solution to solve this problem?

Related

bind to already running Service

Heres my problem:
I am implementing a music player (PlayerActivity.java / xml) which is bound to a service (PlayerService.java) that basically is just an instance of a musicplayer so that it can run in the background. When lefting the app or changing the activity and the restarting the activity i want to bind to the still running service without stopping or restarting it. I have tried using only bindService() but that made me face the problem: somehow without calling startService() before bindService the Service doesn't get initialized or it takes a few milliseconds so that the functions only get null when accessing service functions.
Here is my service class:
public class playerService extends Service {
public boolean running = false;
public MediaPlayer mediaPlayer;
public boolean isPrepared = false;
private String url;
public class serviceBinder extends Binder {
public playerService getService() {
return playerService.this;
}
}
public boolean isRunning(){
return running;
}
private IBinder mBinder = new serviceBinder();
#Nullable
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.w("Service", "started");
mediaPlayer = new MediaPlayer();
running = true;
Log.e("!!!", running + "");
return START_STICKY;
}
public void pause() {
mediaPlayer.pause();
}
public void resume() {
mediaPlayer.start();
}
public void setupPlayer() {
try {
if (mediaPlayer != null) {
mediaPlayer.reset();
}
} catch (Exception e) {
Log.e("MediaPlayer", e.getMessage());
}
try {
mediaPlayer.setDataSource(url);
mediaPlayer.prepareAsync();
} catch (Exception e) {
Log.e("MediaPlayerToo", e.getMessage());
}
}
public void reset(){
mediaPlayer.reset();
}
public void updateUrl(String url){
this.url = url;
}
public void start(){
mediaPlayer.start();
}
public int getCurrentPosition(){
return mediaPlayer.getCurrentPosition();
}
public int getDuration(){
return mediaPlayer.getDuration();
}
#Override
public void onDestroy() {
super.onDestroy();
mediaPlayer.stop();
mediaPlayer.reset();
}
}
I experimented with waiting until the Service has started with a boolean. But when i add any kind of code
after
mService = binder.getService();
startService(intent);
it seems to not get created at all.
Here is also my onServiceConnected class
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
playerService.serviceBinder binder = (playerService.serviceBinder) service;
Intent intent = new Intent(getApplicationContext(), playerService.class);
mService = binder.getService();
startService(intent);
//... <- adding code here will result in the Service not starting?!!
}
I know that my problem is quite confusing, but this was the best i could come up explaining it. I would be really glad if you had any idea because this problem stops me from deployment. Thank you very much!!

Service getting killed even if the app is in Background

I am using a Service for performing some task, which should run only if the app is in background, moreover the service runs for sometime and after sometime, it gets destroyed. Earlier this was working completely fine, but don't know where i am doing wrong.
Here is the code of my Service:
public class MyService extends Service {
Context context;
public static final String TAG = MyService.class.getSimpleName();
public MyService(Context applicationContext) {
super();
context = applicationContext;
Log.i("myservice", "here service created!");
}
public MyService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "[onCreateService]");
super.onStartCommand(intent, flags, startId);
// Code
registerOverlayReceiver();
context = this;
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterOverlayReceiver();
Log.i("EXIT", "ondestroy!");
}
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void unregisterOverlayReceiver() {
if (myReceiver != null) {
unregisterReceiver(myReceiver);
}
}
private static final String ACTION_DEBUG = "abc.action.DEBUG";
private void registerOverlayReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(ACTION_DEBUG);
registerReceiver(myReceiver, filter);
}
private BroadcastReceiver myReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG, "[onReceive]" + action);
if (action.equals(Intent.ACTION_SCREEN_ON)) {
showMyActivity();
} else if (action.equals(ACTION_DEBUG)) {
showMyActivity();
}
}
};
private void showMyActivity() {
Intent intent = new Intent();
intent.setClass(this, MyActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
I have debugged but couldn't find out the problem for the same.
Anybody who came across anything like this can help me out.

how to call method after 2 minutes in background

i need to call a method after 2 minutes. i have tried handler and timer but when app close then it doesn't work. then i have call timer in service and start service on button click and stop service after 2 minutes. it works but problem is sometime service calls itself. below is my code.
MyService class
public class MyService extends Service {
private static Retrofit retrofit = null;
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
new CountDownTimer(120000, 1000) {
public void onTick(long millisUntilFinished) {
Log.d("message", "start");
}
public void onFinish() {
Log.d("message", "finish");
callMyMethod();
stopService(new Intent(MyService.this, MainActivity.class));
}
}.start();
}
MainActivity class from where i start service.
stopService(new Intent(getContext(), MyService.class));
startService(new Intent(getContext(), MyService.class));
Manifest
<service android:name="MyService" android:enabled="true" android:exported="true"/>
NOTE: I WANT TO CALL SERVICE ONLY ON BUTTON CLICK AND STOP SERVICE AFTER 2 MINUTES.
please need help to solve this issue or If there is other good solution other than services.
Updated: if network response in not success how can i handle to retry.
MyService class
public class MyService extends Service {
private static Retrofit retrofit = null;
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
new CountDownTimer(120000, 1000) {
public void onTick(long millisUntilFinished) {
Log.d("mess", "start");
}
public void onFinish() {
Log.d("mess", "finish");
selectDriverForJOb();
stopSelf();//(new Intent(MyService.this, MainActivity.class));
}
}.start();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
private void selectDriverForJOb() {
SharedPreferences sharedPreferences = getSharedPreferences("Login_credentials", MODE_PRIVATE);
String user_id = sharedPreferences.getString("userID", null);
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
APIServices_interface selectDriver_interface = retrofit.create(APIServices_interface.class);
Call<SelectDriverforJobResult> call = null;
try {
call = selectDriver_interface.selectDriverforJob(user_id);
} catch (Exception e) {
e.printStackTrace();
}
if (call != null) {
try {
call.enqueue(new Callback<SelectDriverforJobResult>() {
#Override
public void onResponse(Call<SelectDriverforJobResult> call, Response<SelectDriverforJobResult> response) {
SelectDriverforJobResult selectDriver = response.body();
String message = selectDriver.getMessage();
if(!(message.equalsIgnoreCase("success"))){
}
}
#Override
public void onFailure(Call<SelectDriverforJobResult> call, Throwable t) {
Log.d("mess : ", "Error Updating ");
//want to retry
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
override onStartCommand in service and return START_NOT_STICKY it will not restart the
public void onStartCommand()
{
return START_NOT_STICKY
}
and use stopSelf() insted of stopService(new Intent(getContext(), MyService.class));
Your Service will not run after app close, Read background restriction.
And this is not really a Service use case.
The solution can be to your problem is AlarmManager . Set an alarm for exact after 2 minutes and your Receiver willget called when its triggered . Before using AlarmManger give a read to setExactAndAllowWhileIdle() and setExact().
Alternatively you can also use WorkManager to set a Exact Job . But This will be an overkill if you do not need Job Constraints. So if your work does not depends of any Constraints like Network connection or other you can go with AlarmManger.
PS:- If your task depends on a Network connection then you should go with WorkManager. Read Schedule tasks with WorkManager .

Immortal Service

I need to implement a project, such as chat. We decided to use the Socket.IO library. FCM is not considered. To receive messages in the background using Service. Here:
public class SocketServiceProvider extends Service {
private Socket mSocket;
private final String EVENT_NEW_MESSAGE = "new_message";
private final String LOG_TAG = "SocketServiceProvider";
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
Log.e(LOG_TAG, "created()");
realm = Realm.getDefaultInstance();
startForeground(1, new Notification());
if (mSocket == null)
mSocket = BaseApplication.getSocket();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(LOG_TAG, "onStartedCommand()");
startSocket();
return START_STICKY;
}
private void startSocket() {
if (mSocket.connected()){
stopSocket();
}
mSocket.on(Socket.EVENT_CONNECT, onConnect);
mSocket.on(EVENT_NEW_MESSAGE, onNewMessage);
mSocket.connect();
}
private void stopSocket() {
mSocket.off();
mSocket.disconnect();
}
private Emitter.Listener onConnect = new Emitter.Listener() {
#Override
public void call(Object... args) {
new Handler(Looper.getMainLooper()).post(() -> {
if (mSocket.connected()) {
isOnline = true;
Log.e(LOG_TAG, "Connected!");
}
});
}
};
private Emitter.Listener onNewMessage = args -> {
final JSONObject data = (JSONObject) args[0];
final String username;
final String message;
try {
username = data.getString("from");
message = data.getString("message");
} catch (JSONException e) {
Log.e("MainActivity", e.getMessage());
return;
}
Log.e(LOG_TAG, username + " wrote: " + message);
};
#Override
public void onDestroy() {
super.onDestroy();
Log.e(LOG_TAG, "onDestroy()");
stopSocket();
ContextCompat.startForegroundService(this, new Intent(this, SocketServiceProvider.class));
}
}
The only problem is that when the phone goes into Doze mode, messages do not come. Tried to wake up with AlarmManager in onTaskRemoved(), onDestroy(), unsuccessfully.
Even with onDestroy() tried to call BroadcastReceiver, so that it started back my Service, just did not understand why, but its onReceive() method does not work.
Here is my last option, the code that posted. There is I usе startForegroundService. And this option worked, at least not dying. Only in this case, the battery discharges quickly
Googled, Write that using JobIntentService can be implemented, but nowhere described in detail.
Question: How can this be done and how did you implement such tasks? And how can this be achieved with JobIntentService?

The first transition is always the same - Activity Recognition API - Activity Transition

I use activity recognition api and activity transitions. When I first run the app, the first transition is always the. In this app it's WALKING-ENTER. When I tried with only IN_VEHICLE-ENTER and IN_VEHICLE-EXIT in transitions, it was IN_VEHICLE-ENTER. I thought about ignoring the first transition but a device I've tested on didn't have such a problem. The devices that had these problem were Android 8.1, and the device didn't have the problem was 6.0.
MainActivity extends AppCompatActivity
private static Intent serviceIntent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int PERMISSION_ALL = 1;
String[] PERMISSIONS = {Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION};
if(!hasPermissions(this, PERMISSIONS)){
ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSION_ALL);
}
Button button = findViewById(R.id.button);
button.setOnClickListener(
new View.OnClickListener() {
public void onClick(View view) {
serviceIntent = new Intent(MainActivity.this, ServiceS.class);
MainActivity.this.startService(serviceIntent);
}
});
}
public static boolean hasPermissions(Context context, String... permissions) {
if (permissions != null) {
for (String permission : permissions) {
if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
}
return true;
}
ServiceS extends Service
public ServiceS() {
super();
}
public static void locationArrived(Context context, Location location) {
Log.d("hmm: ", location.toString());
}
#SuppressLint("MissingPermission")
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onCreate() {
initActivityTransition();
super.onCreate();
}
#Override
public void onDestroy() {
Log.d("hmm: ", "Updates stopped!");
Task task = ActivityRecognition.getClient(this)
.removeActivityTransitionUpdates(activityPendingIntent);
super.onDestroy();
}
// ACTIVITY TRANSITION BLOCK START
private static List<ActivityTransition> transitions = new ArrayList<>();
private static PendingIntent activityPendingIntent;
private static ActivityTransitionRequest transitionRequest;
void initActivityTransition(){
transitions.add(
new ActivityTransition.Builder()
.setActivityType(DetectedActivity.IN_VEHICLE)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
.build());
transitions.add(
new ActivityTransition.Builder()
.setActivityType(DetectedActivity.IN_VEHICLE)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
.build());
transitions.add(
new ActivityTransition.Builder()
.setActivityType(DetectedActivity.WALKING)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
.build());
transitions.add(
new ActivityTransition.Builder()
.setActivityType(DetectedActivity.WALKING)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
.build());
transitions.add(
new ActivityTransition.Builder()
.setActivityType(DetectedActivity.STILL)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
.build());
transitions.add(
new ActivityTransition.Builder()
.setActivityType(DetectedActivity.STILL)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
.build());
Intent activityIntentService = new Intent(this, TransitionReceiver.class);
activityPendingIntent = PendingIntent.getBroadcast(this, 1, activityIntentService, PendingIntent.FLAG_UPDATE_CURRENT);
Log.d("hmm: ","DriveBuddyService - initActivityTransition");
}
static void transitionArrived(final ActivityTransitionEvent event, final Context context){
Log.d("hmm: ", event.toString());
Toast.makeText(context, event.getActivityType() + "-" + event.getTransitionType(), Toast.LENGTH_LONG).show();
}
#Override
public void onStart(Intent intent, int startId) {
if(transitionRequest==null) {
transitionRequest = new ActivityTransitionRequest(transitions);
}
Task task = ActivityRecognition.getClient(this)
.requestActivityTransitionUpdates(transitionRequest, activityPendingIntent);
super.onStart(intent, startId);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
TransitionReceiver extends BroadcastReceiver.
public void onReceive(final Context context, Intent intent) {
Log.d("hmm: ","DriveBuddyTransitionReceiver - Enter");
if (ActivityTransitionResult.hasResult(intent)) {
ActivityTransitionResult result = ActivityTransitionResult.extractResult(intent);
for (ActivityTransitionEvent event : result.getTransitionEvents()) {
ServiceS.transitionArrived(event, context);
}
}
}
I observed the same thing too. It seems that the last activity is kept in cache or something like that. I saw this happening on Android 7.1 and Android 8.1.
To counter this I check the elapsed time of when the transition happened. I ignore the callback if the transition happened over 30 seconds ago.
This is what my code looks like:
#Override
public void onReceive(Context context, Intent i) {
if (ActivityTransitionResult.hasResult(i)) {
ActivityTransitionResult result = ActivityTransitionResult.extractResult(i);
for (ActivityTransitionEvent event : result.getTransitionEvents()) {
//continue only if the activity happened in the last 30 seconds
//for some reason callbacks are received for old activities when the receiver is registered
if(((SystemClock.elapsedRealtime()-(event.getElapsedRealTimeNanos()/1000000))/1000) <= 30) {
//activity transition is legit. Do stuff here..
}
}
}
}
I've typically seen callbacks arrive 0-10 seconds of them occurring.

Categories

Resources