Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 328aa43b authored by Winson's avatar Winson
Browse files

Fix uninstall/disable with multi-user compressed stubs

Updates uninstall logic to ensure that only the user
being acted on is affected.

Previously, uninstalling from any user would disable
the app for user 0. This updates it to only change
the state of the stubbed package for the user that
initiates the uninstall.

This also changes the re-uncompression logic such that
stubs are kept in their system-only state until any
user has marked it installed and enabled.

Also adds tests for all the uninstall scenarios for
a stub system app. Some test cases are migrated from
I6fdd3ed485db0d3f8135266010d6797d9ac37ea0. Only the
corruption test isn't implemented, as this change
doesn't verify the install/scan behavior at boot.

Bug: 159204252

Test: atest com.android.server.pm.test.SystemStubMultiUserDisableUninstallTest
Test: atest PackageManagerServiceHostTests

Change-Id: I4ee2affc058d2bae363c537b3294d5d46a5051a0
parent 8a6daa61
Loading
Loading
Loading
Loading
+32 −18
Original line number Original line Diff line number Diff line
@@ -18530,7 +18530,6 @@ public class PackageManagerService extends IPackageManager.Stub
        // user handle installed state
        // user handle installed state
        int[] allUsers;
        int[] allUsers;
        /** enabled state of the uninstalled application */
        /** enabled state of the uninstalled application */
        final int origEnabledState;
        synchronized (mLock) {
        synchronized (mLock) {
            uninstalledPs = mSettings.mPackages.get(packageName);
            uninstalledPs = mSettings.mPackages.get(packageName);
            if (uninstalledPs == null) {
            if (uninstalledPs == null) {
@@ -18546,10 +18545,6 @@ public class PackageManagerService extends IPackageManager.Stub
            }
            }
            disabledSystemPs = mSettings.getDisabledSystemPkgLPr(packageName);
            disabledSystemPs = mSettings.getDisabledSystemPkgLPr(packageName);
            // Save the enabled state before we delete the package. When deleting a stub
            // application we always set the enabled state to 'disabled'.
            origEnabledState = uninstalledPs == null
                    ? COMPONENT_ENABLED_STATE_DEFAULT : uninstalledPs.getEnabled(userId);
            // Static shared libs can be declared by any package, so let us not
            // Static shared libs can be declared by any package, so let us not
            // allow removing a package if it provides a lib others depend on.
            // allow removing a package if it provides a lib others depend on.
            pkg = mPackages.get(packageName);
            pkg = mPackages.get(packageName);
@@ -18628,20 +18623,32 @@ public class PackageManagerService extends IPackageManager.Stub
            if (stubPkg != null && stubPkg.isStub()) {
            if (stubPkg != null && stubPkg.isStub()) {
                final PackageSetting stubPs;
                final PackageSetting stubPs;
                synchronized (mLock) {
                synchronized (mLock) {
                    // restore the enabled state of the stub; the state is overwritten when
                    // the stub is uninstalled
                    stubPs = mSettings.getPackageLPr(stubPkg.getPackageName());
                    stubPs = mSettings.getPackageLPr(stubPkg.getPackageName());
                }
                if (stubPs != null) {
                if (stubPs != null) {
                        stubPs.setEnabled(origEnabledState, userId, "android");
                    boolean enable = false;
                    for (int aUserId : allUsers) {
                        if (stubPs.getInstalled(aUserId)) {
                            int enabled = stubPs.getEnabled(aUserId);
                            if (enabled == COMPONENT_ENABLED_STATE_DEFAULT
                                    || enabled == COMPONENT_ENABLED_STATE_ENABLED) {
                                enable = true;
                                break;
                            }
                            }
                        }
                        }
                if (origEnabledState == COMPONENT_ENABLED_STATE_DEFAULT
                    }
                        || origEnabledState == COMPONENT_ENABLED_STATE_ENABLED) {
                    if (enable) {
                        if (DEBUG_COMPRESSION) {
                        if (DEBUG_COMPRESSION) {
                            Slog.i(TAG, "Enabling system stub after removal; pkg: "
                            Slog.i(TAG, "Enabling system stub after removal; pkg: "
                                    + stubPkg.getPackageName());
                                    + stubPkg.getPackageName());
                        }
                        }
                        enableCompressedPackage(stubPkg, stubPs);
                        enableCompressedPackage(stubPkg, stubPs);
                    } else if (DEBUG_COMPRESSION) {
                        Slog.i(TAG, "System stub disabled for all users, leaving uncompressed "
                                + "after removal; pkg: " + stubPkg.getPackageName());
                    }
                }
                }
            }
            }
        }
        }
@@ -18985,8 +18992,15 @@ public class PackageManagerService extends IPackageManager.Stub
                // and re-enable it afterward.
                // and re-enable it afterward.
                final PackageSetting stubPs = mSettings.mPackages.get(deletedPkg.getPackageName());
                final PackageSetting stubPs = mSettings.mPackages.get(deletedPkg.getPackageName());
                if (stubPs != null) {
                if (stubPs != null) {
                    stubPs.setEnabled(
                    int userId = action.user == null
                            COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android");
                            ? UserHandle.USER_ALL : action.user.getIdentifier();
                    if (userId == UserHandle.USER_ALL) {
                        for (int aUserId : allUserHandles) {
                            stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, aUserId, "android");
                        }
                    } else if (userId >= UserHandle.USER_SYSTEM) {
                        stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, userId, "android");
                    }
                }
                }
            }
            }
        }
        }
