Android Alarm manager not firing when device is in sleep mode - android

I'm trying to create a reminder app for tablets.
My problem is that if the tablet is in sleep mode the alarm isn't getting called.
i tried a lot off project on github none of them wase working when my tablet wase in sleep mode.
My code is the following:
The code to set the alarm:
Intent intent = new Intent(getApplicationContext(),RingAlarmReceiver.class);
Intent intent = new Intent("kidsplaylist.info.waketest.MyWakefulReceiver");
PendingIntent pIntent = PendingIntent.getBroadcast(getApplicationContext(),0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarm = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 30);
alarm.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pIntent);
The code of the receiver:
public class MyWakefulReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
WakeLocker.acquire(context);
// Start the service, keeping the device awake while the service is
// launching. This is the Intent to deliver to the service.
Intent service = new Intent(context, MyIntentService.class);
startWakefulService(context, service);
}
}
The code for the service that is supposed to ring the alarm:
public class MyIntentService extends IntentService {
public static final int NOTIFICATION_ID = 1;
private NotificationManager mNotificationManager;
public MyIntentService() {
super("MyIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
// Do the work that requires your app to keep the CPU running.
String song = Settings.System.DEFAULT_RINGTONE_URI.toString();
MediaPlayer mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setDataSource(getApplicationContext(), Uri.parse(song));
mediaPlayer.prepare();
mediaPlayer.setLooping(false);
mediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
}
// Release the wake lock provided by the WakefulBroadcastReceiver.
MyWakefulReceiver.completeWakefulIntent(intent);
}
}
Can anyone with experience in such thick advice me how to fix it
Thank's a lot
Avi
P.B: When the device is connected to the charger or when the screen is on it works OK
The problem is when the device screen is off.

Note that on Android 6.0+, Doze mode and app standby will affect AlarmManager events.
Beyond that, using an IntentService to directly play media will not work well. Once onHandleIntent() returns, the IntentService is destroyed, and your process will typically be terminated shortly thereafter.
I strongly recommend that you raise a Notification that uses this media as a ringtone. That would eliminate the need for the service entirely (you could raise the Notification in onReceive(), since that should be fairly quick to execute). It gives the user more control over whether the music plays (via Notification controls on Android 5.0+), and it gives the user a straightforward way to shut it up (swipe away the Notification).
If you insist upon playing the media yourself, you will need to use a regular Service and manage your own WakeLock, so that you can keep all that outstanding while the media is playing. Use an OnCompletionListener to stopSelf() the service and release() the WakeLock when the media is done.

Related

Notification Listener Service does not work after app is crashed

