Skip to content

Instantly share code, notes, and snippets.

@SebaUbuntu
Last active September 10, 2025 05:26
Show Gist options
  • Save SebaUbuntu/ec053a00a9988eaea091fb1cc1f19324 to your computer and use it in GitHub Desktop.
Save SebaUbuntu/ec053a00a9988eaea091fb1cc1f19324 to your computer and use it in GitHub Desktop.
Generate framework compatibility matrix from fqnames

Generate framework compatibility matrix from fqnames

  • Download these 2 files
  • Compile AOSP without fcm from stock and wait for check_vintf to error out
  • Delete Python prefix from all lines (e.g. checkvintf E 06-24 00:30:22 49120 49120 check_vintf.cpp:554])
  • Paste the result in fqnames.txt
  • Launch the script
# Add here a list of fqname
# You can get it if you try to build AOSP without a fcm
# check_vintf will print a list of needed entries
# This currently works with AIDL and HIDL
# Here an example from Xiaomi SM8150 common tree
[email protected]::IGnss/gnss_vendor
[email protected]::ISap/slot2
[email protected]::IImsCmService/qti.ims.connectionmanagerservice
[email protected]::IUceService/com.qualcomm.qti.uceservice
[email protected]::IDisplayColor/default
[email protected]::IDisplayConfig/default
[email protected]::IDisplayPostproc/default
[email protected]::IGoodixFingerprintDaemon/default
[email protected]::IGoodixFingerprintDaemonExt/default
[email protected]::IDisplayModes/default
[email protected]::IPictureAdjustment/default
vendor.lineage.power.IPower/default
[email protected]::IUsbRestrict/default
[email protected]::IFactory/default
[email protected]::IEsePowerManager/default
[email protected]::ILocHidlGnss/gnss_vendor
[email protected]::ILocHidlGnss/gnss_vendor
[email protected]::ILocHidlGnss/gnss_vendor
[email protected]::ILocHidlGnss/gnss_vendor
[email protected]::IAlarm/default
[email protected]::IAudioHalExt/default
[email protected]::IBluetoothAudioProvidersFactory/default
[email protected]::IBluetoothSar/default
[email protected]::IBTConfigStore/default
[email protected]::ICapabilityConfigStore/default
[email protected]::IDataConnection/slot1
[email protected]::IDataConnection/slot2
[email protected]::IQtiAllocator/default
[email protected]::IQtiAllocator/default
[email protected]::IQtiMapper/default
[email protected]::IQtiMapper/default
[email protected]::IDspService/dspservice
[email protected]::IFmHci/default
[email protected]::IIop/default
[email protected]::IPerf/default
[email protected]::IQccvndhal/qccvndhal
[email protected]::IQSEECom/default
[email protected]::IAppConnector/default
[email protected]::IGPAppConnector/default
[email protected]::IQcRilAudio/slot1
[email protected]::IQcRilAudio/slot2
[email protected]::IImsRadio/imsradio0
[email protected]::IImsRadio/imsradio1
[email protected]::IUimLpa/UimLpa0
[email protected]::IUimLpa/UimLpa1
[email protected]::IQtiOemHook/oemhook0
[email protected]::IQtiOemHook/oemhook1
[email protected]::IQtiRadio/slot1
[email protected]::IQtiRadio/slot2
[email protected]::IQtiRadio/slot1
[email protected]::IQtiRadio/slot2
[email protected]::IUim/Uim0
[email protected]::IUim/Uim1
[email protected]::IUimRemoteServiceClient/uimRemoteClient0
[email protected]::IUimRemoteServiceClient/uimRemoteClient1
[email protected]::IUimRemoteServiceServer/uimRemoteServer0
[email protected]::IUimRemoteServiceServer/uimRemoteServer1
[email protected]::ISensorsCalibrate/default
[email protected]::ISoter/default
[email protected]::ITuiComm/default
[email protected]::IHidlVppService/vppService
[email protected]::IWifiDisplaySessionImageTrack/wifidisplaysessionimagetrack
[email protected]::IRTPService/imsrtpservice
[email protected]::IXiaomiFingerprint/default
[email protected]::IMlipayService/default
# Author: Sebastiano Barezzi <[email protected]>
# Version: 1.3
from re import search
class Version:
def __init__(self, version: str):
self.major, self.minor = version.split(".")
def merge_version(self, version):
if version.minor > self.minor:
self.minor = version.minor
def format(self):
version_str = ' <version>'
if int(self.minor) > 0:
version_str += f"{self.major}.0-{self.minor}"
else:
version_str += f"{self.major}.{self.minor}"
version_str += '</version>\n'
return version_str
class Interface:
def __init__(self, name: str, instance: str):
self.name = name
self.instances = [instance]
def merge_interface(self, interface):
for instance in interface.instances:
if not instance in self.instances:
self.instances += [instance]
def format(self):
interface_str = ' <interface>\n'
interface_str += f' <name>{self.name}</name>\n'
for instance in self.instances:
interface_str += f' <instance>{instance}</instance>\n'
interface_str += ' </interface>\n'
return interface_str
class Entry:
def __init__(self, fqname: str):
self.type = "HIDL" if "@" in fqname else "AIDL"
if self.type == "HIDL":
self.name, version = fqname.split("::")[0].split("@")
interface_name, interface_instance = fqname.split("::")[1].split("/", 1)
else:
self.name, interface_str = fqname.rsplit(".", 1)
interface_name, interface_instance = interface_str.split("/")
if self.type == "HIDL":
version = Version(version)
self.versions = {version.major: version}
else:
self.versions = {}
interface = Interface(interface_name, interface_instance)
self.interfaces = {interface.name: interface}
def merge_entry(self, entry):
if entry.name != self.name:
raise AssertionError("Different entry name")
if entry.type != self.type:
raise AssertionError("Different HAL type")
for version_major, version in entry.versions.items():
if version_major in self.versions:
self.versions[version_major].merge_version(version)
else:
self.versions[version_major] = version
for interface_name, interface in entry.interfaces.items():
if interface_name in self.interfaces:
self.interfaces[interface_name].merge_interface(interface)
else:
self.interfaces[interface_name] = interface
pass
def format(self):
entry_str = f'<hal format="{self.type.lower()}" optional="true">\n'
entry_str += f' <name>{self.name}</name>\n'
for version in self.versions.values():
entry_str += version.format()
for interface in self.interfaces.values():
entry_str += interface.format()
entry_str += '</hal>\n'
return entry_str
def main():
entries = {}
for fqname in open("fqnames.txt").readlines():
fqname = fqname.strip()
if fqname == "" or fqname[0] == '#':
continue
versioned_aidl_match = search(" \(@[0-9]+\)$", fqname)
if versioned_aidl_match:
fqname = fqname.removesuffix(versioned_aidl_match.group(0))
entry = Entry(fqname)
if entry.name in entries:
entries[entry.name].merge_entry(entry)
else:
entries[entry.name] = entry
fcms = [entry.format() for entry in entries.values()]
print("".join(fcms))
return
main()
@davigamer987
Copy link

