@@ -14,7 +14,9 @@ import android.content.pm.ServiceInfo
1414import android.os.Build
1515import android.os.IUserManager
1616import android.util.Log
17+ import java.io.File
1718import java.lang.reflect.Method
19+ import java.util.stream.Collectors
1820import org.matrix.vector.daemon.utils.getRealUsers
1921
2022private const val TAG = " VectorSystem"
@@ -27,27 +29,53 @@ const val MATCH_ALL_FLAGS =
2729 PackageManager .MATCH_UNINSTALLED_PACKAGES or
2830 MATCH_ANY_USER
2931
32+ /* *
33+ * Internal helper that throws exceptions instead of swallowing them. This is crucial for detecting
34+ * TransactionTooLargeException (Binder limits).
35+ */
36+ @Throws(Exception ::class )
37+ private fun IPackageManager.getPackageInfoCompatThrows (
38+ packageName : String ,
39+ flags : Int ,
40+ userId : Int
41+ ): PackageInfo ? {
42+ return if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
43+ getPackageInfo(packageName, flags.toLong(), userId)
44+ } else {
45+ getPackageInfo(packageName, flags, userId)
46+ }
47+ }
48+
3049/* * Safely fetches PackageInfo, handling API level differences. */
3150fun IPackageManager.getPackageInfoCompat (
3251 packageName : String ,
3352 flags : Int ,
3453 userId : Int
3554): PackageInfo ? {
3655 return try {
37- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
38- getPackageInfo(packageName, flags.toLong(), userId)
39- } else {
40- getPackageInfo(packageName, flags, userId)
41- }
56+ getPackageInfoCompatThrows(packageName, flags, userId)
4257 } catch (e: Exception ) {
4358 null
4459 }
4560}
4661
4762/* *
48- * Fetches PackageInfo alongside its components (Activities, Services, Receivers, Providers).
49- * Includes a fallback mechanism to prevent TransactionTooLargeException on massive apps .
63+ * Checks if the package is truly available for the given user. Apps can be "installed" but
64+ * disabled/hidden by profile owners .
5065 */
66+ fun IPackageManager.isPackageAvailable (
67+ packageName : String ,
68+ userId : Int ,
69+ ignoreHidden : Boolean
70+ ): Boolean {
71+ return runCatching {
72+ isPackageAvailable(packageName, userId) ||
73+ (ignoreHidden && getApplicationHiddenSettingAsUser(packageName, userId))
74+ }
75+ .getOrDefault(false )
76+ }
77+
78+ /* * Fetches PackageInfo alongside its components (Activities, Services, Receivers, Providers). */
5179fun IPackageManager.getPackageInfoWithComponents (
5280 packageName : String ,
5381 flags : Int ,
@@ -60,33 +88,55 @@ fun IPackageManager.getPackageInfoWithComponents(
6088 PackageManager .GET_RECEIVERS or
6189 PackageManager .GET_PROVIDERS
6290
63- // Fast path: Try fetching everything at once
64- getPackageInfoCompat(packageName, fullFlags, userId)?.let {
65- return it
66- }
91+ var pkgInfo: PackageInfo ? = null
6792
68- // Fallback path: Fetch sequentially to avoid Binder Transaction limits
69- val baseInfo = getPackageInfoCompat(packageName, flags, userId) ? : return null
93+ try {
94+ // If the binder buffer overflows, it will throw an exception here.
95+ pkgInfo = getPackageInfoCompatThrows(packageName, fullFlags, userId)
96+ } catch (e: Exception ) {
97+ // Fallback path: Fetch sequentially if the initial query threw an Exception
98+ pkgInfo =
99+ try {
100+ getPackageInfoCompatThrows(packageName, flags, userId)
101+ } catch (ignored: Exception ) {
102+ null
103+ }
70104
71- runCatching {
72- baseInfo.activities =
73- getPackageInfoCompat(packageName, flags or PackageManager .GET_ACTIVITIES , userId)
74- ?.activities
75- }
76- runCatching {
77- baseInfo.services =
78- getPackageInfoCompat(packageName, flags or PackageManager .GET_SERVICES , userId)?.services
79- }
80- runCatching {
81- baseInfo.receivers =
82- getPackageInfoCompat(packageName, flags or PackageManager .GET_RECEIVERS , userId)?.receivers
105+ if (pkgInfo != null ) {
106+ runCatching {
107+ pkgInfo.activities =
108+ getPackageInfoCompatThrows(packageName, flags or PackageManager .GET_ACTIVITIES , userId)
109+ ?.activities
110+ }
111+ runCatching {
112+ pkgInfo.services =
113+ getPackageInfoCompatThrows(packageName, flags or PackageManager .GET_SERVICES , userId)
114+ ?.services
115+ }
116+ runCatching {
117+ pkgInfo.receivers =
118+ getPackageInfoCompatThrows(packageName, flags or PackageManager .GET_RECEIVERS , userId)
119+ ?.receivers
120+ }
121+ runCatching {
122+ pkgInfo.providers =
123+ getPackageInfoCompatThrows(packageName, flags or PackageManager .GET_PROVIDERS , userId)
124+ ?.providers
125+ }
126+ }
83127 }
84- runCatching {
85- baseInfo.providers =
86- getPackageInfoCompat(packageName, flags or PackageManager .GET_PROVIDERS , userId)?.providers
128+
129+ if (pkgInfo?.applicationInfo == null ) return null
130+ if (pkgInfo.packageName != " android" ) {
131+ val sourceDir = pkgInfo.applicationInfo?.sourceDir
132+ if (sourceDir == null ||
133+ ! File (sourceDir).exists() ||
134+ ! isPackageAvailable(packageName, userId, true )) {
135+ return null
136+ }
87137 }
88138
89- return baseInfo
139+ return pkgInfo
90140}
91141
92142/* * Extracts all unique process names associated with a package's components. */
@@ -148,10 +198,7 @@ private val getInstalledPackagesMethod: Method? by lazy {
148198 ?.apply { isAccessible = true }
149199}
150200
151- /* *
152- * Reflectively calls getInstalledPackages and casts to ParceledListSlice. This works on Android 17+
153- * because PackageInfoList extends ParceledListSlice.
154- */
201+ /* * Reflectively calls getInstalledPackages and casts to ParceledListSlice. */
155202private fun IPackageManager.getInstalledPackagesReflect (
156203 flags : Any ,
157204 userId : Int
@@ -164,11 +211,12 @@ private fun IPackageManager.getInstalledPackagesReflect(
164211 .getOrNull() ? : emptyList()
165212}
166213
167- fun IPackageManager.getInstalledPackagesForAllUsers (
214+ fun IPackageManager.getInstalledPackagesFromAllUsers (
168215 flags : Int ,
169216 filterNoProcess : Boolean
170217): List <PackageInfo > {
171218 val result = mutableListOf<PackageInfo >()
219+ // Assuming userManager is available in this scope as in original code
172220 val users = userManager?.getRealUsers() ? : emptyList()
173221
174222 for (user in users) {
@@ -179,20 +227,30 @@ fun IPackageManager.getInstalledPackagesForAllUsers(
179227 val infos = getInstalledPackagesReflect(flagParam, user.id)
180228 if (infos.isEmpty()) continue
181229
182- result.addAll(
183- infos.filter {
184- it.applicationInfo != null && it.applicationInfo!! .uid / PER_USER_RANGE == user.id
185- })
230+ val validUserApps =
231+ infos
232+ .parallelStream()
233+ .filter {
234+ it.applicationInfo != null && (it.applicationInfo!! .uid / PER_USER_RANGE ) == user.id
235+ }
236+ .filter { isPackageAvailable(it.packageName, user.id, true ) }
237+ .collect(Collectors .toList())
238+
239+ result.addAll(validUserApps)
186240 }
187241
188242 if (filterNoProcess) {
189- return result.filter {
190- getPackageInfoWithComponents(
191- it.packageName, MATCH_ALL_FLAGS , it.applicationInfo!! .uid / PER_USER_RANGE )
192- ?.fetchProcesses()
193- ?.isNotEmpty() == true
194- }
243+ return result
244+ .parallelStream()
245+ .filter {
246+ getPackageInfoWithComponents(
247+ it.packageName, MATCH_ALL_FLAGS , it.applicationInfo!! .uid / PER_USER_RANGE )
248+ ?.fetchProcesses()
249+ ?.isNotEmpty() == true
250+ }
251+ .collect(Collectors .toList())
195252 }
253+
196254 return result
197255}
198256
0 commit comments