4

The way Android works, there are two cases in which an app is killed.

  1. The device is low on RAM. This is kind of self-explanatory, there is generally no page file on Android, and RAM is finite.
  2. The maximum number of cached processes is reached. To quote a comment in the AOSP source code:

The maximum number of cached processes we will keep around before killing them. NOTE: this constant is only a control to not let us go too crazy with keeping around processes on devices with large amounts of RAM. For devices that are tighter on RAM, the out of memory killer is responsible for killing background processes as RAM is needed, and we should never be relying on this limit to kill them. Also note that this limit only applies to cached background processes; we have no limit on the number of service, visible, foreground, or other such processes and the number of those processes does not count against the cached process limit.

I've noticed that it's not quite that simple, though. Time especially seems to be a factor. I can open 10, 20 apps and it's not a problem. Let the phone sit overnight, and it will only be 4 apps in my case.

In the first case, the solution is fairly obvious: somehow increase the amount of usable RAM, whether that's by reducing the number of services running, using zRAM, using a device with a lot of RAM, etc. My device (SHIFT6mq) has 8GB of RAM, and usually only about half of that is being used, so that's obviously not the restriction in my case. Which leads us to 2.

The solution here is less obvious. The most promising bit I found is this: https://github.com/crok/crokrammgmtfix, and the accompanying blog post here: https://telegra.ph/Fine-tuning-an-Android-system-04-20. I've tried the magisk module, and tried what's listed on the blog post. Doesn't matter, when I execute su -c dumpsys activity settings the results stay the same, and when I wake up, there are still only 4 apps running, whereas there were many more before I went to bed. I also found this thread on XDA: https://forum.xda-developers.com/t/how-to-disable-adjust-the-background-task-limit.3886743/, which has very similar measures. Problem is, this all seems to apply to Android versions under 10 - and I definitely couldn't get it to work on Android 10.

Now, some would say, that's the way it should work. You know what? If I wanted the app closed, I wouldn't have left it open. Now leave it open. I have 8GB of RAM, which has shown to be more than enough for that. And it closes the apps overnight... which is specifically when I have the phone plugged in.

So, how do I tweak the settings of the activity manager (or anything else for that matter) so that it stops killing apps I explicitly left open, when there's more than enough RAM available?

Note: I realize there are some devices (e.g. Samsung, Huawei) with overly aggressive battery saving measures on top of that in AOSP. This is not what I'm referring to. The stock ROM on the SHIFT6mq is very close to AOSP.

5
  • 2
    1. "My device (SHIFT6mq) has 8GB of RAM, and usually only about half of that is being used..". How did you get the exact RAM usage? 2. Did you set "Background process limit" in Developer Options? Sep 29, 2021 at 14:03
  • I checked in the developer settings. And no, I've checked over and over again, "Background process limit" is set to the standard setting, "Standard limit". Which makes me wonder... maybe there's some setting that can be unofficially changed to increase the number of apps that can run? Sep 30, 2021 at 12:21
  • Why would you want to force apps to stay running if they aren't doing anything? The only result is draining your battery quicker.
    – svin83
    Dec 19, 2021 at 1:26
  • Not necessarily. Most apps won't actually use battery while running in the background and doing nothing. Also, the first part of the solution doesn't necessarily increase the number of apps running in the background. If you look into the details of how Android works, it increases the number of activities for which it has a saved state. For me that is extremely useful. For instance, I would start watching a longer youtube video and when I wanted to continue the next day, it would have lost where I was in the video. Or look something up on a map, keep it open for later, and lose what I found. Jan 9, 2022 at 23:19
  • Keeping more apps actively running makes them restart less. Especially, for instance, web pages. If I have a large number of browser tabs open that I'm actively using, plus a larger number of apps that I'm also actively using, pages will constantly be reloading, losing where I was and possibly my input - even though my device has more than enough RAM to handle them. Jan 9, 2022 at 23:22

2 Answers 2

1

Well, this sure enough confirmed my hypothesis of "if no one on stackexchange knows the answer, you're going to have to figure it out yourself".

So I did.

The answer to this question is twofold. There's the window manager and the activity manager, and both play a certain role in this.

While it was tempting to write a TL;DR at the top here, I didn't, because especially with the second part, you have to be very careful with what you're doing, at least for now.

Window Manager/Recent Tasks

Let's start with the window manager, as this will also be interesting for people without large amounts of RAM. Also, it's quite a bit simpler.

It works with a number of variables: config_activeTaskDurationHours as well as a number of NumVisibleRecentTasks variables. config_activeTaskDurationHours determines, as the name suggests, how long a task is considered active, that is, relevant to the user. On my device, this was set to 6. After those 6 hours, the "task" no longer appears in the recent apps list, with one exception: the number of open apps would be lower than, in my case, config_minNumVisibleRecentTasks, launcher included. In that case, the app is not discarded. If the number of open apps is the same as config_minNumVisibleRecentTasks, though, then as soon as you open an app that isn't opened yet, the oldest open app that hasn't been used for more than 6 hours is discarded.

There are also a couple of other related settings that weren't relevant in my case:

  • config_minNumVisibleRecentTasks_grid: This is for when apps are displayed in a grid. This view is not available on many builds of Android, though.
  • config_minNumVisibleRecentTasks_lowRam: The same setting for low RAM devices.
  • config_maxNumVisibleRecentTasks, config_maxNumVisibleRecentTasks_grid, and config_maxNumVisibleRecentTasks_lowRam: these are upper limits for the numbers of recent tasks. In my case config_maxNumVisibleRecentTasks was set to -1, which means there is no maximum limit.