+6 −4
Original line number Original line Diff line number Diff line
@@ -423,13 +423,15 @@ class PackageManagerShellCommand extends ShellCommand {
            final List<ApplicationInfo> list;
            final List<ApplicationInfo> list;
            if (packageName == null) {
            if (packageName == null) {
                final ParceledListSlice<ApplicationInfo> packages =
                final ParceledListSlice<ApplicationInfo> packages =
                        mInterface.getInstalledApplications(
                        mInterface.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY
                                PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
                                        | PackageManager.MATCH_UNINSTALLED_PACKAGES,
                                UserHandle.USER_SYSTEM);
                list = packages.getList();
                list = packages.getList();
            } else {
            } else {
                list = new ArrayList<>(1);
                list = new ArrayList<>(1);
                list.add(mInterface.getApplicationInfo(packageName,
                list.add(mInterface.getApplicationInfo(packageName, PackageManager.MATCH_SYSTEM_ONLY
                        PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM));
                                | PackageManager.MATCH_UNINSTALLED_PACKAGES,
                        UserHandle.USER_SYSTEM));
            }
            }
            for (ApplicationInfo info : list) {
            for (ApplicationInfo info : list) {
                if (info.isUpdatedSystemApp()) {
                if (info.isUpdatedSystemApp()) {
+22 −11
Original line number Original line Diff line number Diff line
@@ -25,17 +25,28 @@ java_test_host {
    ],
    ],
    test_suites: ["general-tests"],
    test_suites: ["general-tests"],
    java_resources: [
    java_resources: [
        ":PackageManagerDummyAppVersion1",
        ":PackageManagerTestAppStub",
        ":PackageManagerDummyAppVersion2",
        ":PackageManagerTestAppVersion1",
        ":PackageManagerDummyAppVersion3",
        ":PackageManagerTestAppVersion2",
        ":PackageManagerDummyAppVersion4",
        ":PackageManagerTestAppVersion3",
        ":PackageManagerDummyAppOriginalOverride",
        ":PackageManagerTestAppVersion3Invalid",
        ":PackageManagerServiceHostTestsResources",
        ":PackageManagerTestAppVersion4",
    ]
        ":PackageManagerTestAppOriginalOverride",
    ],
}
}


filegroup {
genrule {
    name: "PackageManagerServiceHostTestsResources",
    name: "PackageManagerTestAppVersion3Invalid",
    srcs: [ "resources/*" ],
    tools: [
    path: "resources/"
        "soong_zip",
        "zipalign",
    ],
    srcs: [
        ":PackageManagerTestAppVersion3",
    ],
    out: ["PackageManagerTestAppVersion3Invalid.apk"],
    cmd: "mkdir -p $(genDir)/apk && unzip $(in) -d $(genDir)/apk" +
        " && truncate -s 800 $(genDir)/apk/META-INF/CERT.RSA" +
        " && $(location soong_zip) -o $(genDir)/temp.apk -L 0 -C $(genDir)/apk -D $(genDir)/apk" +
        " && $(location zipalign) -f 4 $(genDir)/temp.apk $(out)",
}
}
+54 −2
Original line number Original line Diff line number Diff line
@@ -21,8 +21,9 @@ import com.android.tradefed.device.ITestDevice
import java.io.File
import java.io.File
import java.io.FileOutputStream
import java.io.FileOutputStream


internal fun SystemPreparer.pushApk(file: String, partition: Partition) =
internal fun SystemPreparer.pushApk(javaResourceName: String, partition: Partition) =
        pushResourceFile(file, HostUtils.makePathForApk(file, partition).toString())
        pushResourceFile(javaResourceName, HostUtils.makePathForApk(javaResourceName, partition)
                .toString())


internal fun SystemPreparer.deleteApkFolders(
internal fun SystemPreparer.deleteApkFolders(
    partition: Partition,
    partition: Partition,
@@ -58,4 +59,55 @@ internal object HostUtils {
        }
        }
        return file
        return file
    }
    }

    /**
     * dumpsys package and therefore device.getAppPackageInfo doesn't work immediately after reboot,
     * so the following methods parse the package dump directly to see if the path matches.
     */
    fun getCodePaths(device: ITestDevice, pkgName: String) =
            device.executeShellCommand("pm dump $pkgName")
                    .lineSequence()
                    .map(String::trim)
                    .filter { it.startsWith("codePath=") }
                    .map { it.removePrefix("codePath=") }
                    .toList()

    private fun userIdLineSequence(device: ITestDevice, pkgName: String) =
            device.executeShellCommand("pm dump $pkgName")
                    .lineSequence()
                    .dropWhile { !it.startsWith("Packages:") }
                    .takeWhile {
                        !it.startsWith("Hidden system packages:") &&
                                !it.startsWith("Queries:")
                    }
                    .map(String::trim)
                    .filter { it.startsWith("User ") }

    fun getUserIdToPkgEnabledState(device: ITestDevice, pkgName: String) =
            userIdLineSequence(device, pkgName).associate {
                val userId = it.removePrefix("User ")
                        .takeWhile(Char::isDigit)
                        .toInt()
                val enabled = it.substringAfter("enabled=")
                        .takeWhile(Char::isDigit)
                        .toInt()
                        .let {
                            when (it) {
                                0, 1 -> true
                                else -> false
                            }
                        }
                userId to enabled
            }

    fun getUserIdToPkgInstalledState(device: ITestDevice, pkgName: String) =
            userIdLineSequence(device, pkgName).associate {
                val userId = it.removePrefix("User ")
                        .takeWhile(Char::isDigit)
                        .toInt()
                val installed = it.substringAfter("installed=")
                        .takeWhile { !it.isWhitespace() }
                        .toBoolean()
                userId to installed
            }
}
}
Loading