Skip to content

Commit c0909c5

Browse files
committed
Add user profile support to HiddenUserManager
Introduces methods to retrieve user profile IDs and UserHandles in HiddenUserManager, updates IUserManager interface with getProfileIds and getProfiles, and refactors HiddenPackageManager to use user profiles for package queries. This enhances multi-user support and improves package management across user profiles.
1 parent ff2d8ad commit c0909c5

File tree

3 files changed

+136
-7
lines changed

3 files changed

+136
-7
lines changed

hidden-api/src/main/java/android/os/IUserManager.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ public interface IUserManager extends IInterface {
1414

1515
UserInfo getUserInfo(int userId);
1616

17+
int[] getProfileIds(int userId, boolean enabledOnly);
18+
19+
List<UserInfo> getProfiles(int userId, boolean enabledOnly);
20+
1721
abstract class Stub extends Binder implements IUserManager {
1822

1923
public static IUserManager asInterface(IBinder obj) {

platform/src/main/kotlin/com/dergoogler/mmrl/platform/hiddenApi/HiddenPackageManager.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@file:Suppress("unused")
1+
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
22

33
package com.dergoogler.mmrl.platform.hiddenApi
44

@@ -65,10 +65,12 @@ class HiddenPackageManager(
6565
fun getInstalledPackagesAll(userManager: HiddenUserManager, flags: Int = 0): List<PackageInfo> {
6666
return try {
6767
val packages = mutableListOf<PackageInfo>()
68-
val userInfos = userManager.getUsers()
69-
for (userInfo in userInfos) {
70-
packages.addAll(getInstalledPackages(flags, userInfo.id))
71-
}
68+
val userProfileIds = userManager.getUserProfiles().map { it.hashCode() }
69+
70+
packages.addAll(userProfileIds.flatMap {
71+
getInstalledPackages(flags, it)
72+
})
73+
7274
packages
7375
} catch (e: Exception) {
7476
Log.e(TAG, "getInstalledPackagesAll", e)

platform/src/main/kotlin/com/dergoogler/mmrl/platform/hiddenApi/HiddenUserManager.kt

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
1-
@file:Suppress("UNCHECKED_CAST")
1+
@file:Suppress("UNCHECKED_CAST", "UnusedReceiverParameter", "MemberVisibilityCanBePrivate")
22

33
package com.dergoogler.mmrl.platform.hiddenApi
44

55
import android.content.Context
66
import android.content.pm.UserInfo
77
import android.os.IUserManager
8+
import android.os.Parcel
89
import android.os.Process
910
import android.os.RemoteException
11+
import android.os.UserHandle
1012
import android.util.Log
13+
import android.util.SparseArray
14+
import androidx.core.util.size
1115
import com.dergoogler.mmrl.platform.PlatformManager.getSystemService
1216
import com.dergoogler.mmrl.platform.stub.IServiceManager
17+
import java.util.Random
18+
1319

1420
class HiddenUserManager(
1521
private val service: IServiceManager,
@@ -68,6 +74,29 @@ class HiddenUserManager(
6874
excludePreCreated = true
6975
)
7076

77+
fun getProfileIds(userId: Int, enabledOnly: Boolean): IntArray {
78+
return try {
79+
userManager.getProfileIds(userId, enabledOnly)
80+
} catch (e: RemoteException) {
81+
Log.w("HiddenUserManager", "Error getting profile ids.", e)
82+
IntArray(0)
83+
}
84+
}
85+
86+
fun getUserProfiles(): List<UserHandle> {
87+
val userIds: IntArray = getProfileIds(myUserId, true)
88+
return convertUserIdsToUserHandles(userIds)
89+
}
90+
91+
fun getUserProfiles(userId: Int): List<UserHandle> = getUserProfiles(userId, true)
92+
93+
fun getUserProfiles(enabledOnly: Boolean): List<UserHandle> = getUserProfiles(myUserId, enabledOnly)
94+
95+
fun getUserProfiles(userId: Int, enabledOnly: Boolean): List<UserHandle> {
96+
val userIds: IntArray = userManager.getProfileIds(userId, enabledOnly)
97+
return convertUserIdsToUserHandles(userIds)
98+
}
99+
71100
fun getUserInfo(userId: Int): UserInfo = userManager.getUserInfo(userId)
72101

73102
fun getUserId(uid: Int): Int = uid / PER_USER_RANGE
@@ -76,7 +105,101 @@ class HiddenUserManager(
76105

77106
fun isSameUser(uid1: Int, uid2: Int): Boolean = getUserId(uid1) == getUserId(uid2)
78107

108+
private fun convertUserIdsToUserHandles(userIds: IntArray): List<UserHandle> {
109+
val result: MutableList<UserHandle> = ArrayList(userIds.size)
110+
111+
for (userId in userIds) {
112+
result.add(of(userId))
113+
}
114+
115+
return result
116+
}
117+
79118
companion object {
80119
const val PER_USER_RANGE: Int = 100000
120+
121+
const val MU_ENABLED: Boolean = true
122+
val NUM_CACHED_USERS = if (MU_ENABLED) 8 else 0;
123+
124+
val CACHED_USER_HANDLES: Array<UserHandle?> =
125+
arrayOfNulls(NUM_CACHED_USERS)
126+
127+
val extraUserHandleCache: SparseArray<UserHandle> = SparseArray(0)
128+
129+
const val USER_OWNER: Int = 0
130+
const val USER_SYSTEM: Int = 0
131+
const val USER_NULL: Int = -10000
132+
const val USER_ALL: Int = -1
133+
const val USER_CURRENT: Int = -2
134+
const val USER_CURRENT_OR_SELF: Int = -3
135+
const val MIN_SECONDARY_USER_ID: Int = 10
136+
const val MAX_EXTRA_USER_HANDLE_CACHE_SIZE: Int = 32
137+
138+
init {
139+
for (i in CACHED_USER_HANDLES.indices) {
140+
CACHED_USER_HANDLES[i] = userHandle(MIN_SECONDARY_USER_ID + i)
141+
}
142+
}
143+
144+
val OWNER: UserHandle = userHandle(USER_OWNER)
145+
val SYSTEM: UserHandle = userHandle(USER_SYSTEM)
146+
val ALL: UserHandle = userHandle(USER_ALL)
147+
val NULL: UserHandle = userHandle(USER_NULL)
148+
val CURRENT: UserHandle = userHandle(USER_CURRENT)
149+
val CURRENT_OR_SELF: UserHandle = userHandle(USER_CURRENT_OR_SELF)
150+
151+
fun of(userId: Int): UserHandle {
152+
if (userId == USER_SYSTEM) {
153+
return SYSTEM // Most common.
154+
}
155+
156+
// These are sequential; so use a switch. Maybe they'll be optimized to a table lookup.
157+
when (userId) {
158+
USER_ALL -> return ALL
159+
USER_CURRENT -> return CURRENT
160+
USER_CURRENT_OR_SELF -> return CURRENT_OR_SELF
161+
}
162+
163+
if (userId >= MIN_SECONDARY_USER_ID
164+
&& userId < (MIN_SECONDARY_USER_ID + CACHED_USER_HANDLES.size)
165+
) {
166+
return CACHED_USER_HANDLES[userId - MIN_SECONDARY_USER_ID] ?: NULL
167+
}
168+
169+
if (userId == USER_NULL) { // Not common.
170+
return NULL
171+
}
172+
173+
return getUserHandleFromExtraCache(userId)
174+
}
175+
176+
fun getUserHandleFromExtraCache(userId: Int): UserHandle {
177+
synchronized(extraUserHandleCache) {
178+
val extraCached: UserHandle? = extraUserHandleCache.get(userId)
179+
if (extraCached != null) {
180+
return extraCached
181+
}
182+
if (extraUserHandleCache.size >= MAX_EXTRA_USER_HANDLE_CACHE_SIZE) {
183+
extraUserHandleCache.removeAt(
184+
(Random()).nextInt(MAX_EXTRA_USER_HANDLE_CACHE_SIZE)
185+
)
186+
}
187+
val newHandle = userHandle(userId)
188+
extraUserHandleCache.put(userId, newHandle)
189+
return newHandle
190+
}
191+
}
192+
193+
fun userHandle(userId: Int): UserHandle {
194+
val parcel = Parcel.obtain()
195+
parcel.writeInt(userId)
196+
parcel.setDataPosition(0)
197+
198+
199+
val handle: UserHandle = UserHandle.CREATOR.createFromParcel(parcel)
200+
parcel.recycle()
201+
return handle
202+
}
81203
}
82-
}
204+
}
205+

0 commit comments

Comments
 (0)