I have a problem on my app and I want to report this bug.
I develope the app which can crawls notifications using NotificationListenerService.
It works well.
But NotificationListenerService class has the problem I think.
Because, If the app is crashed, app can't crawl the notification at all,
UNTIL the phone reboots.
Is anyone who can solve this problem??
Please help me.
The bug is very clear!! But It is not easy to find the solution ....
If do you have already permissions then:
In your service class or another service/activity you can switch the "component hability" to listen notifications:
public void tryReconnectService() {
toggleNotificationListenerService();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
ComponentName componentName =
new ComponentName(getApplicationContext(), NotificationReaderV2Service.class);
//It say to Notification Manager RE-BIND your service to listen notifications again inmediatelly!
requestRebind(componentName);
}
}
/**
* Try deactivate/activate your component service
*/
private void toggleNotificationListenerService() {
PackageManager pm = getPackageManager();
pm.setComponentEnabledSetting(new ComponentName(this, NotificationReaderV2Service.class),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
pm.setComponentEnabledSetting(new ComponentName(this, NotificationReaderV2Service.class),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
}
Your notification listener, is a SERVICE, it can be killed by System, you can do your service as FOREGROUND to drastically decrease the probability that the system will kill your service.
#Override
public void onListenerConnected() {
super.onListenerConnected();
Log.d(TAG, "Service Reader Connected");
Notification not = createNotification();
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (mNotificationManager != null) {
mNotificationManager.notify(NOTIFICATION_ID, not);
}
startForeground(NOTIFICATION_ID, not);
//Alarm to auto - send Intents to Service to reconnect, you can ommit next line.
alarmIt();
}
If do you like so more "safe", you can to programming not-friendly battery alarms, try to use inexact alarms please, the user's battery will be happy:
private void alarmIt() {
Log.d(TAG, "ALARM PROGRAMMATED at"+HotUtils.formatDate(new Date()));
Calendar now = Calendar.getInstance();
now.setTimeInMillis(System.currentTimeMillis());
now.set(Calendar.MINUTE, now.get(Calendar.MINUTE) + 1);
Intent intent = new Intent(this, NotificationReaderV2Service.class);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.setAction(REBIND_ACTION);
PendingIntent pendingIntent = PendingIntent.getService(this, 0,
intent, 0);
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
//The alarms that are repeated are inaccurate by default, use RTC_WAKE_UP at your convenience.
//Alarm will fire every minute, CHANGE THIS iF DO YOU CAN, you can't use less than 1 minute to repeating alarms.
manager.setRepeating(AlarmManager.RTC_WAKEUP, now.getTimeInMillis(), 1000 * 60 * 1, pendingIntent);
}
and next read the Intent to reconnect service binding:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Notification service onStartCommandCalled");
if (intent!=null && !HotUtils.isNullOrEmpty(intent.getAction()) && intent.getAction().equals(REBIND_ACTION)){
Log.d(TAG, "TRYING REBIND SERVICE at "+HotUtils.formatDate(new Date()));
tryReconnectService();//switch on/off component and rebind
}
//START_STICKY to order the system to restart your service as soon as possible when it was killed.
return START_STICKY;
}
Keep in mind that doing all these steps you can sure that your service will be killed anyway by the system but this code will restart the service and make it harder to kill it.
Maybe, you should consider using PARTIAL_WAKE_LOCK with your service and execute it in a process independently (:remote) if you want even more certainty (Maybe this is useless)
I would like to add a common error that is often followed, NEVER override the onBind and onUnbind method or overwrite the INTENT ACTION.
This will cause your service to not be connected and never run onListenerConnected
Keep the Intent as it is, in most cases you do not need to edit it.
I see exactly the same on this. The only "solution" I've found was to have the notification listener running in a separate process. Then if the rest of the app crashes it doesn't stop the listener. So it's only then specifically notification listener service crashes that require the reboot.
Seems a terrible and over complicated solution though.
I had the same problem. Here are few things that I did and now it works wonderfully for me.
Override onStartCommand, call super and return START_STICKY;
Override onNotificationRemoved, call super and add a toast so that you know in android itself that you service has not died yet whenever you swipe a notification.
Exclude your app from Battery saving list (Settings-> Battery-> Power Saving Exclusion)
Post this the service never dies even after the main app's crash. I dont need to reboot now to restart it.

Repetitive service design Android

I have following design in my app: I have a one activity that sets repeating alarm that launches receiver that starts my service. Every minute. In my service, I set Start Sticky but once Android decides to kill my service, I can't get it to restart. It's Android 4.4.2. Here is my code:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent i1 = new Intent(MainActivity.this, AlarmReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(MainActivity.this, 0, i1, PendingIntent.FLAG_UPDATE_CURRENT);
am.setRepeating(AlarmManager.RTC_WAKEUP, 0, 60 * 1000, pi);
}
here is the receiver
public void onReceive(Context arg0, Intent arg1) {
// For our recurring task, we'll just display a message
Log.d(Constants.TAG, "Starting Service");
Intent intent = new Intent(arg0, MyLocationService.class);
arg0.startService(intent);
}
and service:
private static PowerManager.WakeLock wl;
private static OkHttpClient client;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(Constants.TAG, "Service onStartCommand");
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Constants.TAG);
wl.acquire();
client = new OkHttpClient();
new GetLocation(MyLocationService.this).execute();
return START_STICKY;
}
You're most likely seeing an interaction with power management and alarms. Starting with API 19 all alarms are inexact by default, so they will be collated with other alarms. Further, once the device has entered into a lower power state, alarms are delivered to BroadcastReceivers and the device guaranteed to stay awake as long as the receiver is executing its onReceive() method. Once it returns from that method (and any other BroadcastReceiver associated with the alarm runs), the device will immediately go to a low power state again. Since your app had previously been killed, the Service is no longer running and will not get CPU time.
The way to resolve this is to use a WakefulReceiver which takes a wakelock when it runs onReceive(), starts your Service for processing. The Service will then release the wakelock when it is done processing. This article will give you a good explanation of this: http://po.st/7UpipA
Note that waking up every minute is going to seriously degrade the battery life of the device, so you should consider backing this off.