Traceback (most recent call last):
File "/home/davi/generate_fcm.py", line 116, in
main()
File "/home/davi/generate_fcm.py", line 104, in main
entry = Entry(fqname)
File "/home/davi/generate_fcm.py", line 47, in init
interface_name, interface_instance = fqname.split("::")[1].split("/", 1)
IndexError: list index out of range

@davigamer987
Copy link

This is my fqnames, i think it's formated properly https://nekobin.com/nevutivuvu

@SebaUbuntu
Copy link
Author

This is my fqnames, i think it's formated properly https://nekobin.com/nevutivuvu

Should be fixed now

@Waxaranai
Copy link

Hey, it doesn't work when there is aidl type entry
example: android.hardware.power.IPower/default (@2)

@RDS-007
Copy link

RDS-007 commented Aug 18, 2022

ye. I too faced it

@karthik1896
Copy link

Hey, it doesn't work when there is aidl type entry example: android.hardware.power.IPower/default (@2)

Remove those values with braces . It should be android.hardware.power.IPower/default

@erikdrozina
Copy link

Hey, it doesn't work when there is aidl type entry example: android.hardware.power.IPower/default (@2)

The ouput is:

<hal format="aidl" optional="true">
    <name>android.hardware.power</name>
    <version>2</version>
    <interface>
        <name>IPower</name>
        <instance>default</instance>
    </interface>
</hal>

@itsxrp
Copy link

itsxrp commented Jan 3, 2023

from: too many arguments
generate_fcm.py: line 6: class: command not found
generate_fcm.py: line 7: syntax error near unexpected token (' generate_fcm.py: line 7: def init(self, version: str):'

any clue why it appearing

@fuyukihidekii
Copy link

from: too many arguments generate_fcm.py: line 6: class: command not found generate_fcm.py: line 7: syntax error near unexpected token (' generate_fcm.py: line 7: def init(self, version: str):'

any clue why it appearing

python3 generate_fcm.py

@s4704
Copy link

s4704 commented Oct 3, 2023

Not work for me: https://gist.github.com/s4704/c8171239a87fefac3f6bb83ab1419f6a
python3 generate_fcm.py Traceback (most recent call last): File "/home/igor/dev/distrib/generate_fcm/generate_fcm.py", line 122, in <module> main() File "/home/igor/dev/distrib/generate_fcm/generate_fcm.py", line 112, in main entries[entry.name].merge_entry(entry) File "/home/igor/dev/distrib/generate_fcm/generate_fcm.py", line 68, in merge_entry raise AssertionError("Different HAL type") AssertionError: Different HAL type

@SharmagRit
Copy link

Traceback (most recent call last):
File "/home/sharmagrit/generate_fcm.py", line 122, in
main()
File "/home/sharmagrit/generate_fcm.py", line 112, in main
entries[entry.name].merge_entry(entry)
File "/home/sharmagrit/generate_fcm.py", line 68, in merge_entry
raise AssertionError("Different HAL type")
AssertionError: Different HAL type

@dodyirawan85
Copy link

Hey, it doesn't work when there is aidl type entry example: android.hardware.power.IPower/default (@2)

I try to adapt the code to support versioning aidl here
Feel free to test and suggest better code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment