Does AAS implement the intent firewall (IFW) functionality?

Does AAS implement the intent firewall (IFW) functionality to block specific APK activities/services/receivers/providers?

For those who don’t know what the IFW is. This is a built-in feature of AOSP to block specific APK activities/services/receivers/providers, making them unavailable to the app and blocking their code from running. I make liberal use of the IFW on my current Android device.

The IFW configuration consists of per-app XML files stored in /data/system/ifw/.

I would need this literally just for 1 proprietary closed-source app, and I’m not even sure if I’m going to install this app in the first place. I have no real-world use case for AAS (other than playing around) once I receive my Jolla Phone. Still asking in case someone knows. Thanks.

2 Likes

How would i check?
The directory exists and is empty.

This whole thing is just such an Android/modern approach to enshittification - why can’t stuff just be not shit from the beginning? Truly tragic.

1 Like

IFW exists since API Level 19 (Android 4.4.2 released in 2013)

Here is an example of an app I no longer use since a long time now. The IFW configurations are never cleaned up.

/data/system/ifw/at.gv.oe.app.xml

<rules>
<activity block="true" log="false">
  <component-filter name="at.gv.oe.app/at.atrust.mobsig.library.PushActivity"/>
  <component-filter name="at.gv.oe.app/com.google.android.gms.common.api.GoogleApiActivity"/>
  <component-filter name="at.gv.oe.app/com.google.android.play.core.missingsplits.PlayCoreMissingSplitsActivity"/>
  <component-filter name="at.gv.oe.app/com.google.android.play.core.common.PlayCoreDialogWrapperActivity"/>
</activity>
<service block="true" log="false">
  <component-filter name="at.gv.oe.app/androidx.work.impl.background.systemalarm.SystemAlarmService"/>
  <component-filter name="at.gv.oe.app/com.google.mlkit.common.internal.MlKitComponentDiscoveryService"/>
  <component-filter name="at.gv.oe.app/at.gv.brz.oegvat.mpns.app.plugin.android.FirebasePushMessagingService"/>
  <component-filter name="at.gv.oe.app/com.google.firebase.components.ComponentDiscoveryService"/>
  <component-filter name="at.gv.oe.app/com.google.firebase.messaging.FirebaseMessagingService"/>
  <component-filter name="at.gv.oe.app/androidx.camera.core.impl.MetadataHolderService"/>
</service>
<broadcast block="true" log="false">
  <component-filter name="at.gv.oe.app/com.capacitorjs.plugins.localnotifications.LocalNotificationRestoreReceiver"/>
  <component-filter name="at.gv.oe.app/androidx.work.impl.background.systemalarm.RescheduleReceiver"/>
  <component-filter name="at.gv.oe.app/com.capacitorjs.plugins.localnotifications.NotificationDismissReceiver"/>
  <component-filter name="at.gv.oe.app/androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy"/>
  <component-filter name="at.gv.oe.app/androidx.work.impl.diagnostics.DiagnosticsReceiver"/>
  <component-filter name="at.gv.oe.app/com.google.firebase.iid.FirebaseInstanceIdReceiver"/>
  <component-filter name="at.gv.oe.app/androidx.work.impl.utils.ForceStopRunnable$BroadcastReceiver"/>
  <component-filter name="at.gv.oe.app/com.google.android.datatransport.runtime.scheduling.jobscheduling.AlarmManagerSchedulerBroadcastReceiver"/>
  <component-filter name="at.gv.oe.app/com.tailoredapps.sign.plugin.viewpdf.share.pdfsigned.ShareReceiver"/>
  <component-filter name="at.gv.oe.app/androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy"/>
  <component-filter name="at.gv.oe.app/androidx.profileinstaller.ProfileInstallReceiver"/>
  <component-filter name="at.gv.oe.app/androidx.work.impl.background.systemalarm.ConstraintProxy$NetworkStateProxy"/>
  <component-filter name="at.gv.oe.app/androidx.work.impl.background.systemalarm.ConstraintProxy$StorageNotLowProxy"/>
  <component-filter name="at.gv.oe.app/androidx.work.impl.background.systemalarm.ConstraintProxyUpdateReceiver"/>
  <component-filter name="at.gv.oe.app/com.capacitorjs.plugins.localnotifications.TimedNotificationPublisher"/>
