6

I have read several posts regarding the new 'Doze' Mode in Android M, also the article on Android developers website : https://developer.android.com/training/monitoring-device-state/doze-standby.html yet I still have an issue with a Foreground Service correctly running on a phone (Android 6.0.1).

Issue:

I have a Service which handles playing music using a MediaPlayer object, the Service Implements onCompletion and onPrepared MediaPlayer interfaces.

Pseudo code:

    public class MusicService extends Service implements MediaPlayer.OnPreparedListener,
            MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {

            // Class Variables/Methods etc...

            @Override
            public void onPrepared(MediaPlayer mp) {

                   mp.start();

                   // update notification using startForeground()
            }


            @Override
                public void onCompletion(MediaPlayer mp) {

                   // Go and get another music track in separate thread
                   // (Have used AsyncTask and RX Java/RX Android to test if problem is here - works fine)
            }

            @Override
                public boolean onError(MediaPlayer mp, int what, int extra) {

                    // handleError

                    return false;
            }

    }

The app works well, when the phone is plugged into a power source, or when the screen is on, or app is open and screen is on. However when the phone has been sitting idle and playing music (between 5 - 40mins) the Service fails to retrieve the next track once onCompletion() is called.

The Service is setup and meets the following conditions:

  • The Service Notification is started with startForeground
  • The MediaPlayer Object has a partial wakelock PARTIAL_WAKE_LOCK
  • The Service runs in a separate process to the main app/UI
  • All Media uses prepareAsync() (logic to get the next track uri is performed in background thread, prepareAsync() is called only in the Main thread)

Things I have tried:

  • Retrieving the next song (after onCompletion is called) in the Main thread instead of a background thread
  • Acquiring a separate Partial WakeLock in onCompletion(), and holding and then releasing it when onPrepared() is called.
  • Few other trivial things that didn't make a difference...

I have a logcat output, from when the phone is left, untouched for a period of time with the screen off, and UNPLUGGED (logcat was obtained once I plugged it back in and it updated the log) which demonstrates the problem.

06-15 02:31:39.346 musicplayer:music_service E/MusicService: onPrepared : Tom Petty GH - 11 - You Got Lucky
06-15 02:31:39.548 musicplayer:music_service D/MusicService: Releasing wakelock
06-15 02:35:15.467 musicplayer:music_service E/MusicService: onCompletion called
06-15 02:35:15.467 musicplayer:music_service D/MusicService: Acquiring wakelock
06-15 02:35:15.514 musicplayer:music_service D/NextActionStrategy: Retrieving song - rx java - Main Ui Thread =false
06-15 02:35:15.870 musicplayer:music_service D/NextActionStrategy: Song Retrieved : Ten Years- Main Ui Thread = true
06-15 02:35:15.950 musicplayer:music_service E/MusicService: onPrepared : Ten Years
06-15 02:35:16.149 musicplayer:music_service D/MusicService: Releasing wakelock
06-15 02:38:59.002 musicplayer:music_service E/MusicService: onCompletion called
06-15 02:38:59.002 musicplayer:music_service D/MusicService: Acquiring wakelock
06-15 02:38:59.049 musicplayer:music_service D/NextActionStrategy: Retrieving song - rx java - Main Ui Thread =false
06-15 02:38:59.211 musicplayer:music_service D/NextActionStrategy: Song Retrieved : What Am I- Main Ui Thread = true
06-15 02:50:07.642 musicplayer:music_service E/MusicService: onPrepared : What Am I
06-15 02:50:07.976 musicplayer:music_service D/MusicService: Releasing wakelock
06-15 02:50:22.097 musicplayer:music_service D/MusicService: onStartCommand called : action.PAUSE

The routine would be

  • play a track
  • onCompletion() called
  • aquire wakelock get the next track
  • PrepareAsync()
  • onPrepared called - start playing and update notification using startForeground

From the logs you can see at 02:38:59.xxx onCompletion is called, a track is found, and returns to the Main thread where prepareAsync() is called, but then nothing happens for 12mins until I manually wake the phone up, and at which point just code continues where is stopped and the track starts playing.

I don't believe its the device as other music apps I use work fine in this situation, however I am limited to testing on one real device only :(

If you've got this far thanks for bearing with me! Any suggestions would be appreciated (hopefully regarding a solution and not my music tastes!).