Android: Start Service at certain Battery Level

I want to start an IntentService if the battery level reaches a certain value while charging.
However, I dont want to register a BroadcastReceiver for android.intent.action.BATTERY_CHANGED in the AndroidManifest.xml because i need the service only started at a certain level (e.g. 40%) and only while charging.
Another idea would be starting a service that runs as long as the phone is charged and register a receiver programmatically.
A third idea would be to periodically start a service with AlarmManager and check battery level.
What is the best way?
Solution:
I have implemented the fourth idea from answer below.
final int PI_REQUEST_CODE = 123456;
int pref_BatteryUpdatePeriod = 120000; // 2 minutes
// set repeating alarm manager
Intent monitorIntent = new Intent(this, CheckBatteryReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this.getApplicationContext(), PI_REQUEST_CODE, monitorIntent, 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, Calendar.getInstance().get(Calendar.MILLISECOND), pref_BatteryUpdatePeriod, pendingIntent);
and the broadcast receiver for the alarm:
public class CheckBatteryReceiver extends BroadcastReceiver {
private int targetBatterylevel = 40;
#Override
public void onReceive(Context context, Intent intent) {
// get sticky intent
Intent batteryStatusIntent = context.getApplicationContext()
.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
int batteryLevel = batteryStatusIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 50);
// evaluate battery level
if(batteryLevel >= targetBatterylevel){
// start service to stop charging
} else {
// continue charging
}
}
}
Note: I had to use context.getApplicationContext() instead of context otherwise the app would crash with an exception that I can't register a receiver within a broadcastreceiver
I dont want to register a BroadcastReceiver for android.intent.action.BATTERY_CHANGED in the AndroidManifest.xml because i need the service only started at a certain level (e.g. 40%) and only while charging.
That does not work anyway, as you cannot register for that broadcast in the manifest.
Another idea would be starting a service that runs as long as the phone is charged and register a receiver programmatically.
Yuck.
A third idea would be to periodically start a service with AlarmManager and check battery level.
A fourth idea is to use AlarmManager, as you suggest, but bypass the service at the outset. Just have AlarmManager invoke a BroadcastReceiver. It can check the battery level by calling context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)) (on the context passed into onReceive()). The Intent returned by registerReceiver() will be the last broadcast of ACTION_BATTERY_CHANGED. If the battery level is at or below your desired threshold, then start your IntentService. You may need to adopt the WakefulBroadcastReceiver or WakefulIntentService pattern if your alarm will wake up the device. Also allow the user to choose the polling period, so they can trade off precision for better device performance.
I would go with the fourth idea.
It doesn't sound too bad to have a service running, reacting to android.intent.action.BATTERY_CHANGED as long as it doesn't do anything when you don't need it to. That is, have a check for the interesting battery level and charging state early and just exit if you have nothing to do.
If you want to complicate things, register a mini service which only has the task of starting your main service when conditions are right. However, I don't see what benefit that would have.
public class Main extends Activity {
private TextView batteryTxt;
private BroadcastReceiver mBatInfoReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context ctxt, Intent intent) {
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
batteryTxt.setText(String.valueOf(level) + "%");
}
};
#Override
public void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.main);
contentTxt = (TextView) this.findViewById(R.id.batteryTxt);
this.registerReceiver(this.mBatInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
}
}

IntentService is killed when app is stopped

