@@ -8,11 +8,13 @@ import android.content.IntentFilter
88import android.content.pm.IPackageManager
99import android.content.pm.PackageInfo
1010import android.content.pm.PackageManager
11+ import android.content.pm.ParceledListSlice
1112import android.content.pm.ResolveInfo
1213import android.content.pm.ServiceInfo
1314import android.os.Build
1415import android.os.IUserManager
1516import android.util.Log
17+ import java.lang.reflect.Method
1618import org.matrix.vector.daemon.utils.getRealUsers
1719
1820private const val TAG = " VectorSystem"
@@ -131,6 +133,37 @@ fun IPackageManager.clearApplicationProfileDataCompat(packageName: String) {
131133 runCatching { clearApplicationProfileData(packageName) }
132134}
133135
136+ /* * Cached method reference to avoid repeated reflection lookups in loops. */
137+ private val getInstalledPackagesMethod: Method ? by lazy {
138+ val isLongFlags = Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU
139+ android.content.pm.IPackageManager ::class
140+ .java
141+ .declaredMethods
142+ .find {
143+ it.name == " getInstalledPackages" &&
144+ it.parameterTypes.size == 2 &&
145+ it.parameterTypes[0 ] ==
146+ (if (isLongFlags) Long ::class .javaPrimitiveType else Int ::class .javaPrimitiveType)
147+ }
148+ ?.apply { isAccessible = true }
149+ }
150+
151+ /* *
152+ * Reflectively calls getInstalledPackages and casts to ParceledListSlice. This works on Android 17+
153+ * because PackageInfoList extends ParceledListSlice.
154+ */
155+ private fun IPackageManager.getInstalledPackagesReflect (
156+ flags : Any ,
157+ userId : Int
158+ ): List <PackageInfo > {
159+ val method = getInstalledPackagesMethod ? : return emptyList()
160+ return runCatching {
161+ val result = method.invoke(this , flags, userId)
162+ @Suppress(" UNCHECKED_CAST" ) (result as ? ParceledListSlice <PackageInfo >)?.list
163+ }
164+ .getOrNull() ? : emptyList()
165+ }
166+
134167fun IPackageManager.getInstalledPackagesForAllUsers (
135168 flags : Int ,
136169 filterNoProcess : Boolean
@@ -139,16 +172,12 @@ fun IPackageManager.getInstalledPackagesForAllUsers(
139172 val users = userManager?.getRealUsers() ? : emptyList()
140173
141174 for (user in users) {
142- val infos =
143- runCatching {
144- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
145- getInstalledPackages(flags.toLong(), user.id)
146- } else {
147- getInstalledPackages(flags, user.id)
148- }
149- }
150- .getOrNull()
151- ?.list ? : continue
175+ // We pass flags as Any so the reflective invoke handles Long or Int correctly
176+ val flagParam: Any =
177+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) flags.toLong() else flags
178+
179+ val infos = getInstalledPackagesReflect(flagParam, user.id)
180+ if (infos.isEmpty()) continue
152181
153182 result.addAll(
154183 infos.filter {
0 commit comments