Mark
  • 9,604
  • 5
  • 36
  • 64
  • have you whitelisted your app? You´ll have to do it manually in the settings. And be aware of Huawei devices, they got another battery saving management next to the doze mode. Here you have to handle both.... – Opiatefuchs Jun 15 '16 at 11:57
  • 1
    @Opiatefuchs - this is something that I don't have to do with other Media Player apps - is this the only way? – Mark Jun 15 '16 at 12:00
  • Man, I struggled so long with that s...t. I have a Huawei Ascend Mate 7 with lollipop. Everything worked fine, because I knewed the battery saver and how to handle it. Then, Update came to MM and nothing worked anymore. Turns out that this was a bug on Huawei, later they updated MM and then it worked. So, what´s your device and android version? Also, I saw this strange thing that some Apps like WhatsApp have a special feature in the settings which others haven´t so it must be a implementation by the supplier and no developer can handle it. – Opiatefuchs Jun 15 '16 at 12:04
  • Now, it doesn´t care which kind of app it is, in MM you have to whitelist it. And that´s only possible through a user interaction. I´ll have done it via the intent that API suggested, but then it will be possible that google kick out your app from playstore, if they think it´s not necessary. Anyway, I have implemented an AlarmManager who wakes up every x minutes via partial wakelock. – Opiatefuchs Jun 15 '16 at 12:07
  • @Opiatefuchs My device is a OnePlus One running CM13 (6.0.1) - strangely when plugged in and using the adb commands: adb shell dumpsys battery unplug & adb shell am set-inactive true - to simulate being unplugged and in Doze mode, the problem doesn't happen - only when it is physically unplugged and in Doze mode. – Mark Jun 15 '16 at 12:08
  • Why some other apps are automatically whitelisted, I don´t know. I think some apps like shazam, whatsapp or similar, are protected by the system at supplier side. – Opiatefuchs Jun 15 '16 at 12:08
  • I have media Player/ Music Player apps NOT whitelisted (Optimising Battery Use) that don't suffer this problem. I can't see, according to the docs (using startForeground) where I am going wrong. – Mark Jun 15 '16 at 12:10
  • Yes, that behaviour is pointed out in the docs. By plugin to usb, doze mode doesn´t come up. And by the way, the test with the adb commands, also don´t work in my case. I´ll never reached the same result like without plugin. I think there are some bugs still on MM.. – Opiatefuchs Jun 15 '16 at 12:11
  • It´s a uhge discussion around MM and doze mode. Most developers are frustrated now and having the same problem. I have also implemented a ForegroundService as a try and it doesn´t work. Only with my alarmManager setExactAndAllowWhileIdle() method, I was successfull. – Opiatefuchs Jun 15 '16 at 12:13
  • Ok, to clarify, would targeting compile level 22, not 23 (Android M) get around the problem? I'm just trying to narrow down, whether its my code or not. As I said other Media player apps (not whitelisted) work fine on my 6.0.1 device - could it be they are targeting Api22 not 23? The media player object holds a partial wakelock, and never cuts out half way through a song - so a wakelock works .. – Mark Jun 15 '16 at 12:26
  • 1
    No, targeting API22 will not fix that problem. Then you will have no possibillity to let alarm manager execute because of not available methods. You should give it a try with the alarm manager, beause it´s pointed out in the docs that wake locks are ignored. Instead, use the method `setAndAllowWhileIdle()` or `setExactAndAllowWhileIdle()` from alarmManager. Also `setAlarmClock` will work, but then you have a clock icon on the top of device and the alarm is visible through the system clock. – Opiatefuchs Jun 15 '16 at 12:45
  • @Opiatefuchs OK, thanks for the advise I'll give it a go - glad I'm not the only one experiencing this issue - I've spent too much time trying to solve what should be trivial thing. – Mark Jun 15 '16 at 12:49
  • look at this discussion.....https://plus.google.com/+AndroidDevelopers/posts/94jCkmG4jff – Opiatefuchs Jun 15 '16 at 12:59
  • @Opiatefuchs Thanks, I was actually wading my way through the discussion posts already! I cannot believe that there is not clear way round this, and developers are being left to 'hack' their way to a workaround (at this point a hack would be welcomed!). It seems that Android M, in an attempt to stop apps abusing wake locks and consuming power while idle, has also stopped legitimate apps working normally, but without providing a general workaround/solution, I mean come on .. a music player that can't play music if left alone == pointless app!! – Mark Jun 15 '16 at 13:21
  • Yep, that´s exact my situation with a chat app. Who wants to have a chat app that is not notify? It´s like I said in the post: Doze mode is good, but it should decided by the user not by Google (or should I say Goopple ;) ). Anyway, I tested yesterday and allow battery optimizations respectively I removed from whitelist. At a usual daily situation it works as long as user moves the device. But one hour after I layed down my device and go to sleep, over night nothing was fired. If this is a wrong implementation on Huawei side again, I can´t tell. – Opiatefuchs Jun 16 '16 at 06:27
  • @Mark Keen I had a similar problem with my background `Service` always stopping after 10 to 15 minutes idle only on (6.0.1). I looked into Doze Mode but that wasn't the problem. I solved it by making sure my `Service` used `startForeground()` (as you have), and making sure `onStartCommand` returns START_NOT_STICKY. I put a PARTIAL_WAKE_LOCK in my `onCreate()` method. This worked for me, now the service runs more than 2 hours in the background without a hitch. – midiwriter Jun 23 '16 at 02:06
  • @MikeLudwig Hi, thanks for the advise. my `Service` setup as `START_NOT_STICKY` also I hold a `PARTIAL_WAKE_LOCK` when `MediaPlayer.OnCompletionListener` callback method `onCompletion(MediaPlayer mp)` whilst I perform a task and release the wake lock AFTER the MediaPlayer Object obtains its own `PARTIAL_WAKE_LOCK` and plays a song. I'm sure that the problem is doze mode because when the device is plugged into a power source this problem does not exist. From the docs : `If a user leaves a device unplugged and stationary for a period of time, with the screen off, the device enters Doze mode.` – Mark Jun 23 '16 at 09:22
  • All I can advise (if you are still having the problem) is put your PARTIAL_WAKE_LOCK in the `Service`'s `onCreate()` method as I have done (and it remains for the full life cycle of the `Service` without being stopped and restarted). My `Service` downloads and writes to a file for hours, on battery and screen off, and doesn't stop since doing this. I realize we have not the same scenario, but I hope you can get this solved. I was very frustrated for days because it was only happening on (6.0.1) and no matter what I did the `Service` stopped after some minutes with no error in Logcat. – midiwriter Jun 23 '16 at 16:33
  • I have the very same problem (http://stackoverflow.com/questions/42119445/internet-music-player-cant-load-songs-when-device-locked-doze-mode). Did you solve some way? – Massimo Feb 09 '17 at 09:19
  • @Massimo Not really if I'm honest - it wasn't a production app I was working on so I favoured learning something else useful, rather than banging my head against the wall. I emailed the devleoper of Shuttle+ app (https://play.google.com/store/apps/details?id=com.simplecity.amp_pro&hl=en) Who was helpful, but at the same time couldn't pinpoint anything specific as he had not suffered any of these problems. The only other thing I suggest you try, if you haven't already, is to run the Player Service in a separate process, with the partial wake lock and a foreground notification. – Mark Feb 09 '17 at 10:00

1 Answers1

0

I just wanted you to see how I am implementing my startForeground() method. Maybe this can help:

 void setUpAsForeground(String text) {
    mNotificationManager = (NotificationManager)      this.getSystemService(Context.NOTIFICATION_SERVICE);
    PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, Main.class), 0);

    NotificationCompat.Builder noti = new NotificationCompat.Builder(this)
            .setContentTitle( "My App Title")
            .setContentText(text)
            .setSmallIcon(R.drawable.ic_stat_recording)
            .setStyle(new NotificationCompat.BigTextStyle()
                    .bigText(text))

            .setPriority(Notification.PRIORITY_MAX)
            .setContentIntent(contentIntent)
            .setOngoing(false);
    final Notification notification = noti.build();
    //mNotificationManager.notify(NOTIFICATION_ID, noti.build());
    startForeground(NOTIFICATION_ID, notification);

}

And in onCreate():

 @Override
 public void onCreate() {

    PowerManager powerManager = (PowerManager)      getSystemService(POWER_SERVICE);
    PowerManager.WakeLock wakeLock =      powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
            "MyWakelockTag");
    wakeLock.acquire();

    ***********
}
midiwriter
  • 426
  • 5
  • 12
  • the problem is not a PARTIAL_WAKE_LOCK - the media player object obtains a wakelock itself whilst its playing. The problem is Doze mode, and as per the docs, if Doze mode kicks in whilst a phone is idle, and unplugged from a power source then wake locks are ignored, meaning even if a wakelock is held Doze mode overrides them source - https://developer.android.com/training/monitoring-device-state/doze-standby.html#understand_doze under Doze restrictions - as I've said, the problem only occurs when unplugged, and left still - thanks for your help though. – Mark Jun 23 '16 at 19:14
  • 1
    Did you actually found any solution for this? @MarkKeen – Luis Pereira Mar 17 '17 at 14:15