I'm working on an app that uses a MediaPlayer object to play H.264 MP4 videos from a WallpaperService as it is a live wallpaper app. Battery drain occurs while the device (Nexus 5, Android 6.0.1) is idle and sleeping if I pause/stop the MediaPlayer with mediaPlayer.pause() or mediaPlayer.stop(). The drain is about 3-7%/hour as tested multiple times overnight. As soon as I release the media player with mediaPlayer.release(), the battery drain goes back to a more normal 1%/hour. I pause/stop the mediaPlayer when onVisibilityChanged calls false. The phone is reporting to be going to sleep in both the stock Android battery chart and Better Battery Stats.
How can this battery drain be explained if the CPU is going into a sleep state successfully?
EDIT: Something new I've discovered is that when calling mediaPlayer.setSurface(null) right before mediaPlayer.pause(), the idle battery use comes back to normal. I can then do mediaPlayer.setSurface(surface) to set it back before mediaPlayer.start(). The problem is there's some black artifacting for a couple of seconds after restarting.
I can't give you a precise answer but can give you what to look for. I suspect what is going on is that pause() is checking for events frequently enough to keep the processor from entering the deeper sleep/C-states. In contrast, stop() doesn't need to check for events and so allows the processor to enter a deep sleep state. I wrote an article on sleep states some years back.
I suspect that the writers of the function decided to check more frequently than is necessary. This is a very common mistake due to the developers thinking that shorter sleeps / more frequent checking result in better response (which it almost never does). You can check this by using a processor power monitor that actually checks the hardware sleep states. Unfortunately, most don't and only check for processor independent "equivalents".
So let's get back to your question: What can you do about it. I can give you some advice but none of it is very satisfying:
Check for an API or data structure that allows you to set the
checking interval for pause(). By the way, I don't know of any.
Write your own. Of course, this complicates writing platform independent apps
Use an alternative media player that has done this correctly
Hammer on google until it's fixed
Like I said, none of this is very satisfying. By the way, searching the net, I found evidence that this has happened more than once with new Android releases.
Good luck and let us know what happens.
Related
I have an application that depends on recording all sounds and analyses it and notify me when it records a specific tone.
So this app consuming battery power as it works all time to detect the sound tone wanted.
I need an idea to prevent this problem please.
Thanks in advance.
It appears that you are not allowing the processor to drop into a quiescent low power state. To allow the processor to conserve power, you need to have the processor idle as much as possible. If you are continuously sampling, this isn't going to happen. My answer here can give you some background.
I suggest you do the following:
Find out what is the minimum fidelity you can use and still identify the tones you want. To say this differently, determine the maximum sampling interval. For example, you may find that you can get by with sampling every quarter second and still identify the tone you want. This will allow the processor to drop into an energy conservation state.
Make sure you are using interrupts and not polling, i.e. use something like usleep(). So to check every .25 sec, you'll use something like while( running ){ sampleTone(); usleep(250000); }.
Check your sound sampling device's capabilities. It may have the ability to do something more sophisticated that will further minimize the number of samples/sec you need. For example, it may allow you to send the samples directly to disk or memory without requiring the CPU to wake up.
It is a well known issue that many Android phones switch off the accelerometer when the screen goes off. However something seems to have changed with Android Fit (the app). Fit keeps counting steps even when the screen goes off. If Fit is installed, then events are raised for step counting within the Fit environment and I am able to capture them using
Fitness.SensorsApi.findDataSources(mClient, new DataSourcesRequest.Builder()
.setDataTypes(DataType.TYPE_STEP_COUNT_CUMULATIVE)
I have tested this on a Samsung S4 and on a Oneplus One and in both cases the steps are counted.
How do they do that? What Android classes do they use?
My understanding is that the available method introduced since Kitkat is to implement a SensorEventListener. For example theelfismike provides code that implements this. However on many phones the step counting stops when the screen goes off. Interestingly the counting does not seem to stop if the Google Fit app is installed (hence I guess they keep the accelerometer on).
Am I missing something? Is the functionality of keeping counting steps after screen off available to the mortal programmers?
Thanks!
As Ilja said, your code runs even after the screen gets turned off. But in this case I guess we need a little different answer.
They definitely use a Service that keeps a wakelock and they query the sensors for data. Important part here is holding the wakelock - you have to prevent the device from going into sleep during lifetime of your service - if you don't want to miss some data.
But this approach will be drain the battery really fast, because in order to detect steps you need to process quite a lot of data from sensors.
Thats why there is sensor batching. That allows you to get continuous sensor data even without keeping the device awake. It basically stores the sensor events in a hw based queue right in the chip itself and only sends them to your app (service,..) at predefined intervals in batches. This allows you to do a 24/7 monitoring without draining the battery significantly. Please note that only supported chipsets can do that (you can find details in Android docs), in case of older phones you need to fallback to the hideous wakelock keeping method in order to get your data.
You can also just use Google Fit APIs, but this would only work when there're both Google Fit + Google Play Services installed on the device with monitoring turned on.
Every normal Thread is keep on working when the screen goes off or when the Activity lost its focus...but when the activity gets killed then all thread are killed...
However you can use services for longrunning tasks like asking the accelerometer for example
I have an audio app that plays multiple tracks at the same time, each with their own mediaPlayer. Each track is reasonably long, upwards of two minutes.
So long as the tracks are encoded as ogg files, everything works great on Android 4.x. I've yet to encounter a device running stock 4.x that has any audio problems with this setup.
But on Lollipop 5.x there are a wide variety of audio problems - stuttering, tracks cutting out, and bluetooth audio almost never seems to work.
I've discovered that going into Developer options in 5.x and unchecking "use Nuplayer (experimental)" instantly solves these problems and returns to 4.x levels of performance.
Is there a way I can programmatically force my app to use the 4.x media stack (I believe it's called Awesomeplayer?) and not to use the new Nuplayer system? At least until I can discover the source of the Nuplayer problems?
Update:
Setting a partial wake lock on the MediaPlayer resolves this problem:
playerToPrepare.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
A partial wake lock shouldn't have too big of an impact, and it seems like MediaPlayer itself cleans this up when playback completes.
-- Original Answer ---
So, I finally found a way to safely detect wether or not NuPlayer will be used or not on Lollipop. Seems like the best strategy for now is to inform the user to open Developer Settings and enable AwesomePlayer until Google fixes NuPlayer.
Sadly, there's no good way to change this setting for the user, we can just read its value unless you're signed as a system application.
This approach checks Android's system properties values to see if the user have enabled the use of AwesomePlayer or not under Developer Settings. Since Lollipop have NuPlayer on by default, if this value is disabled, we know NuPlayer will be used.
Drop SystemProperties.java into your project for access to read the system properties, do not change its package name from android.os (it calls through to its corresponding JNI methods, so needs to stay the same).
You can now check if the phone is Lollipop/5.0, if AwesomePlayer is enabled, and act accordingly if it's not (e.g. by opening the Developer Settings):
public void openDeveloperSettingsIfAwesomePlayerNotActivated(final Context context) {
final boolean useAwesome = SystemProperties.getBoolean("persist.sys.media.use-awesome", false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !useAwesome) {
final Intent intent = new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
context.startActivity(intent);
}
}
Enabling/Disabling NuPlayer didn't help. But I managed the wakelock part with a friendly UI. I'll look tonight as SysCtl from KitKat and compare it with the one on Lollipop, maybe I'll find something interesting.
So bluetooth stuttering is related to the dumb kernel on 5.02 that stutters the playback as soon as screen is off. I used a partial wakelock so the cpu stays active after screen off with this app. It works. No more stuttering. As for speakers that require high sample rate I just switched the cpu governor to performance. It's a workaround but the partial wakelock should work especially on bluetooth headphones. Here's the app's link https://play.google.com/store/apps/details?id=eu.thedarken.wl&hl=en
I am running a simple application that receives
and displays the values of Bluetooth Low Energy
advertisement packets in real time.
The Glass heats up in about 5 minutes and touch
commands stop working. The Glass is not super
hot, but warmer than feels comfortable.
Commenting out the Bluetooth stuff reduces the
heating considerably.
How can I make this application workable on the
Glass?
Without seeing the exact code you're using it is difficult to diagnose, but from your description we might be able to make a guess.
It sounds like you're using BluetoothAdapter.startLeScan() without setting up a timeout to stop the scan, and then scanning for an extended period of time. The documentation at http://developer.android.com/guide/topics/connectivity/bluetooth-le.html#find points out:
Because scanning is battery-intensive, you should observe the
following guidelines:
As soon as you find the desired device, stop scanning.
Never scan on a
loop, and set a time limit on your scan. A device that was previously
available may have moved out of range, and continuing to scan drains
the battery.
Battery drains typically correspond to Glass overheating.
There isn't a lot you can do to solve the problem, but it may involve changing how you think about what you're doing.
If you're doing this in immersion mode, you may want to switch it to a live card instead. You may also not really want to scan "forever", but may want to try scanning for a fairly short period of time, then turning it off for a short period of time, and repeating this. Or you may want to turn it off for a period of time once a new packet is received. Your exact needs may dictate this, but given the characteristics of how often advertisement packets are sent out, you may not need to be constantly listening to get all of the packets.
As a current Glass developer myself, I've found that there isn't really any good way around this.
We're all facing this problem. There isn't much room, every is tightly packed, and what Glass is having to do is complicated. It will heat up if you are constantly scanning. Especially when you are taking videos - Glass will become very hot.
I found that lowering the screen brightness helps a bit, at least with the battery life, if nothing else.
This is how you set the screen brightness to its minimum, in case you do want to try it and see if it helps:
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.screenBrightness = 0f;
getWindow().setAttributes(lp);
I'm currently investigating workarounds for this heat problem as well. Glass overheats quickly when capturing previews and doing heavy calculations or image processing on it.
This paper is very informative:
http://arxiv.org/pdf/1404.1320v1.pdf
Title: "Draining our Glass: An Energy and Heat
Characterization of Google Glass"
Hey, I finished my code and everything works as it should but I'm wondering since smartphones have limited battery, CPU .. How can I check if my application will run good on older phones? and how can I check if my app consumes the phones battery?
Thanks
The only way to really know it to test the app in the phones.
That said, you can profile it and make educated guesses based on how CPU-intensive is your app, for how long is it running, if you have services using cpu continuously, etc.
There are a few things to consider:
The main battery drain is the screen. If you keep any kind of screen lock (even dim), it will destroy the battery.
Any other lock (wifi, etc.), will induce battery drain. Do you use them? Do you need them? Do you release them as soon as they're not needed?
Do you have hardware listeners (e.g., location, accelerometer), unregister them as soon as they're not needed
Take a look at this video: http://www.google.com/events/io/2009/sessions/CodingLifeBatteryLife.html
Also take a look on
http://developer.android.com/guide/developing/debugging/debugging-tracing.html