I have this application that needs to run a service (background) that beeps periodically.
The phone needs to beep the entire day for 5 seconds every one minute (used a handler in the service). I have implemented this service which does this perfectly, but when the phone goes into deep sleep mode, the execution stops of this handler stops. Using this answer from the question in SO, I managed to use wake locks and it works fine. But when I explicitly put the phone in deep sleep mode, the handler stops executing. Where do I place the wakelock in the service. Code snippet below.
public class PlaySound extends Service{
PowerManager.WakeLock wl ;
PowerManager pm;
private SoundManager mSoundManager;
boolean wakeUpFlag = false;
#Override
public void onCreate(){
super.onCreate();
mSoundManager = new SoundManager();
mSoundManager.initSounds(getBaseContext());
mSoundManager.addSound(1, R.raw.sound);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startservice();
return START_STICKY;
}
private void startservice() {
System.out.println("Started the service");
timer.scheduleAtFixedRate( new TimerTask() {
public void run() {
toastHandler.sendEmptyMessage(0);
}
}, 0, 60000);
}
private final Handler toastHandler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
result =start();
System.out.println("result"+result);
close();
}
};
protected void close() {
try {
if(wakeUpFlag){
wl.release();
System.out.println("Released the wakelock");
}
if(!pm.isScreenOn()){
System.out.println("Screen is off - back to sleep");
pm.goToSleep(1000);
}
else{
System.out.println("Screen is on - no need to sleep");
}
bs.close();
writer.close();
System.out.println("Closed socket and writer");
System.out.println("Size of file:"+f.length()/1024);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
public void start(){
try{
wakeUpFlag = false;
pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
if(!pm.isScreenOn()) {
wakeUpFlag = true;
wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE,"CollectData");
System.out.println("Screen off - wake lock acquired");
wl.acquire();
}
else{
System.out.println("Screen on - no need of wake lock");
}
}
catch(Exception e){
e.printStackTrace();
}
mSoundManager.playSound(1);
}
I dont think you are using the correct flag accorinding to the android documentation fior PowerManager:
*If you hold a partial wakelock, the CPU will continue to run, irrespective of any timers and even after the user presses the power button. In all other wakelocks, the CPU will run, but the user can still put the device to sleep using the power button.
In other words, try using PARTIAL_WAKE_LOCK as this is the only one that gurantees the cpu to run
Follow the pattern Mark Murphy provides with the WakefulIntentService. I would suggest picking up his books, not only for the detailed explanation of this class and example he includes in one of them, but for the other wealth of information you'll find in them.
I just recently implemented this pattern for my main app and this class works like a charm.
I think you'd be better off using android.app.AlarmManager to schedule a wakeup alarm. Be careful though - you don't want to do any long-running work in your onReceive() method as that's normally called on the main thread, and will hang your activity. You'll still need to acquire the wakelock for the duration of your task to prevent the phone sleeping part-way through.
Related
I´m trying to have a service that executes some code in a Handler at every second. While the phone is connected to the PC everythings running fine. However as soon as I start the service while not connected to the PC and I lock the phone, the periodic task just stops executing and executes all posted events when the app is opened again.
I´ve tried adding a PARTIAL_WAKE_LOCK in the onCreate of the Service but it doesn´t help at all.
Service.java
private PowerManager.Wakelock mWakeLock;
private Handler mHandler;
private Timer mTimer;
#Override
public void onCreate() {
super.onCreate();
Notification notification = createNotification();
startForeground(1, notification);
// create and acquire the WakeLock
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "APP:LockName");
mWakeLock.acquire();
// create Handler for periodic code execution
HandlerThread thread = new HandlerThread("PeriodicThread", THREAD_PRIORITY_BACKGROUND);
thread.start();
Looper looper = thread.getLooper();
mHandler = new Handler(looper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
mTimer = new Timer();
mTimer.scheduleAtFixedRate(new PeriodicTask(), 0, 1000);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
mTimer.cancel();
mWakeLock.release();
}
private class PeriodicTask extends TimerTask {
#Override
public void run() {
mHandler.post(() -> { // periodic task here});
}
}
I would expect the service to execute normally when the screen is locked, since the whole execution is protected by the PARTIAL_WAKE_LOCK.
Or are WakeLocks Thread-specific and I need to acquire a lock for the newly created Thread?
Many Thanks for your help
Edit 1:
I´ve tried adding the permission REQUEST_IGNORE_BATTERY_OPTIMIZATIONS to the app and even then the behavior is phone dependent. On a Samsung phone it works now as intended and the task is executed each second. on Huawei phone there´s no change and the periodic task is only executed when the app is opened again.
Is there a way to at least secure the same behaviour on different phones?
I am working with an android service. I want the service run always even when device is sleep. But my service is stop when my device is sleep. this is my code
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
isRunning = true;
mythread = new Thread() {
#Override
public void run() {
while (isRunning) {
turnOnScreen(60000*3);
//Download some information by android Volley request
downloadInfo();
try {
Thread.sleep(60000*2);
} catch (InterruptedException e) {
isRunning = false;
e.printStackTrace();
}
}
}
};
if (isRunning) {
if (!mythread.isAlive()) {
mythread.start();
}
}
return START_REDELIVER_INTENT;
}
And turnOnScreen method is
private void turnOnScreen(long milliSeconds){
PowerManager mgr = (PowerManager)this.getSystemService(this.POWER_SERVICE);
PowerManager.WakeLock wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
wakeLock.acquire(milliSeconds);
}
Please help anyone. Thanks in advance.
I want the service run always even when device is sleep
Your users may disagree with this. Certainly lots of users have complained about poor battery life, which is why Google and device manufacturers have taken steps to stop developers from doing things like this.
But my service is stop when my device is sleep
On Android 8.0+, your service will stop running after one minute.
On Android 6.0+, your approach will not work once the device goes into Doze mode. Some manufacturers implemented similar restrictions prior to Android 6.0. Plus, if downloadInfo() takes longer than a minute, the device may fall asleep between wakelocks.
I tried to use the following code trigger toast as background service but it gets executed for 20 times, it was not working till 100. With thread it is not working gives error.
Felt service get destroyed.
How to trigger notification with 30 minutes difference as a background service, though app is closed,
I need to display Good morning, Good Afternoon, Good Evening and Good night as a Notification.
without any internet support.
Is following procedure not ok? I think so. How to do this?
import android.app.Service;
public class HelloService extends Service {
private static final String TAG = "HelloService";
int i=0;
private boolean isRunning = false;
#Override
public void onCreate() {
Log.i(TAG, "Service onCreate");
Toast.makeText(this, " On create Hello Service Started", Toast.LENGTH_LONG).show();
isRunning = true;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Service onStartCommand");
for (;i<100; i++) {
try {
// Thread.sleep(1000);
Toast.makeText(getApplicationContext(), "Hello Service On Loop"+i , Toast.LENGTH_LONG).show();
//
} catch (Exception e) {
}
}
//Stop service once it finishes its task
// i++;
stopSelf();
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent arg0) {
Log.i(TAG, "Service onBind");
return null;
}
#Override
public void onDestroy() {
isRunning = false;
Log.i(TAG, "Service onDestroy");
}
}
onStartCommand() is called on the main (UI) thread. If you execute a loop inside onStartCommand(), Android will kill the process after about 30 seconds with an ANR (Application Not Responding) because you cannot block the main (UI) thread.
You can do what you want either using AlarmManager to set a timer that will start your Service or trigger a BroadcastReceiver at certain times, or you can post a Runnable to a Handler in onStartCommand() with a certain delay and do whatever you want in the Runnable, or you can start a background thread in onStartCommand() and the background thread can loop and sleep or whatever and then do what you want.
In any case, you cannot show a Toast every second. This will flood the UI with toasts and either Android will dump most of them (ignore them) or the UI will be so busy showing them that Android will kill your app due to ANR or the user will just uninstall your app!
In my fragment is a button, which when clicked will start a service, which polls sensors, and uses an executor service to store sensor data to the database. The onClick of the fragment looks like this:
public void onClick(View v) {
if (!recordingStarted){
try{
recordingStarted = true;
mainActivity.startService(new Intent(mainActivity, SensorService.class));
startButton.setText(getResources().getString(R.string.start_button_label_stop));
Snackbar.make(coordinatorLayout, "Recording...", Snackbar.LENGTH_SHORT).show();
} catch (SQLException e){
mainActivity.logger.e(getActivity(),TAG, "SQL error insertSubject()", e);
}
} else {
dialog = new ProgressDialog(mainActivity);
dialog.setTitle("Stop recording");
dialog.setMessage("Please wait...");
dialog.show();
mainActivity.stopService(new Intent(mainActivity, SensorService.class));
startButton.setEnabled(false);
Snackbar.make(coordinatorLayout, "Recording stopped.", Snackbar.LENGTH_SHORT).show();
}
}
SensorService class is set up to store sensor data to the database every 10ms using an ExecutorService.
The relevant parts of that class are:
public class SensorService extends Service implements SensorEventListener {
public void onSensorChanged(SensorEvent event) {
//get sensor values here
//insert into database
try{
executor.execute(insertHandler);
} catch (SQLException e) {
Log.e(TAG, "insertData: " + e.getMessage(), e);
}
}
}
#Override
public void onCreate() {
super.onCreate();
dbHelper = new DBHelper(getApplicationContext());
PowerManager manager =
(PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = manager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
registerReceiver(receiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
//Executor service and runnable for DB inserts
executor = Executors.newSingleThreadExecutor();
insertHandler = new InsertHandler();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
startForeground(Process.myPid(), new Notification());
registerListener();
wakeLock.acquire();
return START_STICKY;
}
#Override
public void onDestroy() {
//Prevent new tasks from being added to thread
executor.shutdown();
try {
//Wait for all tasks to finish before we proceed
while (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
Log.i(TAG, "Waiting for current tasks to finish");
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
//Stop everything else once the task queue is clear
unregisterReceiver(receiver);
unregisterListener();
wakeLock.release();
dbHelper.close();
stopForeground(true);
}
class InsertHandler implements Runnable {
public void run() {
dbHelper.insertData(Short.parseShort(MainActivity.subInfo.get("subNum")), System.currentTimeMillis(),
accelerometerMatrix[0], accelerometerMatrix[1], accelerometerMatrix[2],
accelerometerWorldMatrix[0], accelerometerWorldMatrix[1], accelerometerWorldMatrix[2],
gyroscopeMatrix[0], gyroscopeMatrix[1], gyroscopeMatrix[2]);
}
}
}
So when the user hits the button SensorService goes through its onCreate and onStartCommand methods. Whenever sensor data changes, executor service inserts it into the database
When the user hits the button again to stop, I'm telling the executor to shut down, but to finish processing all of its current tasks first. Once executor queue is cleared, we close the database, unregister listeners etc.
During this stage when the executor is awaiting shutdown and clearing its queue, I want to show a progress dialog to the user to say that its going through the process of closing things down.
So I display the progressdialog in the fragment itself once stop button is pressed. But how do I dismiss the dialog only once the executor service has finish processing its queue and is shut down correctly?
I can easily do this with async tasks, but I have chosen to use executor service for this instead so that is not an option
The progress dialog belongs to the fragment, so I cant do something like MainActivity.dialog.dismiss() from inside the service. I don't feel like thats a very clean way of handling things anyway
So I guess I'm left with Handlers, but not entirely sure how to set it up in this case because the executor service is operating within another service (SensorService). What would the code look like if using a handler to solve this?
You need some mechanism for the service to send a signal to the activity component. The easiest and most common way to do that is using an event bus. The proper implementation of this is completely up to you, but you can use existing event bus libraries to help you. You can find these with a simple google search.
I have problem and after some search I have not found any positive solutions.
After research I have idea that there is not implementation for my problem but this question may be is my last chance.
What do I need to get?
There is application that gets information about mobile network strength signal. I do it by
PhoneStateListener. Of course it works great but when my device goes to sleep mode, listener does not work:
https://code.google.com/p/android/issues/detail?id=10931
https://code.google.com/p/android/issues/detail?id=7592
WakeLock solves problem only in case, if device switch off by timeout. In case when I press hard power button, my device gets sleep mode as well. We can not override power button action.
My goal is get strength signal always when my device is enabled. It does not matter what mode is. All time it should collecting data.
Question:
Are there any ideas? How to achieve that? Are there ways to do this or may be there are some hacks? All solves are welcome. If you had some useful experience, please share this.
Thanks to all for help!!! I hope, this topic will get complete information about this problem.
Alarm manager is the way to go - the tricky part is to keep the phone awake after the alarm manager receiver returns. So
setup an alarm (notice you should also register an "On Boot completed" receiver to set up the alarm after a reboot - your alarms do not survive a reboot) :
Intent monitoringIntent = new Intent(context, YourReceiver.class);
monitoringIntent.setAction("your action");
PendingIntent pi = PendingIntent.getBroadcast(context, NOT_USED,
monitoringIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager)
context.getSystemService(Context.ALARM_SERVICE);
// here is the alarm set up
am.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + INITIAL_DELAY,
INTERVAL_BETWEEN_ALARMS, pi);
receive it - the receiver holds a WakeLock in its onReceive() which never fails :
public abstract class YourReceiver extends BroadcastReceiver {
#Override
final public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if ("your action".equals(action)) {
// monitoring - got broadcast from ALARM
try {
d("SS : " + new Signal().getSignalStrength(context));
} catch (InterruptedException e) {
e.printStackTrace();
}
// Actu8ally the lines above will ANR
// I did it with WakefulIntentService :
// WakefulIntentService.sendWakefulWork(
// context, YourWakefulService.class);
// Will be posting it asap
} else {
w("Received bogus intent : " + intent);
return;
}
}
}
If you are lucky (yourRetrieveSignal() is fast enough) this will work, otherwise you will need a (Wakeful)IntentService pattern in your receiver.
The WakefulIntentService will take care of the wake lock (if you want to avoid a dependency have a look here) - EDIT : keep in mind you can't define listeners in an intent service - see here.
If the receiver ANRs on you, you have to try the WakefulIntentService pattern. In either case you might use this :
This proved the most difficult part actually :
class Signal {
static volatile CountDownLatch latch; //volatile is an overkill quite probably
static int asu;
private final static String TAG = Signal.class.getName();
int getSignalStrength(Context ctx) throws InterruptedException {
Intent i = new Intent(TAG + ".SIGNAL_ACTION", Uri.EMPTY, ctx,
SignalListenerService.class);
latch = new CountDownLatch(1);
asu = -1;
ctx.startService(i);
Log.d(TAG, "I wait");
latch.await();
ctx.stopService(i);
return asu;
}
}
where :
public class SignalListenerService extends Service {
private TelephonyManager Tel;
private SignalListener listener;
private final static String TAG = SignalListenerService.class.getName();
private static class SignalListener extends PhoneStateListener {
private volatile CountDownLatch latch;
private SignalListener(CountDownLatch la) {
Log.w(this.getClass().getName(), "CSTOR");
this.latch = la;
}
#Override
public void onSignalStrengthChanged(int asu) {
Signal.asu = asu;
latch.countDown();
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.w(TAG, "Received : " + intent.getAction());
Tel = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
listener = new SignalListener(Signal.latch);
#SuppressWarnings("deprecation")
final int listenSs = PhoneStateListener.LISTEN_SIGNAL_STRENGTH;
Tel.listen(listener, listenSs);
return START_STICKY;
}
#Override
public void onDestroy() {
Log.w(TAG, "onDestroy");
Tel.listen(listener, PhoneStateListener.LISTEN_NONE);
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
This is working code (but not the pinnacle of elegance admittedly - comments/corrections welcome). Do not forget to register your services in the manifest and acquire permissions.
EDIT 2013.07.23 : I did not use the onReceive - if you use it it will ANR - this is working code if you use a WakefulIntentService in onReceive and in there you call SignalListenerService.
From my understanding of PhoneStateListener you can't do this while the application CPU is in sleep mode. You can either keep the device awake, which would ruin battery life. Alternatively you can use an alarm (see AlarmManager) to wake the device on intervals, so you can collect the data (impacts battery life still).
Some samples of using AlarmManager can be found here
CommonsWare's location polling example is really good about waking the phone and putting it to sleep again. I think it might help have a look: https://github.com/commonsguy/cwac-locpoll
One of the possible workarounds of android issue 10931 is to send the android.intent.action.SCREEN_ON intent to the 'phone' process after the screen turned off.
Create and register BroadcastReceiver to listen for notifications when the screen turns off
start(Context context) {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
context.registerReceiver(mScreenReceiver, filter);
}
final BroadcastReceiver mScreenReceiver = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, final Intent intent) {
if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
Log.v(LOGTAG, "Screen is off. Running workaround");
new Thread(mReportScreenIsOnRunnable).start();
}
}
};
Send the SCREEN_ON intent to the phone process only.
public final Runnable mReportScreenIsOnRunnable = new Runnable() {
#Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Runtime.getRuntime().exec(new String[] { "su", "-c",
"am broadcast -a android.intent.action.SCREEN_ON com.android.phone" });
} catch (IOException e) {
e.printStackTrace();
}
}
};
After receiving this intent the phone process would resume sending cell location
updates.
Root privileges are required.
This solution is a bit hacky, dangerous and works not on all phones. It can lead to higher power consumption, but not so much more than if you keep the screen turned on.