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

Commit 72b7e673 authored by Alan Stokes's avatar Alan Stokes
Browse files

Preserve install initiatior package on uninstall.

Previously when an installer package was uninstalled we removed all
reference to it in InstallSource.

Now we retain the initiator package name (but not installer or
originator). We set a flag to remember it has been uninstalled, and we
also store its signatures which would otherwise cease to be
available. Update persistence logic.

Also minor refactoring of InstallSource#create to separate the normal
case from those that need to set every field.

This information is not yet exposed in the API, but it will be.

Test: atest PackageManagerTests PackageVerifierTest
Test: Manually verify XML contents before & after uninstalling an installer.
Bug: 134746019
Bug: 146555198
Change-Id: I1452402de554b916e5f79bd386c5857d4860ba12
parent d50c3fc1
Loading
Loading
Loading
Loading
+80 −22
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.server.pm;

import android.annotation.Nullable;

import com.android.internal.util.Preconditions;

import java.util.Objects;

/**
@@ -29,15 +31,26 @@ final class InstallSource {
     * An instance of InstallSource representing an absence of knowledge of the source of
     * a package. Used in preference to null.
     */
    static final InstallSource EMPTY = new InstallSource(null, null, null, false);
    static final InstallSource EMPTY = new InstallSource(null, null, null, false, false, null);

    /** We also memoize this case because it is common - all un-updated system apps. */
    private static final InstallSource EMPTY_ORPHANED = new InstallSource(null, null, null, true);
    private static final InstallSource EMPTY_ORPHANED = new InstallSource(
            null, null, null, true, false, null);

    /** The package that requested the installation, if known. */
    /**
     * The package that requested the installation, if known. May not correspond to a currently
     * installed package if {@link #isInitiatingPackageUninstalled} is true.
     */
    @Nullable
    final String initiatingPackageName;

    /**
     * The signing details of the initiating package, if known. Always null if
     * {@link #initiatingPackageName} is null.
     */
    @Nullable
    final PackageSignatures initiatingPackageSignatures;

    /**
     * The package on behalf of which the initiating package requested the installation, if any.
     * For example if a downloaded APK is installed via the Package Installer this could be the
@@ -57,77 +70,121 @@ final class InstallSource {
    /** Indicates if the package that was the installerPackageName has been uninstalled. */
    final boolean isOrphaned;

    /**
     * Indicates if the package in initiatingPackageName has been uninstalled. Always false if
     * {@link #initiatingPackageName} is null.
     */
    final boolean isInitiatingPackageUninstalled;

    static InstallSource create(@Nullable String initiatingPackageName,
            @Nullable String originatingPackageName, @Nullable String installerPackageName) {
        return create(initiatingPackageName, originatingPackageName, installerPackageName,
                false, false);
    }

    static InstallSource create(@Nullable String initiatingPackageName,
            @Nullable String originatingPackageName, @Nullable String installerPackageName,
            boolean isOrphaned) {
            boolean isOrphaned, boolean isInitiatingPackageUninstalled) {
        return createInternal(
                intern(initiatingPackageName),
                intern(originatingPackageName),
                intern(installerPackageName),
                isOrphaned);
                isOrphaned, isInitiatingPackageUninstalled, null);
    }

    private static InstallSource createInternal(@Nullable String initiatingPackageName,
            @Nullable String originatingPackageName, @Nullable String installerPackageName,
            boolean isOrphaned) {
            boolean isOrphaned, boolean isInitiatingPackageUninstalled,
            @Nullable PackageSignatures initiatingPackageSignatures) {
        if (initiatingPackageName == null && originatingPackageName == null
                && installerPackageName == null) {
                && installerPackageName == null && initiatingPackageSignatures == null
                && !isInitiatingPackageUninstalled) {
            return isOrphaned ? EMPTY_ORPHANED : EMPTY;
        }
        return new InstallSource(initiatingPackageName, originatingPackageName,
                installerPackageName, isOrphaned);
                installerPackageName, isOrphaned, isInitiatingPackageUninstalled,
                initiatingPackageSignatures
        );
    }

    private InstallSource(@Nullable String initiatingPackageName,
            @Nullable String originatingPackageName, @Nullable String installerPackageName,
            boolean isOrphaned) {
            boolean isOrphaned, boolean isInitiatingPackageUninstalled,
            @Nullable PackageSignatures initiatingPackageSignatures) {
        if (initiatingPackageName == null) {
            Preconditions.checkArgument(initiatingPackageSignatures == null);
            Preconditions.checkArgument(!isInitiatingPackageUninstalled);
        }
        this.initiatingPackageName = initiatingPackageName;
        this.originatingPackageName = originatingPackageName;
        this.installerPackageName = installerPackageName;
        this.isOrphaned = isOrphaned;
        this.isInitiatingPackageUninstalled = isInitiatingPackageUninstalled;
        this.initiatingPackageSignatures = initiatingPackageSignatures;
    }

    /**
     * Return an InstallSource the same as this one except with the specified installerPackageName.
     * Return an InstallSource the same as this one except with the specified
     * {@link #installerPackageName}.
     */
    InstallSource setInstallerPackage(String installerPackageName) {
    InstallSource setInstallerPackage(@Nullable String installerPackageName) {
        if (Objects.equals(installerPackageName, this.installerPackageName)) {
            return this;
        }
        return createInternal(initiatingPackageName, originatingPackageName,
                intern(installerPackageName), isOrphaned);
                intern(installerPackageName), isOrphaned, isInitiatingPackageUninstalled,
                initiatingPackageSignatures
        );
    }

    /**
     * Return an InstallSource the same as this one except with the specified value for isOrphaned.
     * Return an InstallSource the same as this one except with the specified value for
     * {@link #isOrphaned}.
     */
    InstallSource setIsOrphaned(boolean isOrphaned) {
        if (isOrphaned == this.isOrphaned) {
            return this;
        }
        return createInternal(initiatingPackageName, originatingPackageName, installerPackageName,
                isOrphaned);
                isOrphaned, isInitiatingPackageUninstalled, initiatingPackageSignatures);
    }

    /**
     * Return an InstallSource the same as this one except it does not refer to the specified
     * installer package name (which is being uninstalled).
     * Return an InstallSource the same as this one except with the specified
     * {@link #initiatingPackageSignatures}.
     */
    InstallSource removeInstallerPackage(String packageName) {
    InstallSource setInitiatingPackageSignatures(@Nullable PackageSignatures signatures) {
        if (signatures == initiatingPackageSignatures) {
            return this;
        }
        return createInternal(initiatingPackageName, originatingPackageName, installerPackageName,
                isOrphaned, isInitiatingPackageUninstalled, signatures);
    }

    /**
     * Return an InstallSource the same as this one updated to reflect that the specified installer
     * package name has been uninstalled.
     */
    InstallSource removeInstallerPackage(@Nullable String packageName) {
        if (packageName == null) {
            return this;
        }

        boolean modified = false;
        String initiatingPackageName = this.initiatingPackageName;
        boolean isInitiatingPackageUninstalled = this.isInitiatingPackageUninstalled;
        String originatingPackageName = this.originatingPackageName;
        String installerPackageName = this.installerPackageName;
        boolean isOrphaned = this.isOrphaned;

        if (packageName.equals(initiatingPackageName)) {
            initiatingPackageName = null;
        if (packageName.equals(this.initiatingPackageName)) {
            if (!isInitiatingPackageUninstalled) {
                // In this case we deliberately do not clear the package name (and signatures).
                // We allow an app to retrieve details of its own install initiator even after
                // it has been uninstalled.
                isInitiatingPackageUninstalled = true;
                modified = true;
            }
        }
        if (packageName.equals(originatingPackageName)) {
            originatingPackageName = null;
            modified = true;
@@ -141,8 +198,9 @@ final class InstallSource {
        if (!modified) {
            return this;
        }

        return createInternal(initiatingPackageName, originatingPackageName, installerPackageName,
                isOrphaned);
                isOrphaned, isInitiatingPackageUninstalled, initiatingPackageSignatures);
    }

    @Nullable
+1 −1
Original line number Diff line number Diff line
@@ -635,7 +635,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
            }
        }
        InstallSource installSource = InstallSource.create(installerPackageName,
                originatingPackageName, requestedInstallerPackageName, false);
                originatingPackageName, requestedInstallerPackageName);
        session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
                mInstallThread.getLooper(), mStagingManager, sessionId, userId, callingUid,
                installSource, params, createdMillis,
