Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Call log permission is never requested #304

Closed
blackwind opened this issue Apr 20, 2023 · 15 comments · Fixed by #308
Closed

Call log permission is never requested #304

blackwind opened this issue Apr 20, 2023 · 15 comments · Fixed by #308

Comments

@blackwind
Copy link

I can't seem to get BCR v1.40 to request the call log permission, even after clearing storage, even with the appropriate variable in my filename template. Apparently it can't be manually enabled in App Info either. Do you need anything from me to debug this?

@chenxiaolong
Copy link
Owner

chenxiaolong commented Apr 20, 2023

Hmm, I have no idea what would cause it to not show up in the app info screen. There's nothing an app can do (in theory) to prevent a permission from showing up there. I'm not able to reproduce this on my Pixel 7 Pro.

Just to rule out the possibility of something lingering, can you try adb uninstall com.chiller3.bcr and then reinstall the module?

If that doesn't do it, the output of adb shell pm dump com.chiller3.bcr might be helpful. I'd expect to see READ_CALL_LOGS to show up in there.

@blackwind
Copy link
Author

To clarify, the permission is there in App Info, and I can tap to change it from "Don't allow" to "Allow", but my change doesn't stick -- it remains under "Not allowed" when I go back to the previous screen. I assume this is the case because, although the manifest declares it, BCR hasn't actually requested that permission from me yet. I'm able to toggle the permissions that have been requested no problem.

@blackwind
Copy link
Author

From pm dump:

