diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index 8e8ca8605343b..bcffd65d34240 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -227,8 +227,28 @@ public interface Computer extends PackageDataSnapshot {
int userId);
boolean shouldFilterApplication(@NonNull SharedUserSetting sus, int callingUid,
int userId);
+ /**
+ * Different form {@link #shouldFilterApplication(PackageStateInternal, int, int)}, the function
+ * returns {@code true} if the target package is not found in the device or uninstalled in the
+ * current user. Unless the caller's function needs to handle the package's uninstalled state
+ * by itself, using this function to keep the consistent behavior between conditions of package
+ * uninstalled and visibility not allowed to avoid the side channel leakage of package
+ * existence.
+ *
+ * Package with {@link PackageManager#SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN} is not
+ * treated as an uninstalled package for the carrier apps customization.
+ */
boolean shouldFilterApplicationIncludingUninstalled(@Nullable PackageStateInternal ps,
int callingUid, int userId);
+ /**
+ * Different from {@link #shouldFilterApplication(SharedUserSetting, int, int)}, the function
+ * returns {@code true} if packages with the same shared user are all uninstalled in the current
+ * user.
+ *
+ * @see #shouldFilterApplicationIncludingUninstalled(PackageStateInternal, int, int)
+ */
+ boolean shouldFilterApplicationIncludingUninstalled(@NonNull SharedUserSetting sus,
+ int callingUid, int userId);
int checkUidPermission(String permName, int uid);
int getPackageUidInternal(String packageName, long flags, int userId, int callingUid);
long updateFlagsForApplication(long flags, int userId);
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 24004bc47954f..edbaba5faa2d7 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -2784,6 +2784,26 @@ public class ComputerEngine implements Computer {
ps, callingUid, null, TYPE_UNKNOWN, userId, true /* filterUninstall */);
}
+ /**
+ * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean)
+ */
+ public final boolean shouldFilterApplicationIncludingUninstalled(
+ @NonNull SharedUserSetting sus, int callingUid, int userId) {
+ if (shouldFilterApplication(sus, callingUid, userId)) {
+ return true;
+ }
+ final ArraySet packageStates =
+ (ArraySet) sus.getPackageStates();
+ for (int index = 0; index < packageStates.size(); index++) {
+ final PackageStateInternal ps = packageStates.valueAt(index);
+ if (ps.getUserStateOrDefault(userId).isInstalled() || ps.isHiddenUntilInstalled()) {
+ return false;
+ }
+ }
+ // Filter it, all packages with the same shared uid are uninstalled.
+ return true;
+ }
+
/**
* Verification statuses are ordered from the worse to the best, except for
* INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER, which is the worse.
@@ -4243,55 +4263,38 @@ public class ComputerEngine implements Computer {
@Override
public int checkUidSignatures(int uid1, int uid2) {
final int callingUid = Binder.getCallingUid();
- final int callingUserId = UserHandle.getUserId(callingUid);
- // Map to base uids.
- final int appId1 = UserHandle.getAppId(uid1);
- final int appId2 = UserHandle.getAppId(uid2);
- SigningDetails p1SigningDetails;
- SigningDetails p2SigningDetails;
- Object obj = mSettings.getSettingBase(appId1);
- if (obj != null) {
- if (obj instanceof SharedUserSetting) {
- final SharedUserSetting sus = (SharedUserSetting) obj;
- if (shouldFilterApplication(sus, callingUid, callingUserId)) {
- return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
- }
- p1SigningDetails = sus.signatures.mSigningDetails;
- } else if (obj instanceof PackageSetting) {
- final PackageSetting ps = (PackageSetting) obj;
- if (shouldFilterApplication(ps, callingUid, callingUserId)) {
- return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
- }
- p1SigningDetails = ps.getSigningDetails();
- } else {
- return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
- }
- } else {
- return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
- }
- obj = mSettings.getSettingBase(appId2);
- if (obj != null) {
- if (obj instanceof SharedUserSetting) {
- final SharedUserSetting sus = (SharedUserSetting) obj;
- if (shouldFilterApplication(sus, callingUid, callingUserId)) {
- return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
- }
- p2SigningDetails = sus.signatures.mSigningDetails;
- } else if (obj instanceof PackageSetting) {
- final PackageSetting ps = (PackageSetting) obj;
- if (shouldFilterApplication(ps, callingUid, callingUserId)) {
- return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
- }
- p2SigningDetails = ps.getSigningDetails();
- } else {
- return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
- }
- } else {
+ final SigningDetails p1SigningDetails = getSigningDetailsAndFilterAccess(uid1, callingUid);
+ final SigningDetails p2SigningDetails = getSigningDetailsAndFilterAccess(uid2, callingUid);
+ if (p1SigningDetails == null || p2SigningDetails == null) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
return checkSignaturesInternal(p1SigningDetails, p2SigningDetails);
}
+ private SigningDetails getSigningDetailsAndFilterAccess(int uid, int callingUid) {
+ // Map to base uids.
+ final int appId = UserHandle.getAppId(uid);
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ final Object obj = mSettings.getSettingBase(appId);
+ if (obj == null) {
+ return null;
+ }
+ if (obj instanceof SharedUserSetting) {
+ final SharedUserSetting sus = (SharedUserSetting) obj;
+ if (shouldFilterApplicationIncludingUninstalled(sus, callingUid, callingUserId)) {
+ return null;
+ }
+ return sus.signatures.mSigningDetails;
+ } else if (obj instanceof PackageSetting) {
+ final PackageSetting ps = (PackageSetting) obj;
+ if (shouldFilterApplicationIncludingUninstalled(ps, callingUid, callingUserId)) {
+ return null;
+ }
+ return ps.getSigningDetails();
+ }
+ return null;
+ }
+
private int checkSignaturesInternal(SigningDetails p1SigningDetails,
SigningDetails p2SigningDetails) {
if (p1SigningDetails == null) {
@@ -4353,28 +4356,8 @@ public class ComputerEngine implements Computer {
public boolean hasUidSigningCertificate(int uid, @NonNull byte[] certificate,
@PackageManager.CertificateInputType int type) {
final int callingUid = Binder.getCallingUid();
- final int callingUserId = UserHandle.getUserId(callingUid);
- // Map to base uids.
- final int appId = UserHandle.getAppId(uid);
- final SigningDetails signingDetails;
- final Object obj = mSettings.getSettingBase(appId);
- if (obj != null) {
- if (obj instanceof SharedUserSetting) {
- final SharedUserSetting sus = (SharedUserSetting) obj;
- if (shouldFilterApplication(sus, callingUid, callingUserId)) {
- return false;
- }
- signingDetails = sus.signatures.mSigningDetails;
- } else if (obj instanceof PackageSetting) {
- final PackageSetting ps = (PackageSetting) obj;
- if (shouldFilterApplication(ps, callingUid, callingUserId)) {
- return false;
- }
- signingDetails = ps.getSigningDetails();
- } else {
- return false;
- }
- } else {
+ final SigningDetails signingDetails = getSigningDetailsAndFilterAccess(uid, callingUid);
+ if (signingDetails == null) {
return false;
}
switch (type) {
@@ -4437,13 +4420,13 @@ public class ComputerEngine implements Computer {
final Object obj = mSettings.getSettingBase(appId);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
- if (shouldFilterApplication(sus, callingUid, callingUserId)) {
+ if (shouldFilterApplicationIncludingUninstalled(sus, callingUid, callingUserId)) {
return null;
}
return sus.name + ":" + sus.mAppId;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
- if (shouldFilterApplication(ps, callingUid, callingUserId)) {
+ if (shouldFilterApplicationIncludingUninstalled(ps, callingUid, callingUserId)) {
return null;
}
return ps.getPackageName();
@@ -4472,14 +4455,14 @@ public class ComputerEngine implements Computer {
final Object obj = mSettings.getSettingBase(appId);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
- if (shouldFilterApplication(sus, callingUid, callingUserId)) {
+ if (shouldFilterApplicationIncludingUninstalled(sus, callingUid, callingUserId)) {
names[i] = null;
} else {
names[i] = "shared:" + sus.name;
}
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
- if (shouldFilterApplication(ps, callingUid, callingUserId)) {
+ if (shouldFilterApplicationIncludingUninstalled(ps, callingUid, callingUserId)) {
names[i] = null;
} else {
names[i] = ps.getPackageName();
@@ -4501,7 +4484,7 @@ public class ComputerEngine implements Computer {
return Process.INVALID_UID;
}
final SharedUserSetting suid = mSettings.getSharedUserFromId(sharedUserName);
- if (suid != null && !shouldFilterApplication(suid, callingUid,
+ if (suid != null && !shouldFilterApplicationIncludingUninstalled(suid, callingUid,
UserHandle.getUserId(callingUid))) {
return suid.mAppId;
}
@@ -4522,13 +4505,13 @@ public class ComputerEngine implements Computer {
final Object obj = mSettings.getSettingBase(appId);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
- if (shouldFilterApplication(sus, callingUid, callingUserId)) {
+ if (shouldFilterApplicationIncludingUninstalled(sus, callingUid, callingUserId)) {
return 0;
}
return sus.getFlags();
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
- if (shouldFilterApplication(ps, callingUid, callingUserId)) {
+ if (shouldFilterApplicationIncludingUninstalled(ps, callingUid, callingUserId)) {
return 0;
}
return ps.getFlags();
@@ -4550,13 +4533,13 @@ public class ComputerEngine implements Computer {
final Object obj = mSettings.getSettingBase(appId);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
- if (shouldFilterApplication(sus, callingUid, callingUserId)) {
+ if (shouldFilterApplicationIncludingUninstalled(sus, callingUid, callingUserId)) {
return 0;
}
return sus.getPrivateFlags();
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
- if (shouldFilterApplication(ps, callingUid, callingUserId)) {
+ if (shouldFilterApplicationIncludingUninstalled(ps, callingUid, callingUserId)) {
return 0;
}
return ps.getPrivateFlags();
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
index a7ba45f0c4c2b..d7b442c8eaf8a 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
+++ b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
@@ -27,6 +27,8 @@ import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.KeySet;
+import android.content.pm.PackageManager;
+import android.os.Process;
import android.os.UserHandle;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -56,8 +58,12 @@ public class CrossUserPackageVisibilityTests {
private static final String TEST_DATA_DIR = "/data/local/tmp/appenumerationtests";
private static final String CROSS_USER_TEST_PACKAGE_NAME =
"com.android.appenumeration.crossuserpackagevisibility";
+ private static final String SHARED_USER_TEST_PACKAGE_NAME =
+ "com.android.appenumeration.shareduid";
private static final File CROSS_USER_TEST_APK_FILE =
new File(TEST_DATA_DIR, "AppEnumerationCrossUserPackageVisibilityTestApp.apk");
+ private static final File SHARED_USER_TEST_APK_FILE =
+ new File(TEST_DATA_DIR, "AppEnumerationSharedUserTestApp.apk");
@ClassRule
@Rule
@@ -66,6 +72,7 @@ public class CrossUserPackageVisibilityTests {
private Instrumentation mInstrumentation;
private IPackageManager mIPackageManager;
private Context mContext;
+ private UserReference mCurrentUser;
private UserReference mOtherUser;
@Before
@@ -77,17 +84,21 @@ public class CrossUserPackageVisibilityTests {
// Get another user
final UserReference primaryUser = sDeviceState.primaryUser();
if (primaryUser.id() == UserHandle.myUserId()) {
+ mCurrentUser = primaryUser;
mOtherUser = sDeviceState.secondaryUser();
} else {
+ mCurrentUser = sDeviceState.secondaryUser();
mOtherUser = primaryUser;
}
uninstallPackage(CROSS_USER_TEST_PACKAGE_NAME);
+ uninstallPackage(SHARED_USER_TEST_PACKAGE_NAME);
}
@After
public void tearDown() {
uninstallPackage(CROSS_USER_TEST_PACKAGE_NAME);
+ uninstallPackage(SHARED_USER_TEST_PACKAGE_NAME);
}
@Test
@@ -151,16 +162,61 @@ public class CrossUserPackageVisibilityTests {
assertThat(e1.getMessage()).isEqualTo(e2.getMessage());
}
+ @Test
+ public void testGetFlagsForUid_cannotDetectCrossUserPkg() throws Exception {
+ installPackage(CROSS_USER_TEST_APK_FILE);
+ final int uid = mContext.getPackageManager().getPackageUid(
+ CROSS_USER_TEST_PACKAGE_NAME, PackageManager.PackageInfoFlags.of(0));
+
+ uninstallPackageForUser(CROSS_USER_TEST_PACKAGE_NAME, mCurrentUser);
+
+ assertThat(mIPackageManager.getFlagsForUid(uid)).isEqualTo(0);
+ }
+
+ @Test
+ public void testGetUidForSharedUser_cannotDetectSharedUserPkg() throws Exception {
+ assertThat(mIPackageManager.getUidForSharedUser(SHARED_USER_TEST_PACKAGE_NAME))
+ .isEqualTo(Process.INVALID_UID);
+
+ installPackageForUser(SHARED_USER_TEST_APK_FILE, mOtherUser, true /* forceQueryable */);
+
+ assertThat(mIPackageManager.getUidForSharedUser(SHARED_USER_TEST_PACKAGE_NAME))
+ .isEqualTo(Process.INVALID_UID);
+ }
+
+ private static void installPackage(File apk) {
+ installPackageForUser(apk, null, false /* forceQueryable */);
+ }
+
private static void installPackageForUser(File apk, UserReference user) {
+ installPackageForUser(apk, user, false /* forceQueryable */);
+ }
+
+ private static void installPackageForUser(File apk, UserReference user,
+ boolean forceQueryable) {
assertThat(apk.exists()).isTrue();
- final StringBuilder cmd = new StringBuilder("pm install --user ");
- cmd.append(user.id()).append(" ");
+ final StringBuilder cmd = new StringBuilder("pm install -t ");
+ if (forceQueryable) {
+ cmd.append("--force-queryable ");
+ }
+ if (user != null) {
+ cmd.append("--user ").append(user.id()).append(" ");
+ }
cmd.append(apk.getPath());
final String result = runShellCommand(cmd.toString());
assertThat(result.trim()).contains("Success");
}
private static void uninstallPackage(String packageName) {
- runShellCommand("pm uninstall " + packageName);
+ uninstallPackageForUser(packageName, null /* user */);
+ }
+
+ private static void uninstallPackageForUser(String packageName, UserReference user) {
+ final StringBuilder cmd = new StringBuilder("pm uninstall ");
+ if (user != null) {
+ cmd.append("--user ").append(user.id()).append(" ");
+ }
+ cmd.append(packageName);
+ runShellCommand(cmd.toString());
}
}
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-crossUserPackageVisibility.xml b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-crossUserPackageVisibility.xml
index 874a1fc5ff3e8..9d38ddfab5f10 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-crossUserPackageVisibility.xml
+++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-crossUserPackageVisibility.xml
@@ -17,6 +17,6 @@
-
+