</broadcast>
</rules>

Most proprietary apps are like this. A lot of useless junk that is not needed for normal operation. One of the reasons I introduced an open-source policy for myself and got rid of almost all closed-source apps. Migrating to Sailfish OS is one step closer to digital independence for me.

As to concrete testing. I don’t know of any simple debugging app to test this specific scenario unfortunately.

EDIT: You could block an actual important activity/service in an app and test if it crashes at startup - it should in this case.

1 Like

Seeing as it seems to be super-standard and not something you can readily enable or disable - i’ll assume it is there and leave that part of the testing for you when you get your phone.

Sorry if this is a stupid question - I never used Android really in my mobile phone journey.

But what you’re saying is that I as a user on Android can create such an XML file and thus prevent apps to talk to each other?
Or is is intended that apps provide such files and thus deny other things to talk to them?

Interesting.

You need root privileges to change IFW settings, unless a custom ROM has implemented a control panel for it (never encountered one tough). I use a regular user app with root integration to control the IFW. For root access I use the open-source and popular Magisk, which also has two additional great features besides enabling root access: overlayfs to modify systems files without modifying the underlying filesystem, and an arbitrary code injection mechanism (native C code and ART code).

The IFW has nothing to do with apps talking to each other. It blocks first-party intents (activities/services/receivers/providers) of the app itself and prevents their code from running. If you try to instantiate a blocked intent you get an exception from the ART (Android Runtime).

From my example above: the rule at.gv.oe.app/com.google.android.gms.common.api.GoogleApiActivity prevents the app at.gv.oe.app from executing all code that might be inside the activity com.google.android.gms.common.api.GoogleApiActivity. If the code is actually required to run, the app crashes naturally, but if the ART exception gets handled and the developer decided that running this code is not important, the app continues to run normally. A lot of proprietary apps I used in the past had a lot of useless tracking and spyware intents (including always-active background services). To my surprise, I never had any significant problems blocking these.

A limitation of the IFW is that the code you want to block must reside inside an intent, if it’s not then you can’t block it via the IFW. For all other cases the IFW couldn’t solve I used Zygisk, an arbitrary code injection mechanism by Magisk. I still have a Zygisk module installed that hides root access and the “bootloader unlocked” status from apps.

Nowadays I only have 1 proprietary app left on my phone (from which I must hide root access and the “bootloader unlocked” status, otherwise it artificially refuses to work). I removed everything and introduced an open-source policy for myself, so the IFW and that Zygisk module aren’t really useful to me anymore these days.

That’s why I’m so interested in Sailfish OS and want to migrate over. Android (as well as iOS) are both lost to me. Too much annoying and hostile anti-user/anti-ownership junk; “you are not allowed to install/run this [thing] on your own device you own”. I’m tired of these cat and mouse games.

PS: I’m mainly just interested in the AAS to play around with it. To see how its implemented, how it works internally and where its limitations are; and whether Magisk and Zygisk might work on it or not (I highly doubt it already). I have no real-world use case for the AAS, as I already discovered plenty of great native Sailfish OS apps for all my daily use.

5 Likes

Thanks lot for the detailed information! Very interesting.

Not directly related, but we do have the option of a networklevel firewall using standard Linux netfilter:

I would be interested in having my firewall script adding IFW configs (if they are supported by AAS) to block other ways that Android apps might obtain some kind of network functionality. However at the moment I don’t know what common Intents might allow that. (Something for me to look into when I have time.)

I also don’t know how I could test if IFW blocking works (which is more relevant to this thread).

As mentioned earlier:
You could block an actual important activity/service in an app and test if it crashes at startup or during navigation (like opening settings, etc.) It should crash if blocking works. It does on a real Android.