These are all grabbed from resources, and can be modified with GravityBox (root and XPosed/EdExposed/LSPosed required), under the Advanced tuning section, in the Framework section. They require a restart to activate the changes.

In my case, I set both config_activeTaskDurationHours and config_minNumVisibleRecentTasks to a ridiculous level: 5000. That way, it takes over a half a year for a task to have a chance to be removed, and that only if there are more than 5000 apps open, which I suspect will never happen.

Activity Manager/OomAdjuster

This was a bit of a tougher nut to crack for me, and took a good amount of searching through the source code to find the answer - although the answer may not have really required it. Oh, well.

I first tried writing an XPosed module to override com.android.server.am.ActivityManagerConstants, specifically updateMaxCachedProcesses. Unfortunately, I couldn't get it to hook the method properly, I don't know why. Mind you, I've never tried writing an XPosed module before, and I did this with AIDE - maybe sometime I'll unpack Android Studio again and try it in there. But if anyone wants finer control, this should offer an avenue to do that.

At the end, my original suspicion that you might be able to use the background process limit unoficially to increase the limit ended up being true. Sure enough, if you go into developer settings and change the background process limit to, say, 4, the output of su -c dumpsys activity settings looks like this at the end:

mOverrideMaxCachedProcesses=4
CUR_MAX_CACHED_PROCESSES=4
CUR_MAX_EMPTY_PROCESSES=2
CUR_TRIM_EMPTY_PROCESSES=15
CUR_TRIM_CACHED_PROCESSES=10

Note that the trim levels aren't changed, and IMO it doesn't make sense to change those levels even if the device has a high amount of RAM. Essentially, if the process numbers are below the trim levels, the system doesn't even bother trimming memory.

So, how can we assign a higher value? This is where things get complicated. I tried to call ActivityManager.setProcessLimit() with the permission android.permission.SET_PROCESS_LIMIT (which is only available to system apps but can also be granted via ADB) as defined in the IActivityManager Interface, but I couldn't get it to work (again, I was working with AIDE, so that may be the problem, I may try it again on Android Studio later).

However, there is a little known android shell command, service call, which essentially lets you call a whole bunch of methods from different service interfaces, see Where to find info on Android's "service call" shell command?. What you're looking for is the activity service, which is defined in android.app.IActivityManager.aidl, and you're looking for the method setProcessLimit(int max). Using the terminal, we can do service list to get a list of the services that can be called here. In my case, the service name I'm looking for was activity. Then, we look up the setProcessLimit(int max) command in the source code. Since I'm reasonably close to AOSP, I can look it up in there. In my case, it's the 40th command.

WARNING: Before I show what I needed for my device, don't just blindly follow this. This is different for just about every version of Android, and if you mess it up, it could cause problems. Read the link from the paragraph beforehand to be sure, at the very least be sure to understand my explanation in the previous paragraph.

In my case, I decided to set it to something ridiculous, so I took the tenfold of the original value, 600. That turned it into: service call activity 40 i32 600 (be sure to run su before that as this needs root privileges). Now, when I call su -c dumpsys activity settings, the section looks like this:

mOverrideMaxCachedProcesses=600
CUR_MAX_CACHED_PROCESSES=600
CUR_MAX_EMPTY_PROCESSES=300
CUR_TRIM_EMPTY_PROCESSES=15
CUR_TRIM_CACHED_PROCESSES=10

Halleluja! We did it! Mind you, AFAIK, you'll have to repeat the service call command on every restart, and if you mess around with the background process limit setting in developer settings, you'll also lose what you just set.

Maybe someday I'll try the approach with a root/adb app or an XPosed module again, and If I do, I'll update this answer. But for now, I'm happy with the results. RAM usage is now more between 5.5 and 6GB, as opposed to 4GB. Apps seem to be restarting less. Life is better.

1

I updated my module that's been mentioned in the original question and actually there's a way to set max_cached_processes to a value that survives restarts - even without root, with simple adb commands. Check the module's documentation and all the links lead me to the solution. Cheers!

Android 9 and below:

settings put global activity_manager_constants max_cached_processes=256

Android 10 and above:

/system/bin/device_config put activity_manager max_phantom_processes 2147483647
/system/bin/device_config put activity_manager max_cached_processes 256

Or something like this:

[ $(getprop ro.build.version.release) -gt 9 ] && cmd device_config set_sync_disabled_for_tests persistent
[ $(getprop ro.build.version.release) -gt 9 ] && cmd device_config put activity_manager max_cached_processes 256 || settings put global activity_manager_constants max_cached_processes=256
[ $(getprop ro.build.version.release) -gt 9 ] && cmd device_config put activity_manager max_phantom_processes 2147483647
[ $(getprop ro.build.version.release) -gt 9 ] && cmd settings put global settings_enable_monitor_phantom_procs false
[ $(getprop ro.build.version.release) -gt 9 ] && cmd device_config put activity_manager max_empty_time_millis 43200000
[ $(getprop ro.build.version.release) -gt 9 ] && cmd settings put global settings_enable_monitor_phantom_procs false

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .