Android 12: Turning off revocation of permissions of unused apps
No solution, yet
This post summarizes my failed attempt to get rid of the said nuisance. Actually, I was successful, but the success was short-lived: The fix that I suggest here gets reverted by the system soon enough.
I expect to get back to this, in particular if I find a way to do either of the following three:
- Manipulate an ABX file (that’s Android’s compressed XML format)
- Permanently disable a service.
- Write a utility that changes the required setting by virtue of Android’s API. A failed attempt to write a command-line utility is described here.
If anyone knows how to do this, beyond anything I’ve written here, please comment below.
Note Gene Cash’ suggestion below in the comments, which is based upon turning off a specific permissions for each app. It’s an interesting direction, however it requires revoking a permission for each new app that we install.
So here comes the long story.
“For your protection”
Yet another “feature” for “my own good”: If an app isn’t used for 90 days, Android 12 automatically revokes the apps permissions, and deletes its temporary data. Which sounds harmless, but if that app involves a registration, well, you’ll have to do that again when you launch it next time. Which can be extremely annoying if you need a taxi quickly because of an issue with your car. And of course you don’t need the taxi app so often…
The common term for this brilliant feature is “removing permissions from unused apps”, but the internal term is “hibernation”.
I guess the people who though about this feature also throw away everything in their homes if they haven’t used them for 90 days.
Anyhow, I like my smartphone repeatable. If I open an app after a long time, I want it to work like it did before. It’s a weird expectation in these days of upgrades every day, I know, but that’s what I want.
It seems like there’s a simple solution. If you have adb access to your phone, that is (or some other type of shell). Or maybe someone will come up with an app that reverts all the silly stuff that new versions of Android come up with. Just be sure to run it every 90 days.
I should mention, that it’s possible to opt out this “feature” for each App individually: Go to each app’s configuration, and opt out “Remove permissions and free up space” under “Unused apps”. But I didn’t find a way to do this globally. Probably because we’re not considered responsible enough to make such a decision.
The short-lived fix
Hibernation of apps is officially documented on this page.
According to that page, the timeout before hibernation is set as a parameter, auto_revoke_unused_threshold_millis2. So
$ adb shell device_config get permissions auto_revoke_unused_threshold_millis2 7776000000
7776000000 milliseconds equals 7776000 seconds, which equals 90 days. Looks like it, doesn’t it?
So the apparent workaround is to make the hibernation time very long, so it never happens. Let’s say 20 times 365 days? Good enough? That’s 630720000000 milliseconds. Yes, this number exceeds 232, but so does the original threshold. It’s a 64-bit machine, after all.
So just go (this doesn’t require rooting the phone):
$ adb shell device_config put permissions auto_revoke_unused_threshold_millis2 630720000000
And then verify that the new number is in effect:
$ adb shell device_config get permissions auto_revoke_unused_threshold_millis2 630720000000
Yey. Does it solve the problem? Nope, that didn’t work. A couple of weeks later I got the same notification, and this time a couple of other apps were hibernated.
Taking a closer look, it turned out that the value of auto_revoke_unused_threshold_millis2 had returned to 7776000000 (after a couple of weeks). How and why, I don’t know. I tried to change it back to the desired value, and ran a reboot, and the updated value survived that. So the values that are set with device_config are permanent. But another reboot later, it was back to 7776000000. So I don’t know what’s going on.
Maybe it’s because I didn’t change the parameter with the same name, but under the app_hibernation namespace?
$ adb shell device_config get app_hibernation auto_revoke_unused_threshold_millis2
7776000000
And maybe the easiest way is to to turn off hibernation altogether. There happens to be a parameter with an interesting name:
$ adb shell device_config get app_hibernation app_hibernation_enabled
true
Googling for this parameter name, I found absolutely nothing. But if it does what its name implies, maybe this will help?
$ adb shell device_config put app_hibernation app_hibernation_enabled false
I can’t even tell if this would help. The parameter got changed back soon enough to “true”.
Other ideas
Truth to be said, I don’t really know why and when the values of these parameters change back to their original values.
But here are a few clues: device_config has a “set_sync_disabled_for_tests” option, so I suppose
adb shell device_config set_sync_disabled_for_tests persistent
could prevent the configuration to reset all the time, but I didn’t try that.
It more than appears that the source of the default values for these parameters are in a permanent setting file, /data/system/users/0/settings_config.xml (needless to say, root access is required to invoke the 0/ directory). There are several similar .xml files in the 0/ directory, as listed in the definition of the class that processes these XML files, SettingsRegistry.
But here’s the crux: Despite the .xml suffix, this isn’t an XML file, but ABX, which is a format that is new in Android 12. It’s a condensed form of XML, which can’t be edited with just a text editor. Since this format is new and specific, there’s currently no reliable toolkit for making pinpoint changes. The only thing I found today (October 2022) is described below, along with its shortcomings.
An entry like this in settings_config.xml:
<setting id="3247" name="app_hibernation/app_hibernation_enabled" package="com.google.android.gms" preserve_in_restore="true" value="true" />
appears with a row like this in the output of “dumpsys settings”.
_id:3247 name:app_hibernation/app_hibernation_enabled pkg:com.google.android.gms value:true
In other words, the ABX file is clearly maintained by the “settings” service (this is what the second argument to dumpsys means). There’s a “settings” command-line utility too, but it doesn’t give access to this attribute (I’m not sure how related it is to the “settings” service).
An alternative approach could be to turn off the Hibernation service itself.
The services can be listed in Settings > System > Developer Options > Running Services, but these are only services that are linked with apps, it seems like. So not there.
On the other hand, the service can be found with dumpsys, and it’s called com.android.server.apphibernation.AppHibernationService. Unfortunately it can’t be disabled with pm disable-user as suggested on this page, be the argument to this command must be a package. The service’s name isn’t recognized by pm for this purpose.
I have no idea how to just disable a service. I’m not even sure it’s possible.
Additional trivia
In case this doesn’t work, here are some random pieces of info that might help. First of all, there’s a service called com.android.server.apphibernation.AppHibernationService. So maybe disable it somehow?
Another thing is that by looking at the output of “adb shell dumpsys package”, it appears like there’s a package called android.intent.action.MANAGE_UNUSED_APPS, which has an Intent named com.google.android.permissioncontroller/com.android.permissioncontroller.permission.ui.ManagePermissionsActivity. Maybe disable it, in the spirit of this post? It’s not clear if that’s the Activity that disables the app, or the one that allows enabling it back.
And then we have this snippet from the (adb shell dumpsys):
logs: "1663488654709 HibernationPolicy:i unused app com.google.android.apps.podcasts - last used on Thu Jan 01 02:00:00 GMT+02:00 1970 " logs: "1663488654710 HibernationPolicy:i unused app com.google.android.apps.authenticator2 - last used on Thu Jan 01 02:00:00 GMT+02:00 1970 " logs: "1663488654711 HibernationPolicy:i unused app com.gettaxi.android - last used on Thu Jan 01 02:00:00 GMT+02:00 1970 " logs: " Globally hibernating apps [com.google.android.apps.podcasts, com.google.android.apps.authenticator2, com.gettaxi.android] "
The sources for the hibernation code:
git clone https://android.googlesource.com/platform/packages/modules/Permission
and the code is under PermissionController/src/com/android/permissioncontroller/. In particular, look at permission/utils/Utils.java for the list of parameters and their internal representation.
The source for the device_config utility is here. The implementation of DeviceConfig’s core methods is here.
Just in case it helps someone (future self in particular).
ABX manipulation
The only reference I found for ABX was in CCL Solution Group’s website. The said post describes the format, and also points at a git repository, which includes a couple of utilities for converting from XML to ABX and back.
The problem with this is that when I tried it on my settings_config.xml file, the back-and-forth conversion didn’t end with exactly the same binary. And I’m not sure if the difference matters. So if I can’t be sure that everything that isn’t related to the little change I want to make stays the same, I prefer not touch it at all.
I suppose there will be better tools in the future (or maybe I’ll write something like that myself, but beware, I will do it in Perl).
So this is how to work with the existing tool:
Downloading the pair of utilities from the git repo:
$ git clone https://github.com/cclgroupltd/android-bits
The last commit in the repository that I refer to below is 7031d0b, made in January 2022.
Change directory to ccl_abx, copy the desired ABX (.xml) file into there, and go
$ python3.6 ccl_abx.py settings_config.xml -mr > opened.xml
(python3.6, as it implies, is Python 3.6 on my machine. There are so many versions, and it didn’t work on earlier ones).
This creates an XML file with the content of the ABX file.
Now, manually edit opened.xml and remove the <root> and </root> tags at the beginning and end. Not clear why they were put there to begin with.
As for back-conversion to ABX, here comes some Java.
ABX manipulation II
This is an update on 27.2.23, following a comment I got to this post: It turns out that there are utilities for ABX conversion on the phone itself, which are wrappers to a Java utility:
$ cat /system/bin/abx2xml #!/system/bin/sh export CLASSPATH=/system/framework/abx.jar exec app_process /system/bin com.android.commands.abx.Abx "$0" "$@" $ cat /system/bin/xml2abx #!/system/bin/sh export CLASSPATH=/system/framework/abx.jar exec app_process /system/bin com.android.commands.abx.Abx "$0" "$@"
And they work:
$ abx2xml settings_config.xml settings_config.xml.unabx $ xml2abx settings_config.xml.unabx settings_config.xml.reabx $ abx2xml settings_config.xml.reabx settings_config.xml.todiff
Unfortunately, settings_config.xml.reabx didn’t end up identical with settings_config.xml. Making a comparison between the outputs of the UNIX “strings” utility, I found a “true3″ string in each setting in the .reabx file, which wasn’t in the original.
Even weirder, the .unabx wasn’t identical to .todiff. But this made them identical (on a regular Linux machine):
$ dos2unix settings_config.xml.unabx
Say what? The ABX was converted into a text file with CR-LF on the first time, and only with LF on the second? I guess the answer to this is somewhere in the Java code.
But maybe it’s OK. I’ll give this a go sometime.
Ugh. Java stuff
Java is definitely not my cup of tea, neither my expertise. So don’t learn anything from me on how to work with Java. The only thing I can say is that it worked.
First, I installed the Java compiler (Linux mint 19):
# apt install default-jdk
Then change directory to makeabx/src.
Compile everything (this will probably make Java-savvy guys laugh, but hey, it worked and created .class files in the respective dirtectories).
$ javac $(find . -iname \*.java)
There will be a few notes about deprecated API, but that’s fine.
Then run the program with
$ java com.ccl.abxmaker.Main path/to/opened.xml
If this fails, saying something like “Could not find or load main class src.com.ccl.abxmaker.Main”, it’s because I tried to run the program from the wrong directory. Even though shell’s autocomplete filled in the class nicely, it’s still wrong. This has to be done from the src/ directory (at least in the way I clumsily compiled this stuff).
This creates a file with the same name and path as the input, but adds an .abx suffix.
The ABX file that I obtained from back-conversion wasn’t identical to the original, however: For example, the first XML element is <settings version=”-1″>. In the original ABX, it’s encoded as a 32-bit integer (0xffffffff), but in the opened.xml.abx it’s encoded as a string (“-1″).
Does it even matter? I don’t know. If the pair of utilities doesn’t pass this kind of regression test, am I ready to inject the result into my phone’s configuration system? Well, no.
Reader Comments
I can’t believe there is not more outrage about this stupidity. As well as a fix by now.
It’s impossible to communicate the magnitude of my displeasure at this inanity.
FYI You can use abx2xml and xml2abx via ADB.
And since both are actually just shell wrappers for /system/framework/abx.jar, it should be possible to use it elsewhere as well if Java is truly as portable as advertising would have us believe.
Thanks a lot for that comment! I tried it out and updated the post above.
Glad to have helped.
And you’re right, the conversion isn’t consistent at all. On the example of the file /data/system/packages.xml I was just editing, the original ABX was 243 KiB, converted to XML it’s 406 KiB, and after minor edits only (changing some signatures) that took it to 408 KiB (+2 KiB), the regenerated ABX ended up bloated to 337 KiB (+40%).
When I first saw the output size I thought it was due to malformed XML (after editing it with sed, which is exactly what it’s not supposed to be used for). Turns out it’s just the way things are, nothing to see here.
The end result also seems to depend on whether I reformat (pretty-print) the XML or linearize it, which it should not, since it’s only affecting whitespace. And I also noticed as you did that by playing it with the converters repeatedly the newline convention in the XML file got changed somewhere along the way.
It does the job in the end, however, likely better than any third-party approaches: the modified file in question got accepted by the Android Package Manager with no complaints. Given the remarkable inconsistency though, it’s probably best to use the version that comes with the target device to avoid stumbling into a compatibility quagmire.
Thanks again for that information. I’m planning to try the manipulation next time I’ll make a System Update (I also have a whole story about preventing that from happening automatically, https://billauer.co.il/blog/2022/08/android-turn-off-system-update/).
Rationale: If I mess up completely, I just reload the Android images again.
Please don’t give up on this… Another outraged Android user here.. the workaround I had been using was a Tasker task that used AutoInput to remove the Unused App toggle from within the Playstore itself. A lot less technical but not a permanent or even good solution however the only avenue I found to try to rectify the forced settings changes
I think I found it. Like you, I’ve been trying to figure out this for ages.
Run “device_config put app_hibernation app_hibernation_enabled false” in adb shell.
My phone is rooted, but I did not have to be superuser to do this. The “unused app” toggle is now gone from the App Info page!
This survived a reboot and hasn’t turned back on after a couple days.
I found this on https://developer.android.com/topic/performance/app-hibernation
As mentioned in the post above, this command didn’t help in my case, because app_hibernation_enabled got back to true “by itself” in my case. You didn’t mention if you checked it with adb again after doing this.
Sorry… I should not post too early in the morning.
I dug a bit more and discovered the “appops” command. The invocation we’re interested in is:
appops set 10259 AUTO_REVOKE_PERMISSIONS_IF_UNUSED ignore
“AUTO_REVOKE_PERMISSIONS_IF_UNUSED” is the “hibernation” operation and we can do “ignore” or “allow” – note that it’s case-sensitive.
It’s a bit strange, as it operates differently depending if you give it a UID or a package name, and “appops set org.genecash.garagedor AUTO_REVOKE_PERMISSIONS_IF_UNUSED ignore” does not do what you want it to do.
I’ve used the following to set all “third-party” (i.e. apps I’ve installed personally) to not hibernate:
for uid in `pm list packages -U -3 | awk ‘{ print $3}’ FS=:` ; do appops set $uid AUTO_REVOKE_PERMISSIONS_IF_UNUSED ignore ; done
FYI, you might need to do “appops write-settings” afterwards to “immediately write pending changes to storage” – these settings changes do show up in the app info settings, but it doesn’t seem to hurt.
Thanks for that. I’ve added a note in the post mentioning this comment.
I haven’t tried this myself (yet?). I can however confirm that appops knows about AUTO_REVOKE_PERMISSIONS_IF_UNUSED as an OP on my phone.
I’ll add a couple of links on this matter: https://automationchronicles.com/set-permissions-via-adb-and-appops-command/ and https://developer.android.com/reference/android/app/AppOpsManager
For general knowledge, the list of AppOps can found as the RUNTIME_AND_APPOP_PERMISSIONS_OPS array on https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/AppOpsManager.java
It would have been much nicer to change the default permission for this OP to “ignore” (which means disallow). That would be a catch-all solution for this problem. Not sure if this is possible, though.
According to https://developer.android.com/topic/performance/app-hibernation , jobscheduler job id #2 for com.google.android.permissioncontroller may controls this.
So perhaps canceling job id #2 will work? Alas, it turns back on on every reboot. I am experimenting with having tasker run
cmd jobscheduler cancel com.google.android.permissioncontroller 2
on every boot, in a root shell. I fear that the job may still run before tasker cancels it.
Two more ideas (in root shell):
pm disable com.google.android.permissioncontroller/com.android.permissioncontroller.permission.service.AutoRevokeService
pm disable com.google.android.permissioncontroller/com.android.permissioncontroller.permission.service.AutoRevokeOnBootReceiver
(Webmaster note: The commands are not fully visible as displayed. Copy-Paste the entire comment into an editor in order to see the complete commands).
Hi does Android have a macro program that could set a cron-like job that would go into each app and set the correct permission say once a day at 3:00 a.m. for example? This is sort of cheesy but you wouldn’t have to go into a hundred apps or update new apps this way. Also have any enhancements been added after Android 12 to fix this issue universally? Also does rooting the phone give any more control over annoying stuff like this?
playing silent mp3 instead of sleep (termux).