Sometimes apps can have activities like MainActivity or SettingsActivity, etc.

You can try these 2 apps to get a list of intents of an app. I doubt they can write to /data/system/ifw within the AAS mount space themselves.
https://github.com/MuntashirAkon/AppManager (I currently use this app)
https://github.com/lihenggui/blocker
Alternatively there might be desktop applications to analyze APK files, but I don’t know of any.

I didn’t like the idea of installing an app with a huge amount of permissions on my phone, so instead I’ve come up with a script (for an Android phone) which I hope does the same job. Perhaps you can check it’s output looks reasonable?:

which aapt >/dev/null || sudo apt install aapt

Temp="$(mktemp --suffix=.apk)" ; adb shell pm list packages -f -3 | grep -Po "^package:\K/data/app/.*" | rev | sed -E "s/^([^=]+)=(.*)$/\2\t\1/g" | rev | sort | while read -r Line; do App="$(echo "$Line" | cut -f1)"; Apk="$(echo "$Line" | cut -f2-)"; adb pull "$Apk" "$Temp" >/dev/null; echo -e "\n# $App"; aapt dump xmltree "$Temp" AndroidManifest.xml | grep -A1 "^          E: action" | grep -Po '^            A: android:name[^=]+="\K[^"]+' | sort -u; rm "$Temp"; done

If you don’t want to get the intents of ALL installed apps, then you can add a “| grep my.app.name” before “| while read -r Line”.

I’m going to work on the assumption the listed intents are correct, but some confirmation would be nice…

@magiruuvelvet

I am trying to work-out how to use IFW block stuff done by “app.grapheneos.camera.play”, but I think I was wrong to assume it could block outgoing ‘calls’, and instead it can only block incoming ‘calls’?

If that’s the case, then it seems IFW isn’t of much use to me, as I’d like to block one app from using a specific intent, rather than block all apps using a specific intent.

It seems the only app-specific thing I could try, is to block a specific broadcast receiver of an app? With an Android phone, I seem to be able to list those using this:

adb shell dumpsys package resolvers receiver | grep -v : | awk '{ print $2 }' | sort -u

You seem to not understand what the IFW is. It can only block the types (E in the aapt output) activity, service, receiver, provider. The command you gave me greps the type (E) action which isn’t related to what the IFW handles.

The term firewall in the IFW (Intent Firewall) has nothing to do with networking or the internet protocol (IP) in case that was your impression. It blocks code, not network traffic.

From your second post I get the impression you are talking about app permissions, which has nothing to do with intents. Those are different things.

Due to its nature of blocking code in a very Android-specific way, this must be implemented in the runtime. In our case AAS.

EDIT: I should mention that the IFW is something that is completely optional to implement in alternative Android runtimes. It isn’t required in any way, shape or form to emulate or execute Android apps outside AOSP (the “real” OS). Hence this question exists, as I’d like to know whether the team behind AAS decided to implement this completely optional thing or not.

EDIT 2: An analogy to the JVM (Java Virtual Machine): you can imagine the IFW as something that would block individual and specific .class files within a .jar archive. The JVM can do runtime reflection. Now when an application tries to load a specific .class file at runtime, the IFW would block that attempt. I hope this analogy makes the functionality of the IFW clear.

Correct, I don’t understand the details of how Android apps are constructed, so I have been learning as I go. As I said, it wasn’t clear to me what types of things IFW blocks… however the documentation I linked to does seem to show it can block incoming “actions”, for example:

<!-- block and log a specific broadcast -->
<rules>
  <broadcast block="true" log="true">
    <intent-filter>
      <action name="com.example.demoapp.CUSTOM_INTENT" />
    </intent-filter>
  </broadcast>
</rules>

I understood that. I was interested in the possibility of blocking intents that could be used to gain some kind of internet-related functionality.