+2 −2
Original line number Diff line number Diff line
@@ -1452,7 +1452,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                }

                mInstallerUid = uid;
                mInstallSource = InstallSource.create(packageName, null, packageName, false);
                mInstallSource = InstallSource.create(packageName, null, packageName);
            }
        } catch (PackageManager.NameNotFoundException e) {
            onSessionTransferStatus(statusReceiver, packageName,
@@ -3115,7 +3115,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        }

        InstallSource installSource = InstallSource.create(installInitiatingPackageName,
                installOriginatingPackageName, installerPackageName, false);
                installOriginatingPackageName, installerPackageName);
        return new PackageInstallerSession(callback, context, pm, sessionProvider,
                installerThread, stagingManager, sessionId, userId, installerUid,
                installSource, params, createdMillis, stageDir, stageCid, fileInfosArray,
+26 −12
Original line number Diff line number Diff line
@@ -15158,10 +15158,10 @@ public class PackageManagerService extends IPackageManager.Stub
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
        final String pkgName = pkg.getPackageName();
        final InstallSource installSource = installArgs.installSource;
        final String installerPackageName = installSource.installerPackageName;
        final int[] installedForUsers = res.origUsers;
        final int installReason = installArgs.installReason;
        InstallSource installSource = installArgs.installSource;
        final String installerPackageName = installSource.installerPackageName;
        if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getCodePath());
        synchronized (mLock) {
@@ -15207,6 +15207,14 @@ public class PackageManagerService extends IPackageManager.Stub
                    ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
                }
                if (installSource.initiatingPackageName != null) {
                    final PackageSetting ips = mSettings.mPackages.get(
                            installSource.initiatingPackageName);
                    if (ips != null) {
                        installSource = installSource.setInitiatingPackageSignatures(
                                ips.signatures);
                    }
                }
                ps.setInstallSource(installSource);
                mSettings.addInstallerPackageNames(installSource);