I've this IntentService:
public class ServiceUpdateNewResults extends IntentService{
private void setAlarmToCheckUpdates() {
Calendar calendar = Calendar.getInstance();
//calendar.add(Calendar.DAY_OF_YEAR, 1); //dema
//calendar.set(Calendar.HOUR_OF_DAY, 22); //a les 10
calendar.add(Calendar.SECOND, 20);
Intent myIntent = new Intent(this.getApplicationContext(), ReceiverCheckUpdates.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this.getApplicationContext(), 0, myIntent,0);
AlarmManager alarmManager = (AlarmManager)this.getApplicationContext().getSystemService(this.getApplicationContext().ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC, calendar.getTimeInMillis(), pendingIntent);
}
public ServiceUpdateNewResults() {
super("ServiceUpdateNewResults");
}
#Override
protected void onHandleIntent(Intent intent) {
//fem les coses.
//Toast.makeText(this.getApplicationContext(), "holaa", Toast.LENGTH_LONG).show();
setAlarmToCheckUpdates();
Log.d("debugging","hello");
}
}
And this is calling a BroadCastReceiver every 20 seconds, which ends up calling this service, and this is going to happen "forever". (in a future it will be 1 day, not 20 seconds).
This is the Receiver:
public class ReceiverCheckUpdates extends BroadcastReceiver{
Context context;
#Override
public void onReceive(Context context, Intent intent){
this.context = context;
Intent service1 = new Intent(context, ServiceUpdateNewResults.class);
context.startService(service1);
}
}
This is working perfectly, but if I stop the app from Android settings, the service is also stopped. I want to avoid this. I want that if the App is closed, the service should keep working.
Is it possible?
Actually, when is a service killed ?
if I stop the app from Android settings, the service is also stopped
If by "stop the app from Android settings", you mean that you press the Force Stop button, your app will not run again until something manually runs one of your components (e.g., user launches your activity). More specifically in this case, your alarms are unscheduled.
I want to avoid this.
Then do not press the "Force Stop" button.
I want that if the App is closed, the service should keep working.
In any non-"Force Stop" scenario, the alarms will keep firing (at least until the device falls asleep, given your current implementation).
The IntentService is part of your app. If the system destroys your app, it will destroy the IntentService. You can reduce the chances of this happening by putting the IntentService in a separate process, but you can't stop the system from destroying an IntentService. You can make it highly unlikely that the system will destroy a Service; to do that, you use a "foreground" Service. However, you should avoid doing this unless you really really need to. In addition, you can't have a foreground IntentService, so you'll have to add your own background Handler and HandlerThread.

Service pauses on screen lock

For testing purposes i have made a service that beeps
every 1 minute. (No client-server interface yet). It beeps okay when
the screen in on, but when it goes to sleep the beeping stops.
I am making an application that has to periodically poll the a server
for something.
For this, I am trying to create a service that'll constantly be
running in the background, poll the server every 1 min and then based
on the reply from server it shall generate a task bar notification.
I have a test activity with two buttons, 1 to start and the other to
stop the service. And one service class named S_PS_PollService
The setOnClickListener of 'Start Activity' button contains:
Thread pollServiceThread = new Thread() {
public void run() {
startService(new Intent(MM_MainMenu.this,
S_PS_PollService.class));
}
};
pollServiceThread.start();
The 'Stop Activity' button simply has:
stopService(new Intent(MM_MainMenu.this, S_PS_PollService.class));
Following are the methods from S_PS_PollService class:
public void onCreate() {
pollSound = MediaPlayer.create(this, R.raw.chirp);
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent myIntent = new Intent(this, S_PS_PollService.class);
pendingIntent = PendingIntent.getService(this, 0, myIntent, 0);
// for wake lock
pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag")
// for calendar
calendar = Calendar.getInstance();
}
Onstart:
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
wl.acquire();
pollSound.start();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.MILLISECOND, 60000);
alarmManager.set(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(), pendingIntent);
wl.release();
}
Whenever the alarm kicks off onStart() method is executed, making the
beep and setting new alarm. But it works only as long as screen is on.
I have tried for https://github.com/commonsguy/cwac-wakeful but didnt
get it. Relatively new to android ...
Please help me, im very desperate :) Thanks, !
You have to use the AlarmManager, there are plenty of posts here on stackoverflow.
You want to acquire a partial wake lock (leaving the CPU running whenever sleep is entered on the device) as suggested by your code.
The issue is your presumably overriden on start releases the wake lock. You want to release your wakeLock in onDestroy .. once your service is finished running.
This finally worked for me.
Download the CWAC-WakefulIntentService.jar from https://github.com/commonsguy/cwac-wakeful
add a class in your project
import com.commonsware.cwac.wakeful.WakefulIntentService;
public class WakeService extends WakefulIntentService {
public WakeService(String name) {
super(name);
}
#Override
protected void doWakefulWork(Intent intent) {
}
}
now add the following line in your code where ever you want to repeat the loop and wake the device up
WakefulIntentService.sendWakefulWork(this, S_WS_WakeService.class);

Categories

Resources