I may be able to answer that, if you can help me list the intents (activity, service, receiver, provider) of Android apps that I can attempt to block with the IFW. I mentioned the “app.grapheneos.camera.play” app as a possible test case, but it seems like it might be too simple to have anything I could test blocking?

Maybe I was wrong it’s too simple?:

$ adb shell dumpsys package resolvers | grep -v : | awk '{ print $2 }' | sort -u | grep app.grapheneos.camera.play
app.grapheneos.camera.play/androidx.profileinstaller.ProfileInstallReceiver
app.grapheneos.camera.play/app.grapheneos.camera.ui.activities.CameraLauncher
app.grapheneos.camera.play/app.grapheneos.camera.ui.activities.CaptureActivity
app.grapheneos.camera.play/app.grapheneos.camera.ui.activities.MainActivity
app.grapheneos.camera.play/app.grapheneos.camera.ui.activities.SecureCaptureActivity
app.grapheneos.camera.play/app.grapheneos.camera.ui.activities.SecureMainActivity
app.grapheneos.camera.play/app.grapheneos.camera.ui.activities.VideoCaptureActivity
app.grapheneos.camera.play/app.grapheneos.camera.ui.activities.VideoOnlyActivity

Output looks like the activities to me.

Disclaimer: I don’t know what this specific app is, just guessing from its name. I do not have a Google account, nor do I care about the Play store.

Now you could try 1 of the below rules at a time:

<rules>
<activity block="true" log="false">
  <component-filter name="app.grapheneos.camera.play/app.grapheneos.camera.ui.activities.MainActivity"/>
  <component-filter name="app.grapheneos.camera.play/app.grapheneos.camera.ui.activities.CameraLauncher"/>
</activity>
</rules>
  • If blocking of CameraLauncher works, it should crash once the app tries to show the camera feed. I would guess that MainActivity would immediately try to load CameraLauncher though.
  • If blocking of MainActivity works, the app should crash immediately upon launching.
  • If the app works normally, then AAS doesn’t implement the IFW functionality.

PS: Regarding your “app with too many permissions” concern. I can’t convince you that both these FOSS (free and open-source) apps are safe (and at least App Manager is available on F-Droid); you need to decide this for yourself, but I can assure you that these apps don’t do anything shady + you can always check the source code yourself. Personally I only consider proprietary closed-source apps with too many permissions to be generally scummy and shady.

Blocking “MainActivity” (but not the other one) in an ifw xml causes GrapheneOS’s camera app to fail to start. So I think we can say that ifw is implemented :slight_smile:

For future reference, here is basically what I did:

[defaultuser@JollaPhone2026 ~]$ devel-su 
Password: 
[root@JollaPhone2026 ifw]# appsupport-attach /system/bin/sh
:/ # cd /data/system/ifw
127|:/data/system/ifw # vi app.grapheneos.camera.play.xml                                                                                                  
:/data/system/ifw # ls -l
total 4
-rw-r--r-- 1 root root 278 2026-10-18 12:32 app.grapheneos.camera.play.xml
:/ # exit
[root@JollaPhone2026 ~]# ls -l /home/.appsupport/instance/defaultuser/data/system/ifw/
total 4
-rw-r--r--    1 appsuppo appsuppo       171 Oct 18 12:34 app.grapheneos.camera.play.xml
[root@JollaPhone2026 ~]# stat /home/.appsupport/instance/defaultuser/data/system/ifw/
  File: /home/.appsupport/instance/defaultuser/data/system/ifw/
  Size: 4096            Blocks: 8          IO Block: 4096   directory
Device: fe02h/65026d    Inode: 4456711     Links: 2
Access: (0700/drwx------)  Uid: (501000/ UNKNOWN)   Gid: (501000/appsupport-system)
Access: 2026-08-18 18:01:38.400684588 +0100
Modify: 2026-10-18 12:34:57.947713876 +0100
Change: 2026-10-18 12:34:57.947713876 +0100

So it’s not necessary to start an AppSupport shell (equivalent of adb shell), as you can create an xml file as root, then chown 501000:501000 it.