Fix cross user package visibility leakage for PackageManager (1/n)

APIs:
- IPackageManager#isPackageSignedByKeySet
- IPackageManager#isPackageSignedByKeySetExactly

Bug: 214394643
Test: atest CrossUserPackageVisibilityTests
Change-Id: I14e4cc5cf27f205353fe709bc2bc8b45778b0764
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
index 479ef8e..cdb6324 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
+++ b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
@@ -30,6 +30,7 @@
         "compatibility-device-util-axt",
         "androidx.test.runner",
         "truth-prebuilt",
+        "Harrier",
     ],
     platform_apis: true,
     test_suites: ["device-tests"],
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/AndroidManifest.xml b/services/tests/PackageManagerServiceTests/appenumeration/AndroidManifest.xml
index 2039aaa..0395aa8 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/AndroidManifest.xml
+++ b/services/tests/PackageManagerServiceTests/appenumeration/AndroidManifest.xml
@@ -16,7 +16,15 @@
   -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.appenumeration">
+          xmlns:tools="http://schemas.android.com/tools"
+          package="com.android.server.pm.test.appenumeration">
+
+    <queries>
+        <package android:name="com.android.appenumeration.crossuserpackagevisibility" />
+    </queries>
+
+    <!-- It's merged from Harrier library. Remove it since this test should not hold it. -->
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" tools:node="remove" />
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.server.pm.test.appenumeration"
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml b/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
index 67efa14..f48974a 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
+++ b/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
@@ -34,6 +34,7 @@
         <option name="push" value="AppEnumerationSyncProviderTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationSyncProviderTestApp.apk" />
         <option name="push" value="AppEnumerationHasAppOpPermissionTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationHasAppOpPermissionTestApp.apk" />
         <option name="push" value="AppEnumerationSharedUserTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationSharedUserTestApp.apk" />
+        <option name="push" value="AppEnumerationCrossUserPackageVisibilityTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationCrossUserPackageVisibilityTestApp.apk" />
     </target_preparer>
 
     <option name="test-tag" value="AppEnumerationInternalTest" />
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 3f6199c..437875b 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
@@ -16,30 +16,78 @@
 
 package com.android.server.pm.test.appenumeration;
 
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertThrows;
 
 import android.app.AppGlobals;
 import android.app.Instrumentation;
+import android.content.Context;
 import android.content.pm.IPackageManager;
+import android.content.pm.KeySet;
 import android.os.UserHandle;
 
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.bedstead.harrier.BedsteadJUnit4;
+import com.android.bedstead.harrier.DeviceState;
+import com.android.bedstead.harrier.annotations.EnsureHasSecondaryUser;
+import com.android.bedstead.nene.users.UserReference;
+
+import org.junit.After;
 import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@RunWith(AndroidJUnit4.class)
+import java.io.File;
+
+/**
+ * Verify that app without holding the {@link android.Manifest.permission.INTERACT_ACROSS_USERS}
+ * can't detect the existence of another app in the different users on the device via the
+ * side channel attacks.
+ */
+@EnsureHasSecondaryUser
+@RunWith(BedsteadJUnit4.class)
 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 File CROSS_USER_TEST_APK_FILE =
+            new File(TEST_DATA_DIR, "AppEnumerationCrossUserPackageVisibilityTestApp.apk");
+
+    @ClassRule
+    @Rule
+    public static final DeviceState sDeviceState = new DeviceState();
 
     private Instrumentation mInstrumentation;
     private IPackageManager mIPackageManager;
+    private Context mContext;
+    private UserReference mOtherUser;
 
     @Before
     public void setup() {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mIPackageManager = AppGlobals.getPackageManager();
+        mContext = mInstrumentation.getContext();
+
+        // Get another user
+        final UserReference primaryUser = sDeviceState.primaryUser();
+        if (primaryUser.id() == UserHandle.myUserId()) {
+            mOtherUser = sDeviceState.secondaryUser();
+        } else {
+            mOtherUser = primaryUser;
+        }
+
+        uninstallPackage(CROSS_USER_TEST_PACKAGE_NAME);
+    }
+
+    @After
+    public void tearDown() {
+        uninstallPackage(CROSS_USER_TEST_PACKAGE_NAME);
     }
 
     @Test
@@ -49,4 +97,45 @@
                 () -> mIPackageManager.getSplashScreenTheme(
                         mInstrumentation.getContext().getPackageName(), crossUserId));
     }
+
+    @Test
+    public void testIsPackageSignedByKeySet_cannotDetectCrossUserPkg() throws Exception {
+        final KeySet keySet = mIPackageManager.getSigningKeySet(mContext.getPackageName());
+        assertThrows(IllegalArgumentException.class,
+                () -> mIPackageManager.isPackageSignedByKeySet(
+                        CROSS_USER_TEST_PACKAGE_NAME, keySet));
+
+        installPackageForUser(CROSS_USER_TEST_APK_FILE, mOtherUser);
+
+        assertThrows(IllegalArgumentException.class,
+                () -> mIPackageManager.isPackageSignedByKeySet(
+                        CROSS_USER_TEST_PACKAGE_NAME, keySet));
+    }
+
+    @Test
+    public void testIsPackageSignedByKeySetExactly_cannotDetectCrossUserPkg() throws Exception {
+        final KeySet keySet = mIPackageManager.getSigningKeySet(mContext.getPackageName());
+        assertThrows(IllegalArgumentException.class,
+                () -> mIPackageManager.isPackageSignedByKeySetExactly(
+                        CROSS_USER_TEST_PACKAGE_NAME, keySet));
+
+        installPackageForUser(CROSS_USER_TEST_APK_FILE, mOtherUser);
+
+        assertThrows(IllegalArgumentException.class,
+                () -> mIPackageManager.isPackageSignedByKeySetExactly(
+                        CROSS_USER_TEST_PACKAGE_NAME, keySet));
+    }
+
+    private static void installPackageForUser(File apk, UserReference user) {
+        assertThat(apk.exists()).isTrue();
+        final StringBuilder cmd = new StringBuilder("pm install --user ");
+        cmd.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);
+    }
 }
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp
index e0f8327..9921106 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp
+++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp
@@ -62,3 +62,17 @@
     test_suites: ["device-tests"],
     platform_apis: true,
 }
+
+android_test_helper_app {
+    name: "AppEnumerationCrossUserPackageVisibilityTestApp",
+    srcs: ["src/**/*.java"],
+    manifest: "AndroidManifest-crossUserPackageVisibility.xml",
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+    test_suites: ["device-tests"],
+    platform_apis: true,
+}
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-crossUserPackageVisibility.xml b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-crossUserPackageVisibility.xml
new file mode 100644
index 0000000..874a1fc
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-crossUserPackageVisibility.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2022 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+     package="com.android.appenumeration.crossuserpackagevisibility">
+    <application>
+    </application>
+</manifest>