@@ -19976,6 +19984,11 @@ public class PackageManagerService extends IPackageManager.Stub
                }
            }
            if (installSource.isInitiatingPackageUninstalled) {
                // TODO(b/146555198) Allow the app itself to see the info
                // (at least for non-instant apps)
                initiatingPackageName = null;
            } else {
                // All installSource strings are interned, so == is ok here
                if (installSource.initiatingPackageName == installSource.installerPackageName) {
                    // The installer and initiator will often be the same, and when they are
@@ -19987,8 +20000,9 @@ public class PackageManagerService extends IPackageManager.Stub
                    if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
                        initiatingPackageName = null;
                    }
                }
            }
            originatingPackageName = installSource.originatingPackageName;
            if (originatingPackageName != null) {
                final PackageSetting ps = mSettings.mPackages.get(originatingPackageName);
+18 −1
Original line number Diff line number Diff line
@@ -2819,6 +2819,9 @@ public final class Settings {
        if (installSource.initiatingPackageName != null) {
            serializer.attribute(null, "installInitiator", installSource.initiatingPackageName);
        }
        if (installSource.isInitiatingPackageUninstalled) {
            serializer.attribute(null, "installInitiatorUninstalled", "true");
        }
        if (installSource.originatingPackageName != null) {
            serializer.attribute(null, "installOriginator", installSource.originatingPackageName);
        }
@@ -2836,6 +2839,11 @@ public final class Settings {

        pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);

        if (installSource.initiatingPackageSignatures != null) {
            installSource.initiatingPackageSignatures.writeXml(
                    serializer, "install-initiator-sigs", mPastSignatures);
        }

        writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissionStates());

        writeSigningKeySetLPr(serializer, pkg.keySetData);
@@ -3571,6 +3579,7 @@ public final class Settings {
        String isOrphaned = null;
        String installOriginatingPackageName = null;
        String installInitiatingPackageName = null;
        String installInitiatorUninstalled = null;
        String volumeUuid = null;
        String categoryHintString = null;
        String updateAvailable = null;
@@ -3616,6 +3625,8 @@ public final class Settings {
            isOrphaned = parser.getAttributeValue(null, "isOrphaned");
            installInitiatingPackageName = parser.getAttributeValue(null, "installInitiator");
            installOriginatingPackageName = parser.getAttributeValue(null, "installOriginator");
            installInitiatorUninstalled = parser.getAttributeValue(null,
                    "installInitiatorUninstalled");
            volumeUuid = parser.getAttributeValue(null, "volumeUuid");
            categoryHintString = parser.getAttributeValue(null, "categoryHint");
            if (categoryHintString != null) {
@@ -3772,7 +3783,8 @@ public final class Settings {
            packageSetting.uidError = "true".equals(uidError);
            InstallSource installSource = InstallSource.create(
                    installInitiatingPackageName, installOriginatingPackageName,
                    installerPackageName, "true".equals(isOrphaned));
                    installerPackageName, "true".equals(isOrphaned),
                    "true".equals(installInitiatorUninstalled));
            packageSetting.installSource = installSource;
            packageSetting.volumeUuid = volumeUuid;
            packageSetting.categoryHint = categoryHint;
@@ -3849,6 +3861,11 @@ public final class Settings {
                        mKeySetRefs.put(id, 1);
                    }
                    packageSetting.keySetData.addDefinedKeySet(id, alias);
                } else if (tagName.equals("install-initiator-sigs")) {
                    final PackageSignatures signatures = new PackageSignatures();
                    signatures.readXml(parser, mPastSignatures);
                    packageSetting.installSource =
                            packageSetting.installSource.setInitiatingPackageSignatures(signatures);
                } else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {
                    readDomainVerificationLPw(parser, packageSetting);
                } else {
Loading