I am aware of UsageStats but it only has statistics for apps. How to get the total time spent on the device in intervals daily, weekly, monthly, yearly?
We can't find the screen awake time, but we can run a foreground service with an infinite loop that increments the value of counter every second when the screen is on.
Put the below code in the service.
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
Handler handler= new Handler();
Runnable myRunnable = new Runnable() {
public void run() {
if( powerManager.isScreenOn()){
counter++;
}
}
};
handler.postDelayed(myRunnable,1000);
counter shows the time in seconds.
One of my peer developer has written an intent service that makes an API call and then sleeps for 2 mins. After waking up, it sends again.
Below is the code:
public class GpsTrackingService extends IntentService {
....
#Override
protected void onHandleIntent(Intent intent) {
do{
try{
//make API call here
//then go to sleep for 2 mins
TimeUnit.SECONDS.sleep(120);
} catch(InterruptedException ex){
ex.printStackTrace();
}
} while (preferences.shouldSendGps()); //till the user can send gps.
}
....
}
Manifest
<service android:name=".commons.GpsTrackingService" />
This is working fine when the phone is active. However, whenever the phone goes into doze mode it fails to wake.
Will using alarm manager with WAKE permission solve this?
I have just got the code base and need to fix this within today. It'll be great if someone can help.
As the documentation says:
In Doze mode, the system attempts to conserve battery by restricting
apps' access to network and CPU-intensive services. It also prevents
apps from accessing the network and defers their jobs, syncs, and
standard alarms.
Periodically, the system exits Doze for a brief time to let apps
complete their deferred activities. During this maintenance window,
the system runs all pending syncs, jobs, and alarms, and lets apps
access the network.
In few words, while in Doze mode the system suspends network accesses, ignores Wake Locks, stops acquiring data from sensors, defers AlarmManager jobs to the next Doze maintenance window (which are progressively less frequently called), also WiFi scans, JobScheduler jobs and Sync adapters do not run.
Neither setAndAllowWhileIdle() nor setExactAndAllowWhileIdle() can fire alarms more than once per 9 (?) minutes, per app.
And it seems that the Foreground Services are also involved into this "Doze Drama", at least in MarshMellow (M).
To survive in this situation, tons of applications need to be at least rewiewed. Can you imagine a simple mp3 player which stops playing music when the device enters in Doze Mode?
Doze mode starts automatically, when the device is unplugged from the power supply and left on the table for about 1 hour or so, or even earlier when the user clicks the power button to power down the screen, but I think this could depend by the device manufacturer too.
I tried a lot of countermeasures, some of them really hilarious.
At the end of my tests I reached a possible solution:
One possible (and maybe the only) way to have your app running even when the host device is in Doze mode, is basically to have a ForegroundService (even a fake one, doing no jobs at all) running in another process with an acquired partial WakeLock.
What you need to do is basically the following (you could create a simple project to test it):
1 - In your new project, create a new class which extends Application (myApp), or use the
main activity of the new project.
2 - In myApp onCreate() start a Service (myAntiDozeService)
3 - In myAntiDozeService onStartCommand(), create the Notification
needed to start the service as a foreground service, start the
service with startForeground(id, notification) and acquire the
partial WakeLock.
REMEMBER! This will work, but it is just a starting point, because you have to be careful with the "Side Effects" this approach will generate:
1 - Battery drain: The CPU will work for your app forever if you
don't use some strategy and leave the WakeLock always active.
2 - One notification will be always shown, even in the lockscreen,
and this notification cannot be removed by simply swiping it out, it
will be always there until you'll stop the foreground service.
OK, let's do it.
myApp.java
public class myApp extends Application {
private static final String STARTFOREGROUND_ACTION = "STARTFOREGROUND_ACTION";
private static final String STOPFOREGROUND_ACTION = "STOPFOREGROUND_ACTION";
#Override
public void onCreate() {
super.onCreate();
// start foreground service
startForeService();
}
private void stopForeService() {
Intent service = new Intent(this, myAntiDozeService.class);
service.setAction(STOPFOREGROUND_ACTION);
stopService(service);
}
private void startForeService(){
Intent service = new Intent(this, myAntiDozeService.class);
service.setAction(STARTFOREGROUND_ACTION);
startService(service);
}
#Override
public void onTerminate() {
stopForeService();
super.onTerminate();
}
}
myAntiDozeService.java
public class myAntiDozeService extends Service {
private static final String TAG = myAntiDozeService.class.getName();
private static boolean is_service_running = false;
private Context mContext;
private PowerManager.WakeLock mWakeLock;
private static final int NOTIFICATION_ID = 12345678;
private static final String STARTFOREGROUND_ACTION = "STARTFOREGROUND_ACTION";
private static final String STOPFOREGROUND_ACTION = "STOPFOREGROUND_ACTION";
#Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!is_service_running && STARTFOREGROUND_ACTION.equals(intent.getAction())) {
Log.i(TAG, "Received Start Foreground Intent ");
showNotification();
is_service_running = true;
acquireWakeLock();
} else if (is_service_running && STOPFOREGROUND_ACTION.equals(intent.getAction())) {
Log.i(TAG, "Received Stop Foreground Intent");
is_service_running = false;
stopForeground(true);
stopSelf();
}
return START_STICKY;
}
#Override
public void onDestroy() {
releaseWakeLock();
super.onDestroy();
}
private void showNotification(){
Intent notificationIntent = new Intent(mContext, ActivityMain.class);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(mContext)
.setContentTitle("myApp")
.setTicker("myApp")
.setContentText("Application is running")
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pendingIntent)
.build();
// starts this service as foreground
startForeground(NOTIFICATION_ID, notification);
}
public void acquireWakeLock() {
final PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
releaseWakeLock();
//Acquire new wake lock
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG+"PARTIAL_WAKE_LOCK");
mWakeLock.acquire();
}
public void releaseWakeLock() {
if (mWakeLock != null && mWakeLock.isHeld()) {
mWakeLock.release();
mWakeLock = null;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
AndroidManifest.xml changes.
In the AndroidManifest.xml add this permission:
<uses-permission android:name="android.permission.WAKE_LOCK" />
Don't forget to add the name of your app in the <application> tag:
<application
....
android:name=".myApp"
....
And finally add your foreground service running into another process:
<service
android:name=".myAntiDozeService"
android:process=":MyAntiDozeProcessName">
</service>
A couple of notes.
In the previous example, the notification created, when clicked,
opens the ActivityMain activity of your test project.
Intent notificationIntent = new Intent(mContext, ActivityMain.class);
but you can use another kind of intent too.
To test it, you have to add some job to be performed into your
ActivityMain.java, for example some repeating alarm (which was
normally stopped when the device falls in Doze Mode), or a ripetitive
network access, or a timed tone played, or.... whatever you want.
Remember that the job performed by the main activity has to run
forever because to test this AntiDoze you need to wait at least 1
hour to be sure the device enters in Doze Mode.
To enter in Doze mode, the device has to be quiet and unplugged, so
you can't test it while you are debugging. Debug your app first,
check that everything is running then stop it, unplug, restart the
app again and leave the device alone and quiet on your desk.
The adb commands suggested by the documentation to simulate Doze
and StandBy modes could and could not give you the right results
(it depends, I suppose, by the device manufacturer, drivers, bla
bla). Please make your tests in the REAL behaviour.
In my first test, I used an AlarmManager and a tone generator to play a tone every 10 minutes just to understand that my app was still active.
And it is still running from about 18 hours, breaking my ears with a loud tone exactly every 10 minutes. :-)
Happy coding!
One of my peer developer has written an intent service that makes an API call and then sleeps for 2 mins. After waking up, it sends again.
Only have a service running while it is actively delivering value to the user. Sitting around for two minutes, watching the clock tick, is not actively delivering value to the user.
Will using alarm manager with WAKE permission solve this?
That depends on what you mean by "solve this". You can use AlarmManager to request to get control every two minutes so that you can do work. While the device is in Doze mode, you will not actually get control every two minutes, but once per maintenance window.
My app runs smoothly on the emulator - everything working exactly as it should - but not on my phone.
In my app, I use both a countdownTimer which ticks every minute, and an alarm manager, which should ensure that the user will be notified of whatever it needs to be notified of, should the phone be asleep.
Once the receiver receives the Alarm manager's broadcast, I acquire a partial wake lock for 5 seconds (which is even more than should be needed).
I checked, and wakelock.isHeld() returns true. I have the necessary permission, and the onReceive doesn't take that long (it is not at all computationally expensive).
And yet, the countdownTimer doesn't catch up. nothing else is fired until I actually unlock the phone and look at the app.
Any ideas why? I can't for the life of me figure this one out.
edit: Tried moving the wakelock (wl) declaration outside of the function (and even to the outer class), even though things worked as they were on the emulator (and from what I understand it shouldn't make a difference anyway, which it, indeed, did not :( )
code:
public class Class1 extends Binder {
public static class Class1A extends BroadcastReceiver
{
public void onReceive(Context context, Intent intent) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyApp");
//Acquire the lock
wl.acquire(5 * 1000); //5 seconds to expiration
// Do a few things
}
/// Some more code setting up the alarm
}
private class ReminderHandler extends CountDownTimer {
#Override
public void onTick(long millisUntilFinished) {
logTxt.append("\nTicked. ");
}
#Override
public void onFinish() {
GregorianCalendar tmp = new GregorianCalendar();
logTxt.append("\nCountdown completed at "+ frmtr.format(tmp.getTime()));
}
public ReminderHandler(int Len)
{
super((((long) Len)*60*1000),60*999);
}
}
}
}
As I said, the alarm is received, I acquire a wakelock successfully, but the onTick doesn't happen until I actually unlock the phone and open the app.
Well, this is not an answer I am happy with, but for now, this is all I have:
It seems the it takes more than 5 seconds (and even more than the 15 seconds I later gave it to test this) for the CountdownTimer to catch up (even if it should have only had 1 onTick and 1 onFinish call to catch up to).
I simply released the wakelock in the onFinish. I don't like this (since if there is some bug, it could end up holding the wakelock for way too long), but for now, this is all I can do.
i have a chat application.my code has a thread which manages internet connection.
but after some minutes my thread goes sleep.
I know i should use WakeLock to wake CPU and my thread.
but when i use below code , battery usage goes up.
if (mWakeLock == null) {
mWakeLock = pm.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
"NewOnlineSignalingPowerManagerTag");
}
if (mWifiLock == null) {
mWifiLock = wm.createWifiLock(
WifiManager.WIFI_MODE_FULL,
"NewOnlineSignalingWifiManagerTag");
}
if (!mWakeLock.isHeld()) {
logging.l("mWakeLock.acquire()");
mWakeLock.acquire();
}
if (!mWifiLock.isHeld()) {
logging.l("mWifiLock.acquire()");
mWifiLock.acquire();
}
can you prefer me a better way?
Why do you have to manage a internet connection? You don't have to. Use a Android service, and there won't be a thread issue, nor a sleep issue.
I am having trouble with wakelocks. Basically, I had the wakelock running in my timer thread, a doInBackground of an AsyncTask for the entire duration of my app (it is a background app for taking performance measurements). Then I decided I only want the screen to wakeup every 10 minutes or so for a second or so. So I created another class extending AsyncTask and put the code below into it's doInBackground, but now the screen doesn't turn back on. I should note that I start this thread and two other threads that are AsyncTask with doInBackground methods from onCreate.
Here is my new inner class doing the waking up: Essentially all it is supposed to do is wake the phone screen up every 10 minutes for a bit until my other two background threads set their booleans true.
private class WakeUp extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... arg0) {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, getClass().getName());
do{
try {
Thread.sleep(WAKEUP_EVERY); //600000ms
} catch (InterruptedException e) {
e.printStackTrace();
}
wl.acquire();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
wl.release();
}while(!timeCompleted || !transferCompleted);
return null;
}
}
You've forgotten to tell the wake lock to turn on the screen using the ACQUIRE_CAUSES_WAKEUP flag. As per the documentation:
Normal wake locks don't actually turn on the illumination. Instead, they cause the illumination to remain on once it turns on (e.g. from user activity). This flag will force the screen and/or keyboard to turn on immediately, when the WakeLock is acquired. A typical use would be for notifications which are important for the user to see immediately.
See ACQUIRE_CAUSES_WAKEUP for more details :D