android.permission.RECORD_AUDIO: granted=true, flags=[ USER_SET|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
android.permission.READ_CONTACTS: granted=true, flags=[ USER_SET|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
android.permission.READ_PHONE_STATE: granted=false, flags=[ USER_SET|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
android.permission.READ_CALL_LOG: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED|APPLY_RESTRICTION]

@chenxiaolong
Copy link
Owner

chenxiaolong commented Apr 20, 2023

https://cs.android.com/android/platform/superproject/+/android-13.0.0_r41:frameworks/base/core/res/AndroidManifest.xml;l=1330

This is a hard restricted permission which cannot be held by an app until the installer on record allowlists the permission.

What the heck. I didn't know that was a thing for READ_CALL_LOG. That's similar to Android 13's accessibility permission restriction where the installer/store app that installs another app must explicitly allow it access to certain permissions.

I'll play around and see if I can reproduce this. I don't know why it doesn't affect my device yet--maybe due to my use of debug builds.

EDIT: Yup, debug build was the reason I didn't catch this. The permission is granted for those builds and switching from debug to non-debug builds does not add back the APPLY_RESTRICTION flag.

EDIT 2: Nope, my understanding wasn't quite right. I had an old debug build flashed via Magisk. Then I added READ_CALL_LOG, and installed the debug build as a user app (update to existing system app). The way Android Studio installs updates auto grants the permission.

@chenxiaolong
Copy link
Owner

chenxiaolong commented Apr 21, 2023

This is pretty infuriating. So READ_CALL_LOG is a "hard restricted" permission. The only way to allow the user the choice of granting the permission is if the app is given an exemption. There are 3 types of exemptions: system, upgrade, and installer. None of them work well for BCR.

  • Upgrade exemptions are useless since they're only given to apps during Android OS upgrades to preserve backwards compatibility.

  • System exemptions can be given to apps with a specific role. The roles are hardcoded things, like phone, calendar, assistant, emergency, system_automotive_projection, etc. They're not extensible without modifying the system code via LSPosed or similar.

  • System exemptions can be given to apps listed in /{system,vendor,odm,product,system_ext}/etc/default-permissions/*.xml. These files both allow the exemption and auto-grant the permission, which is annoying, but we could deal with that. However, these files are only processed on a clean install of the OS. Installing a Magisk module and rebooting won't trigger the process that loads these (and when I do it forcibly using frida, Android's package state for BCR gets partially corrupted).

    EDIT 2: Looked further into this. These files are only processed when there are no users (eg. Android is in the initial setup wizard).

  • Installer exemptions can be granted when the installer app (Play Store, F-Droid, pm install, etc.) explicitly grant the exception. But there's no installer app involved when it comes to installing new system apps, like BCR. Updates to system apps can be installed as user apps (which is what happens when system apps are updated via Google Play), but as far as I'm aware, several Android OS's (eg. GrapheneOS) enforce that the update (user app) must have a strictly larger version number than the system app. An older version of BCR would need to be flashed via Magisk and then a newer .apk installed via pm install.

    EDIT: This won't work on GrapheneOS. Updates to system apps are only allowed if there are fs-verity signatures.

I'm still looking through the AOSP code, but I'm not really seeing any great solutions.

@blackwind
Copy link
Author

I'm assuming it's not possible to simply flag BCR with the phone role since you haven't already done that. Overlay the app to system with Magisk to gain access to the privileged permissions, then pm install it as a user app on BOOT_COMPLETED, cleaning that up as needed in the Magisk uninstall routine? Better yet (if it works), temporarily bind mount /data/app/com.chiller3.bcr/ to BCR's system path, which should be writable thanks to Magisk, pm install, and get the restriction revoked without actually installing it as a user app at all?

@blackwind
Copy link
Author

Sounds like it might be possible to simply target API 28 as well, before this restriction was added, but again, I'm assuming you would've already done that unless there was good reason not to.

@chenxiaolong
Copy link
Owner

I found a horrendous way to do this, but I think it'll work.

I made a small command line application inside BCR that'll talk to Android's PermissionManager service and tell it to add FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT and remove FLAG_PERMISSION_APPLY_RESTRICTION. Inside a su - system shell, I can run:

CLASSPATH=/system/priv-app/com.chiller3.bcr/app-debug.apk app_process /system/bin com.chiller3.bcr.standalone.MainKt

and it successfully alters the flags. Seems to work on both GrapheneOS and stock Pixel OS.

I just need to figure out how to switch from the root user to the system user without calling su during the Magisk module installation, ideally without bundling some C program just to call setuid(). The interactive prompt doesn't seem to work too well when called during module installation.

Also need to figure out if all the hidden APIs I'm calling exist in Android 9-12.

chenxiaolong added a commit that referenced this issue Apr 21, 2023
Since Android 10, READ_CALL_LOG has been marked as a hard-restricted
permission, which prevents it from being granted by the user unless the
app is given an exemption. There are three types of exemptions: system,
upgrade, and installer.

System exemptions can be given by `/system/etc/default-permissions`
(only on first boot) or via roles. Neither are usable for BCR. Upgrade
exemptions are only given during Android OS upgrades that further
restrict existing permissions. Installer exemptions are given by
whichever app installs another app, but there's no installer when it
comes to adding new system apps, like BCR.

Since AOSP has no sane built-in way to exempt BCR from the hard
restriction, we'll do it ourselves. This commit introduces a new CLI
utility baked in BCR that will talk to PermissionManager (Android 11+)
or PackageManager (Android 10) to adjust its permission flags. This will
unfortunately require two flashes (but only one reboot) for the initial
install, because BCR must have already been loaded by the package
manager for the flags to be changed. However, once the exemption has
been granted, it persists across future upgrades.

Fixes: #304

Signed-off-by: Andrew Gunnerson <[email protected]>
@chenxiaolong
Copy link
Owner

Would you mind giving the test build at #308 a try?

@blackwind
Copy link
Author

Confirmed working. Great work as always!

@chenxiaolong
Copy link
Owner

Awesome, thanks for testing!

@chenxiaolong
Copy link
Owner

Oops, I never responded to your suggestions, sorry.

I'm assuming it's not possible to simply flag BCR with the phone role since you haven't already done that.

This is a good idea, but I didn't end up trying it. Many of the roles are hardcoded to a specific preinstalled app, but this one in particular happens be tied to the default dialer option exposed in Android's settings. This would require code changes to have BCR pretend to be a dialer, but maybe Android would allow it to keep the exemption if you temporarily set BCR as the dialer and then change it back?

Overlay the app to system with Magisk to gain access to the privileged permissions, then pm install it as a user app on BOOT_COMPLETED, cleaning that up as needed in the Magisk uninstall routine?

Another good idea (at least for OS's that don't enforce that the user app must be newer than the system app). I didn't go this route because the pm install part would require BCR to have root permissions, which I'd prefer it not to have. I didn't see anything in Magisk itself that would allow it to run a command on BOOT_COMPLETED.

Better yet (if it works), temporarily bind mount /data/app/com.chiller3.bcr/ to BCR's system path, which should be writable thanks to Magisk, pm install, and get the restriction revoked without actually installing it as a user app at all?

This unfortunately won't work due to SELinux restrictions. User and system apps are labeled differently.

Sounds like it might be possible to simply target API 28 as well, before this restriction was added, but again, I'm assuming you would've already done that unless there was good reason not to.

I may have missed something, but I don't think this is the case. As far as I can tell, the restrictions apply to apps targetting older API versions too. It technically doesn't break compatibility because Google Play grants exemptions for the apps they allow to use READ_CALL_LOG and in other cases where there's no exemption, it looks exactly like if the user denied the permission prompt.

@blackwind
Copy link
Author

This would require code changes to have BCR pretend to be a dialer, but maybe Android would allow it to keep the exemption if you temporarily set BCR as the dialer and then change it back?

Definitely an impractical solution, then. Seamless or bust, I say.

I didn't see anything in Magisk itself that would allow it to run a command on BOOT_COMPLETED.

until [ "$(getprop sys.boot_completed)" = 1 ]; do sleep 1; done

Perhaps this could be used to work around the two flashes issue?

@chenxiaolong
Copy link
Owner

Yeah, polling would probably work. It would need to be something else that's tied to the user logging in though. I believe sys.boot_completed corresponds to LOCKED_BOOT_COMPLETED and until the user logs in, the relevant data directories for user app installation haven't been decrypted yet. I'll look into it.

chenxiaolong added a commit that referenced this issue Apr 22, 2023
This is more seamless since it removes the need to flash BCR twice. The
startup service will wait up to one hour for the user to unlock the
device before it performs the hard restriction removal. Logs are written
to `/data/local/tmp/bcr_remove_hard_restrictions.log`.

Issue: #304

Signed-off-by: Andrew Gunnerson <[email protected]>
@chenxiaolong
Copy link
Owner

Polling was a great idea: #309. Now we're back to seamless installs!

cheetah:/ $ cat /data/local/tmp/bcr_remove_hard_restrictions.log
----- Environment -----
Timestamp: Fri Apr 21 20:55:06 EDT 2023
Args: /data/adb/modules/com.chiller3.bcr/service.sh
Version: v1.40.r8.g2a6c416
UID/GID/Context: uid=0(root) gid=0(root) context=u:r:magisk:s0
----- Remove hard restrictions -----
Waiting for user 0 to unlock the device
User 0 is unlocking/unlocked
Successfully removed hard restriction from com.chiller3.bcr for android.permission.READ_CALL_LOG
Exit status: 0
----- Logcat -----
--------- beginning of main
04-21 20:55:29.472  1239  1239 D AndroidRuntime: Shutting down VM
----- Package state -----
<...>
      runtime permissions:
<...>
        android.permission.READ_CALL_LOG: granted=false, flags=[ RESTRICTION_SYSTEM_EXEMPT]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants